diff --git a/.venv/Lib/site-packages/__pycache__/decorator.cpython-311.pyc b/.venv/Lib/site-packages/__pycache__/decorator.cpython-311.pyc index fb74fb10..49791b33 100644 Binary files a/.venv/Lib/site-packages/__pycache__/decorator.cpython-311.pyc and b/.venv/Lib/site-packages/__pycache__/decorator.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/anyio/__pycache__/__init__.cpython-311.pyc index fbbfcc2b..af2c7c4d 100644 Binary files a/.venv/Lib/site-packages/anyio/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/__pycache__/from_thread.cpython-311.pyc b/.venv/Lib/site-packages/anyio/__pycache__/from_thread.cpython-311.pyc index df294b9d..23dca6ff 100644 Binary files a/.venv/Lib/site-packages/anyio/__pycache__/from_thread.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/__pycache__/from_thread.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/__pycache__/lowlevel.cpython-311.pyc b/.venv/Lib/site-packages/anyio/__pycache__/lowlevel.cpython-311.pyc index 646940fa..72dc33fb 100644 Binary files a/.venv/Lib/site-packages/anyio/__pycache__/lowlevel.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/__pycache__/lowlevel.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/__pycache__/to_thread.cpython-311.pyc b/.venv/Lib/site-packages/anyio/__pycache__/to_thread.cpython-311.pyc index e69ecf73..f28751de 100644 Binary files a/.venv/Lib/site-packages/anyio/__pycache__/to_thread.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/__pycache__/to_thread.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/_core/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/anyio/_core/__pycache__/__init__.cpython-311.pyc index f2f94f59..2d18a89f 100644 Binary files a/.venv/Lib/site-packages/anyio/_core/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/_core/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/_core/__pycache__/_eventloop.cpython-311.pyc b/.venv/Lib/site-packages/anyio/_core/__pycache__/_eventloop.cpython-311.pyc index 46d00a4b..490996f8 100644 Binary files a/.venv/Lib/site-packages/anyio/_core/__pycache__/_eventloop.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/_core/__pycache__/_eventloop.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/_core/__pycache__/_exceptions.cpython-311.pyc b/.venv/Lib/site-packages/anyio/_core/__pycache__/_exceptions.cpython-311.pyc index 1ff90773..bcfbe687 100644 Binary files a/.venv/Lib/site-packages/anyio/_core/__pycache__/_exceptions.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/_core/__pycache__/_exceptions.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/_core/__pycache__/_fileio.cpython-311.pyc b/.venv/Lib/site-packages/anyio/_core/__pycache__/_fileio.cpython-311.pyc index 1c7af729..76d56a4f 100644 Binary files a/.venv/Lib/site-packages/anyio/_core/__pycache__/_fileio.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/_core/__pycache__/_fileio.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/_core/__pycache__/_resources.cpython-311.pyc b/.venv/Lib/site-packages/anyio/_core/__pycache__/_resources.cpython-311.pyc index 5682dfef..6c916fdc 100644 Binary files a/.venv/Lib/site-packages/anyio/_core/__pycache__/_resources.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/_core/__pycache__/_resources.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/_core/__pycache__/_signals.cpython-311.pyc b/.venv/Lib/site-packages/anyio/_core/__pycache__/_signals.cpython-311.pyc index bf442631..8f912cdb 100644 Binary files a/.venv/Lib/site-packages/anyio/_core/__pycache__/_signals.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/_core/__pycache__/_signals.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/_core/__pycache__/_sockets.cpython-311.pyc b/.venv/Lib/site-packages/anyio/_core/__pycache__/_sockets.cpython-311.pyc index 89c36402..18ed5c1e 100644 Binary files a/.venv/Lib/site-packages/anyio/_core/__pycache__/_sockets.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/_core/__pycache__/_sockets.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/_core/__pycache__/_streams.cpython-311.pyc b/.venv/Lib/site-packages/anyio/_core/__pycache__/_streams.cpython-311.pyc index 4579cc82..c498ac43 100644 Binary files a/.venv/Lib/site-packages/anyio/_core/__pycache__/_streams.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/_core/__pycache__/_streams.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/_core/__pycache__/_subprocesses.cpython-311.pyc b/.venv/Lib/site-packages/anyio/_core/__pycache__/_subprocesses.cpython-311.pyc index 48d4bc56..8da08cb5 100644 Binary files a/.venv/Lib/site-packages/anyio/_core/__pycache__/_subprocesses.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/_core/__pycache__/_subprocesses.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/_core/__pycache__/_synchronization.cpython-311.pyc b/.venv/Lib/site-packages/anyio/_core/__pycache__/_synchronization.cpython-311.pyc index 2b0e0125..63fbd79f 100644 Binary files a/.venv/Lib/site-packages/anyio/_core/__pycache__/_synchronization.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/_core/__pycache__/_synchronization.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/_core/__pycache__/_tasks.cpython-311.pyc b/.venv/Lib/site-packages/anyio/_core/__pycache__/_tasks.cpython-311.pyc index 9fd9595c..170d6ea3 100644 Binary files a/.venv/Lib/site-packages/anyio/_core/__pycache__/_tasks.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/_core/__pycache__/_tasks.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/_core/__pycache__/_testing.cpython-311.pyc b/.venv/Lib/site-packages/anyio/_core/__pycache__/_testing.cpython-311.pyc index 31861e41..4ad8bb66 100644 Binary files a/.venv/Lib/site-packages/anyio/_core/__pycache__/_testing.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/_core/__pycache__/_testing.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/_core/__pycache__/_typedattr.cpython-311.pyc b/.venv/Lib/site-packages/anyio/_core/__pycache__/_typedattr.cpython-311.pyc index 4bb96ac9..cb02931a 100644 Binary files a/.venv/Lib/site-packages/anyio/_core/__pycache__/_typedattr.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/_core/__pycache__/_typedattr.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/abc/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/anyio/abc/__pycache__/__init__.cpython-311.pyc index aaffc9b0..c9b78aa4 100644 Binary files a/.venv/Lib/site-packages/anyio/abc/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/abc/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/abc/__pycache__/_eventloop.cpython-311.pyc b/.venv/Lib/site-packages/anyio/abc/__pycache__/_eventloop.cpython-311.pyc index 7fe09d73..0b91b71b 100644 Binary files a/.venv/Lib/site-packages/anyio/abc/__pycache__/_eventloop.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/abc/__pycache__/_eventloop.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/abc/__pycache__/_resources.cpython-311.pyc b/.venv/Lib/site-packages/anyio/abc/__pycache__/_resources.cpython-311.pyc index 766cd0a7..d0d82f29 100644 Binary files a/.venv/Lib/site-packages/anyio/abc/__pycache__/_resources.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/abc/__pycache__/_resources.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/abc/__pycache__/_sockets.cpython-311.pyc b/.venv/Lib/site-packages/anyio/abc/__pycache__/_sockets.cpython-311.pyc index 09554280..c718c0bd 100644 Binary files a/.venv/Lib/site-packages/anyio/abc/__pycache__/_sockets.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/abc/__pycache__/_sockets.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/abc/__pycache__/_streams.cpython-311.pyc b/.venv/Lib/site-packages/anyio/abc/__pycache__/_streams.cpython-311.pyc index 268256e0..e7b913a9 100644 Binary files a/.venv/Lib/site-packages/anyio/abc/__pycache__/_streams.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/abc/__pycache__/_streams.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/abc/__pycache__/_subprocesses.cpython-311.pyc b/.venv/Lib/site-packages/anyio/abc/__pycache__/_subprocesses.cpython-311.pyc index 242fb506..db0e32d4 100644 Binary files a/.venv/Lib/site-packages/anyio/abc/__pycache__/_subprocesses.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/abc/__pycache__/_subprocesses.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/abc/__pycache__/_tasks.cpython-311.pyc b/.venv/Lib/site-packages/anyio/abc/__pycache__/_tasks.cpython-311.pyc index e1f0801d..3023392c 100644 Binary files a/.venv/Lib/site-packages/anyio/abc/__pycache__/_tasks.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/abc/__pycache__/_tasks.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/abc/__pycache__/_testing.cpython-311.pyc b/.venv/Lib/site-packages/anyio/abc/__pycache__/_testing.cpython-311.pyc index c44b44a4..0f7bf8a9 100644 Binary files a/.venv/Lib/site-packages/anyio/abc/__pycache__/_testing.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/abc/__pycache__/_testing.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/streams/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/anyio/streams/__pycache__/__init__.cpython-311.pyc index 5f8bf477..74bc8689 100644 Binary files a/.venv/Lib/site-packages/anyio/streams/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/streams/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/streams/__pycache__/memory.cpython-311.pyc b/.venv/Lib/site-packages/anyio/streams/__pycache__/memory.cpython-311.pyc index 4189abe9..11599d7f 100644 Binary files a/.venv/Lib/site-packages/anyio/streams/__pycache__/memory.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/streams/__pycache__/memory.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/streams/__pycache__/stapled.cpython-311.pyc b/.venv/Lib/site-packages/anyio/streams/__pycache__/stapled.cpython-311.pyc index ec7ddef0..79371166 100644 Binary files a/.venv/Lib/site-packages/anyio/streams/__pycache__/stapled.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/streams/__pycache__/stapled.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/anyio/streams/__pycache__/tls.cpython-311.pyc b/.venv/Lib/site-packages/anyio/streams/__pycache__/tls.cpython-311.pyc index e003eeb5..7983b81f 100644 Binary files a/.venv/Lib/site-packages/anyio/streams/__pycache__/tls.cpython-311.pyc and b/.venv/Lib/site-packages/anyio/streams/__pycache__/tls.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/distro/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/distro/__pycache__/__init__.cpython-311.pyc index f1d86d1e..f26d654b 100644 Binary files a/.venv/Lib/site-packages/distro/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/distro/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/distro/__pycache__/distro.cpython-311.pyc b/.venv/Lib/site-packages/distro/__pycache__/distro.cpython-311.pyc index 4565179e..7f98088e 100644 Binary files a/.venv/Lib/site-packages/distro/__pycache__/distro.cpython-311.pyc and b/.venv/Lib/site-packages/distro/__pycache__/distro.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/h11/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/h11/__pycache__/__init__.cpython-311.pyc index 9388f5b9..b7d48021 100644 Binary files a/.venv/Lib/site-packages/h11/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/h11/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/h11/__pycache__/_abnf.cpython-311.pyc b/.venv/Lib/site-packages/h11/__pycache__/_abnf.cpython-311.pyc index 01379bf8..5b5a4b92 100644 Binary files a/.venv/Lib/site-packages/h11/__pycache__/_abnf.cpython-311.pyc and b/.venv/Lib/site-packages/h11/__pycache__/_abnf.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/h11/__pycache__/_connection.cpython-311.pyc b/.venv/Lib/site-packages/h11/__pycache__/_connection.cpython-311.pyc index b55c2618..cf4d5f7e 100644 Binary files a/.venv/Lib/site-packages/h11/__pycache__/_connection.cpython-311.pyc and b/.venv/Lib/site-packages/h11/__pycache__/_connection.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/h11/__pycache__/_events.cpython-311.pyc b/.venv/Lib/site-packages/h11/__pycache__/_events.cpython-311.pyc index 9de58f5d..a71eb6d5 100644 Binary files a/.venv/Lib/site-packages/h11/__pycache__/_events.cpython-311.pyc and b/.venv/Lib/site-packages/h11/__pycache__/_events.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/h11/__pycache__/_headers.cpython-311.pyc b/.venv/Lib/site-packages/h11/__pycache__/_headers.cpython-311.pyc index 561385d4..85c399c3 100644 Binary files a/.venv/Lib/site-packages/h11/__pycache__/_headers.cpython-311.pyc and b/.venv/Lib/site-packages/h11/__pycache__/_headers.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/h11/__pycache__/_readers.cpython-311.pyc b/.venv/Lib/site-packages/h11/__pycache__/_readers.cpython-311.pyc index fc6e5931..a9db6bd1 100644 Binary files a/.venv/Lib/site-packages/h11/__pycache__/_readers.cpython-311.pyc and b/.venv/Lib/site-packages/h11/__pycache__/_readers.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/h11/__pycache__/_receivebuffer.cpython-311.pyc b/.venv/Lib/site-packages/h11/__pycache__/_receivebuffer.cpython-311.pyc index d6733e4c..08cb8252 100644 Binary files a/.venv/Lib/site-packages/h11/__pycache__/_receivebuffer.cpython-311.pyc and b/.venv/Lib/site-packages/h11/__pycache__/_receivebuffer.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/h11/__pycache__/_state.cpython-311.pyc b/.venv/Lib/site-packages/h11/__pycache__/_state.cpython-311.pyc index f70c4d50..1972cd8d 100644 Binary files a/.venv/Lib/site-packages/h11/__pycache__/_state.cpython-311.pyc and b/.venv/Lib/site-packages/h11/__pycache__/_state.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/h11/__pycache__/_util.cpython-311.pyc b/.venv/Lib/site-packages/h11/__pycache__/_util.cpython-311.pyc index a995b866..690644a0 100644 Binary files a/.venv/Lib/site-packages/h11/__pycache__/_util.cpython-311.pyc and b/.venv/Lib/site-packages/h11/__pycache__/_util.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/h11/__pycache__/_version.cpython-311.pyc b/.venv/Lib/site-packages/h11/__pycache__/_version.cpython-311.pyc index 0eed353e..125b3856 100644 Binary files a/.venv/Lib/site-packages/h11/__pycache__/_version.cpython-311.pyc and b/.venv/Lib/site-packages/h11/__pycache__/_version.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/h11/__pycache__/_writers.cpython-311.pyc b/.venv/Lib/site-packages/h11/__pycache__/_writers.cpython-311.pyc index e228e44e..296e1fdd 100644 Binary files a/.venv/Lib/site-packages/h11/__pycache__/_writers.cpython-311.pyc and b/.venv/Lib/site-packages/h11/__pycache__/_writers.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/__pycache__/__init__.cpython-311.pyc index b6f729d4..f2e793ef 100644 Binary files a/.venv/Lib/site-packages/httpcore/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/__pycache__/_api.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/__pycache__/_api.cpython-311.pyc index 99096f3c..7c46da31 100644 Binary files a/.venv/Lib/site-packages/httpcore/__pycache__/_api.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/__pycache__/_api.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/__pycache__/_exceptions.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/__pycache__/_exceptions.cpython-311.pyc index 3ad25250..fd41c631 100644 Binary files a/.venv/Lib/site-packages/httpcore/__pycache__/_exceptions.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/__pycache__/_exceptions.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/__pycache__/_models.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/__pycache__/_models.cpython-311.pyc index fff20bee..828b4f66 100644 Binary files a/.venv/Lib/site-packages/httpcore/__pycache__/_models.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/__pycache__/_models.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/__pycache__/_ssl.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/__pycache__/_ssl.cpython-311.pyc index ff4a467a..c7b6a011 100644 Binary files a/.venv/Lib/site-packages/httpcore/__pycache__/_ssl.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/__pycache__/_ssl.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/__pycache__/_synchronization.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/__pycache__/_synchronization.cpython-311.pyc index 88ac3117..bfda9f48 100644 Binary files a/.venv/Lib/site-packages/httpcore/__pycache__/_synchronization.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/__pycache__/_synchronization.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/__pycache__/_trace.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/__pycache__/_trace.cpython-311.pyc index 2208572d..e7045d3e 100644 Binary files a/.venv/Lib/site-packages/httpcore/__pycache__/_trace.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/__pycache__/_trace.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/__pycache__/_utils.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/__pycache__/_utils.cpython-311.pyc index 6c831dc3..ecea5c19 100644 Binary files a/.venv/Lib/site-packages/httpcore/__pycache__/_utils.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/__pycache__/_utils.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_async/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_async/__pycache__/__init__.cpython-311.pyc index 22243f83..9466ffb0 100644 Binary files a/.venv/Lib/site-packages/httpcore/_async/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_async/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_async/__pycache__/connection.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_async/__pycache__/connection.cpython-311.pyc index 6c36a249..f71dc7bb 100644 Binary files a/.venv/Lib/site-packages/httpcore/_async/__pycache__/connection.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_async/__pycache__/connection.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_async/__pycache__/connection_pool.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_async/__pycache__/connection_pool.cpython-311.pyc index 9abc2439..e916836a 100644 Binary files a/.venv/Lib/site-packages/httpcore/_async/__pycache__/connection_pool.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_async/__pycache__/connection_pool.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_async/__pycache__/http11.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_async/__pycache__/http11.cpython-311.pyc index 1abea181..a9b32429 100644 Binary files a/.venv/Lib/site-packages/httpcore/_async/__pycache__/http11.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_async/__pycache__/http11.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_async/__pycache__/http2.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_async/__pycache__/http2.cpython-311.pyc index b90fd274..d9e1505d 100644 Binary files a/.venv/Lib/site-packages/httpcore/_async/__pycache__/http2.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_async/__pycache__/http2.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_async/__pycache__/http_proxy.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_async/__pycache__/http_proxy.cpython-311.pyc index 2f2f1a9d..f2eac504 100644 Binary files a/.venv/Lib/site-packages/httpcore/_async/__pycache__/http_proxy.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_async/__pycache__/http_proxy.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_async/__pycache__/interfaces.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_async/__pycache__/interfaces.cpython-311.pyc index 3766a582..2491c6b1 100644 Binary files a/.venv/Lib/site-packages/httpcore/_async/__pycache__/interfaces.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_async/__pycache__/interfaces.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_async/__pycache__/socks_proxy.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_async/__pycache__/socks_proxy.cpython-311.pyc index f2130057..31ccbb1e 100644 Binary files a/.venv/Lib/site-packages/httpcore/_async/__pycache__/socks_proxy.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_async/__pycache__/socks_proxy.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_backends/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_backends/__pycache__/__init__.cpython-311.pyc index 8adb95bd..9d584723 100644 Binary files a/.venv/Lib/site-packages/httpcore/_backends/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_backends/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_backends/__pycache__/anyio.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_backends/__pycache__/anyio.cpython-311.pyc index 48aeed41..1a307201 100644 Binary files a/.venv/Lib/site-packages/httpcore/_backends/__pycache__/anyio.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_backends/__pycache__/anyio.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_backends/__pycache__/auto.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_backends/__pycache__/auto.cpython-311.pyc index 55c81429..d1e493b5 100644 Binary files a/.venv/Lib/site-packages/httpcore/_backends/__pycache__/auto.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_backends/__pycache__/auto.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_backends/__pycache__/base.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_backends/__pycache__/base.cpython-311.pyc index 0e303a85..2d2ef021 100644 Binary files a/.venv/Lib/site-packages/httpcore/_backends/__pycache__/base.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_backends/__pycache__/base.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_backends/__pycache__/mock.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_backends/__pycache__/mock.cpython-311.pyc index fb8479ed..cf6285bf 100644 Binary files a/.venv/Lib/site-packages/httpcore/_backends/__pycache__/mock.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_backends/__pycache__/mock.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_backends/__pycache__/sync.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_backends/__pycache__/sync.cpython-311.pyc index 19596186..51f020e9 100644 Binary files a/.venv/Lib/site-packages/httpcore/_backends/__pycache__/sync.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_backends/__pycache__/sync.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_backends/__pycache__/trio.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_backends/__pycache__/trio.cpython-311.pyc index 8cb2c369..bec327ff 100644 Binary files a/.venv/Lib/site-packages/httpcore/_backends/__pycache__/trio.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_backends/__pycache__/trio.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_sync/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_sync/__pycache__/__init__.cpython-311.pyc index 03c2e4a6..b14fcb3a 100644 Binary files a/.venv/Lib/site-packages/httpcore/_sync/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_sync/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_sync/__pycache__/connection.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_sync/__pycache__/connection.cpython-311.pyc index 8d8e13a7..ebd1f7f1 100644 Binary files a/.venv/Lib/site-packages/httpcore/_sync/__pycache__/connection.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_sync/__pycache__/connection.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_sync/__pycache__/connection_pool.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_sync/__pycache__/connection_pool.cpython-311.pyc index 2c53bbee..6d8dd955 100644 Binary files a/.venv/Lib/site-packages/httpcore/_sync/__pycache__/connection_pool.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_sync/__pycache__/connection_pool.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http11.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http11.cpython-311.pyc index f5f2ddd1..13d337cd 100644 Binary files a/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http11.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http11.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http2.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http2.cpython-311.pyc index 4bbecad8..6941497a 100644 Binary files a/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http2.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http2.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http_proxy.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http_proxy.cpython-311.pyc index 03938b2c..52276ad9 100644 Binary files a/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http_proxy.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http_proxy.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_sync/__pycache__/interfaces.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_sync/__pycache__/interfaces.cpython-311.pyc index 0f94f00d..e1e5bf40 100644 Binary files a/.venv/Lib/site-packages/httpcore/_sync/__pycache__/interfaces.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_sync/__pycache__/interfaces.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpcore/_sync/__pycache__/socks_proxy.cpython-311.pyc b/.venv/Lib/site-packages/httpcore/_sync/__pycache__/socks_proxy.cpython-311.pyc index 3d275f9b..653cb4f9 100644 Binary files a/.venv/Lib/site-packages/httpcore/_sync/__pycache__/socks_proxy.cpython-311.pyc and b/.venv/Lib/site-packages/httpcore/_sync/__pycache__/socks_proxy.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/httpx/__pycache__/__init__.cpython-311.pyc index 60e9c4d7..9cd651dd 100644 Binary files a/.venv/Lib/site-packages/httpx/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/__pycache__/__version__.cpython-311.pyc b/.venv/Lib/site-packages/httpx/__pycache__/__version__.cpython-311.pyc index 5c978f9c..0e87c732 100644 Binary files a/.venv/Lib/site-packages/httpx/__pycache__/__version__.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/__pycache__/__version__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/__pycache__/_api.cpython-311.pyc b/.venv/Lib/site-packages/httpx/__pycache__/_api.cpython-311.pyc index 02409b4c..b91ecc95 100644 Binary files a/.venv/Lib/site-packages/httpx/__pycache__/_api.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/__pycache__/_api.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/__pycache__/_auth.cpython-311.pyc b/.venv/Lib/site-packages/httpx/__pycache__/_auth.cpython-311.pyc index 8f958586..764685c5 100644 Binary files a/.venv/Lib/site-packages/httpx/__pycache__/_auth.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/__pycache__/_auth.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/__pycache__/_client.cpython-311.pyc b/.venv/Lib/site-packages/httpx/__pycache__/_client.cpython-311.pyc index 226b92a0..864e89a3 100644 Binary files a/.venv/Lib/site-packages/httpx/__pycache__/_client.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/__pycache__/_client.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/__pycache__/_compat.cpython-311.pyc b/.venv/Lib/site-packages/httpx/__pycache__/_compat.cpython-311.pyc index 4ec02c84..e0450acf 100644 Binary files a/.venv/Lib/site-packages/httpx/__pycache__/_compat.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/__pycache__/_compat.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/__pycache__/_config.cpython-311.pyc b/.venv/Lib/site-packages/httpx/__pycache__/_config.cpython-311.pyc index d0235b4e..593eae3b 100644 Binary files a/.venv/Lib/site-packages/httpx/__pycache__/_config.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/__pycache__/_config.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/__pycache__/_content.cpython-311.pyc b/.venv/Lib/site-packages/httpx/__pycache__/_content.cpython-311.pyc index eb97b5d7..d5ba3004 100644 Binary files a/.venv/Lib/site-packages/httpx/__pycache__/_content.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/__pycache__/_content.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/__pycache__/_decoders.cpython-311.pyc b/.venv/Lib/site-packages/httpx/__pycache__/_decoders.cpython-311.pyc index bee1333c..2acd542b 100644 Binary files a/.venv/Lib/site-packages/httpx/__pycache__/_decoders.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/__pycache__/_decoders.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/__pycache__/_exceptions.cpython-311.pyc b/.venv/Lib/site-packages/httpx/__pycache__/_exceptions.cpython-311.pyc index c778b8fd..28d7b753 100644 Binary files a/.venv/Lib/site-packages/httpx/__pycache__/_exceptions.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/__pycache__/_exceptions.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/__pycache__/_main.cpython-311.pyc b/.venv/Lib/site-packages/httpx/__pycache__/_main.cpython-311.pyc index aedf4bd6..f586a30a 100644 Binary files a/.venv/Lib/site-packages/httpx/__pycache__/_main.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/__pycache__/_main.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/__pycache__/_models.cpython-311.pyc b/.venv/Lib/site-packages/httpx/__pycache__/_models.cpython-311.pyc index 85f245f5..7b25a6ab 100644 Binary files a/.venv/Lib/site-packages/httpx/__pycache__/_models.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/__pycache__/_models.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/__pycache__/_multipart.cpython-311.pyc b/.venv/Lib/site-packages/httpx/__pycache__/_multipart.cpython-311.pyc index 8c2e07aa..e186eb7b 100644 Binary files a/.venv/Lib/site-packages/httpx/__pycache__/_multipart.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/__pycache__/_multipart.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/__pycache__/_status_codes.cpython-311.pyc b/.venv/Lib/site-packages/httpx/__pycache__/_status_codes.cpython-311.pyc index 30c5c876..a6d8495f 100644 Binary files a/.venv/Lib/site-packages/httpx/__pycache__/_status_codes.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/__pycache__/_status_codes.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/__pycache__/_types.cpython-311.pyc b/.venv/Lib/site-packages/httpx/__pycache__/_types.cpython-311.pyc index 919ea1c7..1fc7bf0b 100644 Binary files a/.venv/Lib/site-packages/httpx/__pycache__/_types.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/__pycache__/_types.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/__pycache__/_urlparse.cpython-311.pyc b/.venv/Lib/site-packages/httpx/__pycache__/_urlparse.cpython-311.pyc index a6e550cb..a6c66be7 100644 Binary files a/.venv/Lib/site-packages/httpx/__pycache__/_urlparse.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/__pycache__/_urlparse.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/__pycache__/_urls.cpython-311.pyc b/.venv/Lib/site-packages/httpx/__pycache__/_urls.cpython-311.pyc index 795c5ca4..0ac167dd 100644 Binary files a/.venv/Lib/site-packages/httpx/__pycache__/_urls.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/__pycache__/_urls.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/__pycache__/_utils.cpython-311.pyc b/.venv/Lib/site-packages/httpx/__pycache__/_utils.cpython-311.pyc index e87e2d88..395da1c6 100644 Binary files a/.venv/Lib/site-packages/httpx/__pycache__/_utils.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/__pycache__/_utils.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/_transports/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/httpx/_transports/__pycache__/__init__.cpython-311.pyc index b7aa8c8e..b5b06812 100644 Binary files a/.venv/Lib/site-packages/httpx/_transports/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/_transports/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/_transports/__pycache__/asgi.cpython-311.pyc b/.venv/Lib/site-packages/httpx/_transports/__pycache__/asgi.cpython-311.pyc index 6ffbb6f7..542f8c9d 100644 Binary files a/.venv/Lib/site-packages/httpx/_transports/__pycache__/asgi.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/_transports/__pycache__/asgi.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/_transports/__pycache__/base.cpython-311.pyc b/.venv/Lib/site-packages/httpx/_transports/__pycache__/base.cpython-311.pyc index 21fb5d91..4bce2d88 100644 Binary files a/.venv/Lib/site-packages/httpx/_transports/__pycache__/base.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/_transports/__pycache__/base.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/_transports/__pycache__/default.cpython-311.pyc b/.venv/Lib/site-packages/httpx/_transports/__pycache__/default.cpython-311.pyc index a4ac2133..9898cec9 100644 Binary files a/.venv/Lib/site-packages/httpx/_transports/__pycache__/default.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/_transports/__pycache__/default.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/_transports/__pycache__/mock.cpython-311.pyc b/.venv/Lib/site-packages/httpx/_transports/__pycache__/mock.cpython-311.pyc index 2b28e0cf..2fea9de2 100644 Binary files a/.venv/Lib/site-packages/httpx/_transports/__pycache__/mock.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/_transports/__pycache__/mock.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/httpx/_transports/__pycache__/wsgi.cpython-311.pyc b/.venv/Lib/site-packages/httpx/_transports/__pycache__/wsgi.cpython-311.pyc index bdaa302a..337c820b 100644 Binary files a/.venv/Lib/site-packages/httpx/_transports/__pycache__/wsgi.cpython-311.pyc and b/.venv/Lib/site-packages/httpx/_transports/__pycache__/wsgi.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/imageio/__pycache__/__init__.cpython-311.pyc index cbc4bad3..ca04726a 100644 Binary files a/.venv/Lib/site-packages/imageio/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/imageio/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio/__pycache__/typing.cpython-311.pyc b/.venv/Lib/site-packages/imageio/__pycache__/typing.cpython-311.pyc index 963d4314..673b8df3 100644 Binary files a/.venv/Lib/site-packages/imageio/__pycache__/typing.cpython-311.pyc and b/.venv/Lib/site-packages/imageio/__pycache__/typing.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio/__pycache__/v2.cpython-311.pyc b/.venv/Lib/site-packages/imageio/__pycache__/v2.cpython-311.pyc index ebb004d3..2577a450 100644 Binary files a/.venv/Lib/site-packages/imageio/__pycache__/v2.cpython-311.pyc and b/.venv/Lib/site-packages/imageio/__pycache__/v2.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio/__pycache__/v3.cpython-311.pyc b/.venv/Lib/site-packages/imageio/__pycache__/v3.cpython-311.pyc index a29c1a5e..e5f43643 100644 Binary files a/.venv/Lib/site-packages/imageio/__pycache__/v3.cpython-311.pyc and b/.venv/Lib/site-packages/imageio/__pycache__/v3.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio/config/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/imageio/config/__pycache__/__init__.cpython-311.pyc index d9bf5803..50cc79f8 100644 Binary files a/.venv/Lib/site-packages/imageio/config/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/imageio/config/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio/config/__pycache__/extensions.cpython-311.pyc b/.venv/Lib/site-packages/imageio/config/__pycache__/extensions.cpython-311.pyc index 6ae7d583..d2e59bf4 100644 Binary files a/.venv/Lib/site-packages/imageio/config/__pycache__/extensions.cpython-311.pyc and b/.venv/Lib/site-packages/imageio/config/__pycache__/extensions.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio/config/__pycache__/plugins.cpython-311.pyc b/.venv/Lib/site-packages/imageio/config/__pycache__/plugins.cpython-311.pyc index 0bf4ceec..e61e65cd 100644 Binary files a/.venv/Lib/site-packages/imageio/config/__pycache__/plugins.cpython-311.pyc and b/.venv/Lib/site-packages/imageio/config/__pycache__/plugins.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio/core/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/imageio/core/__pycache__/__init__.cpython-311.pyc index 046fe918..a1121630 100644 Binary files a/.venv/Lib/site-packages/imageio/core/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/imageio/core/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio/core/__pycache__/fetching.cpython-311.pyc b/.venv/Lib/site-packages/imageio/core/__pycache__/fetching.cpython-311.pyc index 8743c27b..723de5b5 100644 Binary files a/.venv/Lib/site-packages/imageio/core/__pycache__/fetching.cpython-311.pyc and b/.venv/Lib/site-packages/imageio/core/__pycache__/fetching.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio/core/__pycache__/findlib.cpython-311.pyc b/.venv/Lib/site-packages/imageio/core/__pycache__/findlib.cpython-311.pyc index 7bf58d6d..28907dde 100644 Binary files a/.venv/Lib/site-packages/imageio/core/__pycache__/findlib.cpython-311.pyc and b/.venv/Lib/site-packages/imageio/core/__pycache__/findlib.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio/core/__pycache__/format.cpython-311.pyc b/.venv/Lib/site-packages/imageio/core/__pycache__/format.cpython-311.pyc index bbf9fdcd..04fe22d4 100644 Binary files a/.venv/Lib/site-packages/imageio/core/__pycache__/format.cpython-311.pyc and b/.venv/Lib/site-packages/imageio/core/__pycache__/format.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio/core/__pycache__/imopen.cpython-311.pyc b/.venv/Lib/site-packages/imageio/core/__pycache__/imopen.cpython-311.pyc index 90dc0116..64bd7de1 100644 Binary files a/.venv/Lib/site-packages/imageio/core/__pycache__/imopen.cpython-311.pyc and b/.venv/Lib/site-packages/imageio/core/__pycache__/imopen.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio/core/__pycache__/legacy_plugin_wrapper.cpython-311.pyc b/.venv/Lib/site-packages/imageio/core/__pycache__/legacy_plugin_wrapper.cpython-311.pyc index 3bce24b0..90111cd4 100644 Binary files a/.venv/Lib/site-packages/imageio/core/__pycache__/legacy_plugin_wrapper.cpython-311.pyc and b/.venv/Lib/site-packages/imageio/core/__pycache__/legacy_plugin_wrapper.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio/core/__pycache__/request.cpython-311.pyc b/.venv/Lib/site-packages/imageio/core/__pycache__/request.cpython-311.pyc index cdc43a14..00f47099 100644 Binary files a/.venv/Lib/site-packages/imageio/core/__pycache__/request.cpython-311.pyc and b/.venv/Lib/site-packages/imageio/core/__pycache__/request.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio/core/__pycache__/util.cpython-311.pyc b/.venv/Lib/site-packages/imageio/core/__pycache__/util.cpython-311.pyc index 2364c512..cc50c47b 100644 Binary files a/.venv/Lib/site-packages/imageio/core/__pycache__/util.cpython-311.pyc and b/.venv/Lib/site-packages/imageio/core/__pycache__/util.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio/core/__pycache__/v3_plugin_api.cpython-311.pyc b/.venv/Lib/site-packages/imageio/core/__pycache__/v3_plugin_api.cpython-311.pyc index 7865ff86..0a90d1ee 100644 Binary files a/.venv/Lib/site-packages/imageio/core/__pycache__/v3_plugin_api.cpython-311.pyc and b/.venv/Lib/site-packages/imageio/core/__pycache__/v3_plugin_api.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio/plugins/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/imageio/plugins/__pycache__/__init__.cpython-311.pyc index 8bc2e26f..c42ecb72 100644 Binary files a/.venv/Lib/site-packages/imageio/plugins/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/imageio/plugins/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio/plugins/__pycache__/ffmpeg.cpython-311.pyc b/.venv/Lib/site-packages/imageio/plugins/__pycache__/ffmpeg.cpython-311.pyc index 6b83e9a2..50594d5c 100644 Binary files a/.venv/Lib/site-packages/imageio/plugins/__pycache__/ffmpeg.cpython-311.pyc and b/.venv/Lib/site-packages/imageio/plugins/__pycache__/ffmpeg.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio_ffmpeg/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/imageio_ffmpeg/__pycache__/__init__.cpython-311.pyc index 929439de..2576568c 100644 Binary files a/.venv/Lib/site-packages/imageio_ffmpeg/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/imageio_ffmpeg/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio_ffmpeg/__pycache__/_definitions.cpython-311.pyc b/.venv/Lib/site-packages/imageio_ffmpeg/__pycache__/_definitions.cpython-311.pyc index 2744e748..aa54ec7b 100644 Binary files a/.venv/Lib/site-packages/imageio_ffmpeg/__pycache__/_definitions.cpython-311.pyc and b/.venv/Lib/site-packages/imageio_ffmpeg/__pycache__/_definitions.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio_ffmpeg/__pycache__/_io.cpython-311.pyc b/.venv/Lib/site-packages/imageio_ffmpeg/__pycache__/_io.cpython-311.pyc index ddf8cfc1..77945c3e 100644 Binary files a/.venv/Lib/site-packages/imageio_ffmpeg/__pycache__/_io.cpython-311.pyc and b/.venv/Lib/site-packages/imageio_ffmpeg/__pycache__/_io.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio_ffmpeg/__pycache__/_parsing.cpython-311.pyc b/.venv/Lib/site-packages/imageio_ffmpeg/__pycache__/_parsing.cpython-311.pyc index f0d4a648..712f9bb1 100644 Binary files a/.venv/Lib/site-packages/imageio_ffmpeg/__pycache__/_parsing.cpython-311.pyc and b/.venv/Lib/site-packages/imageio_ffmpeg/__pycache__/_parsing.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/imageio_ffmpeg/__pycache__/_utils.cpython-311.pyc b/.venv/Lib/site-packages/imageio_ffmpeg/__pycache__/_utils.cpython-311.pyc index 60e83f40..f90b4cb8 100644 Binary files a/.venv/Lib/site-packages/imageio_ffmpeg/__pycache__/_utils.cpython-311.pyc and b/.venv/Lib/site-packages/imageio_ffmpeg/__pycache__/_utils.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/__pycache__/Clip.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/__pycache__/Clip.cpython-311.pyc index 71d89623..3e9a50ca 100644 Binary files a/.venv/Lib/site-packages/moviepy/__pycache__/Clip.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/__pycache__/Clip.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/__pycache__/__init__.cpython-311.pyc index 7a7a8c47..8ca20927 100644 Binary files a/.venv/Lib/site-packages/moviepy/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/__pycache__/compat.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/__pycache__/compat.cpython-311.pyc index 8f656fa3..d1eb8853 100644 Binary files a/.venv/Lib/site-packages/moviepy/__pycache__/compat.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/__pycache__/compat.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/__pycache__/config.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/__pycache__/config.cpython-311.pyc index 020f9237..c8629cc8 100644 Binary files a/.venv/Lib/site-packages/moviepy/__pycache__/config.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/__pycache__/config.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/__pycache__/config_defaults.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/__pycache__/config_defaults.cpython-311.pyc index bd246a9f..9fc5e334 100644 Binary files a/.venv/Lib/site-packages/moviepy/__pycache__/config_defaults.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/__pycache__/config_defaults.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/__pycache__/decorators.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/__pycache__/decorators.cpython-311.pyc index e979021a..bf50f7a0 100644 Binary files a/.venv/Lib/site-packages/moviepy/__pycache__/decorators.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/__pycache__/decorators.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/__pycache__/editor.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/__pycache__/editor.cpython-311.pyc index d13282d8..aadc33e0 100644 Binary files a/.venv/Lib/site-packages/moviepy/__pycache__/editor.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/__pycache__/editor.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/__pycache__/tools.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/__pycache__/tools.cpython-311.pyc index 42c80fa7..e98fbdb8 100644 Binary files a/.venv/Lib/site-packages/moviepy/__pycache__/tools.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/__pycache__/tools.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/__pycache__/version.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/__pycache__/version.cpython-311.pyc index 62d9ac26..3abb5105 100644 Binary files a/.venv/Lib/site-packages/moviepy/__pycache__/version.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/__pycache__/version.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/audio/__pycache__/AudioClip.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/audio/__pycache__/AudioClip.cpython-311.pyc index 391a86c8..80ebf123 100644 Binary files a/.venv/Lib/site-packages/moviepy/audio/__pycache__/AudioClip.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/audio/__pycache__/AudioClip.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/audio/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/audio/__pycache__/__init__.cpython-311.pyc index cb44cf24..0a14777e 100644 Binary files a/.venv/Lib/site-packages/moviepy/audio/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/audio/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/__init__.cpython-311.pyc index 8b15d89f..5e2a6f0a 100644 Binary files a/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/audio_fadein.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/audio_fadein.cpython-311.pyc index dfbc648f..5699ea5f 100644 Binary files a/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/audio_fadein.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/audio_fadein.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/audio_fadeout.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/audio_fadeout.cpython-311.pyc index 64c919d9..3fd2884a 100644 Binary files a/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/audio_fadeout.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/audio_fadeout.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/audio_left_right.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/audio_left_right.cpython-311.pyc index 28a0f3fc..5a34892c 100644 Binary files a/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/audio_left_right.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/audio_left_right.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/audio_loop.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/audio_loop.cpython-311.pyc index bd93d393..3d4e7fbf 100644 Binary files a/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/audio_loop.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/audio_loop.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/audio_normalize.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/audio_normalize.cpython-311.pyc index 7255d79f..054d367e 100644 Binary files a/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/audio_normalize.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/audio_normalize.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/volumex.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/volumex.cpython-311.pyc index 6fa48449..5577f529 100644 Binary files a/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/volumex.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/audio/fx/__pycache__/volumex.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/audio/fx/all/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/audio/fx/all/__pycache__/__init__.cpython-311.pyc index 956fdcbf..cb4c4798 100644 Binary files a/.venv/Lib/site-packages/moviepy/audio/fx/all/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/audio/fx/all/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/audio/io/__pycache__/AudioFileClip.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/audio/io/__pycache__/AudioFileClip.cpython-311.pyc index e8fc05cf..2841af0c 100644 Binary files a/.venv/Lib/site-packages/moviepy/audio/io/__pycache__/AudioFileClip.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/audio/io/__pycache__/AudioFileClip.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/audio/io/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/audio/io/__pycache__/__init__.cpython-311.pyc index a515541c..d0d1a59f 100644 Binary files a/.venv/Lib/site-packages/moviepy/audio/io/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/audio/io/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/audio/io/__pycache__/ffmpeg_audiowriter.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/audio/io/__pycache__/ffmpeg_audiowriter.cpython-311.pyc index 0ec1fa7d..ae2583c4 100644 Binary files a/.venv/Lib/site-packages/moviepy/audio/io/__pycache__/ffmpeg_audiowriter.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/audio/io/__pycache__/ffmpeg_audiowriter.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/audio/io/__pycache__/preview.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/audio/io/__pycache__/preview.cpython-311.pyc index 4bd86b6e..38c07161 100644 Binary files a/.venv/Lib/site-packages/moviepy/audio/io/__pycache__/preview.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/audio/io/__pycache__/preview.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/audio/io/__pycache__/readers.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/audio/io/__pycache__/readers.cpython-311.pyc index 116c5dc3..98678147 100644 Binary files a/.venv/Lib/site-packages/moviepy/audio/io/__pycache__/readers.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/audio/io/__pycache__/readers.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/__pycache__/VideoClip.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/__pycache__/VideoClip.cpython-311.pyc index edd0dade..e73f617d 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/__pycache__/VideoClip.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/__pycache__/VideoClip.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/__pycache__/__init__.cpython-311.pyc index b09eb3eb..d45f4850 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/compositing/__pycache__/CompositeVideoClip.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/compositing/__pycache__/CompositeVideoClip.cpython-311.pyc index 04eb2948..2a17ae9d 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/compositing/__pycache__/CompositeVideoClip.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/compositing/__pycache__/CompositeVideoClip.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/compositing/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/compositing/__pycache__/__init__.cpython-311.pyc index 1bc26574..d9b4a618 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/compositing/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/compositing/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/compositing/__pycache__/concatenate.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/compositing/__pycache__/concatenate.cpython-311.pyc index eeacbba4..c4ce36f8 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/compositing/__pycache__/concatenate.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/compositing/__pycache__/concatenate.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/compositing/__pycache__/on_color.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/compositing/__pycache__/on_color.cpython-311.pyc index aeeec619..24669273 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/compositing/__pycache__/on_color.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/compositing/__pycache__/on_color.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/compositing/__pycache__/transitions.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/compositing/__pycache__/transitions.cpython-311.pyc index e624ec89..b85d7600 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/compositing/__pycache__/transitions.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/compositing/__pycache__/transitions.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/__init__.cpython-311.pyc index 5d757b92..2dffab4e 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/accel_decel.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/accel_decel.cpython-311.pyc index 3a159d52..45303701 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/accel_decel.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/accel_decel.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/blackwhite.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/blackwhite.cpython-311.pyc index a93fc43c..b38d4a99 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/blackwhite.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/blackwhite.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/blink.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/blink.cpython-311.pyc index 5a832b1c..5772d85c 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/blink.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/blink.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/colorx.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/colorx.cpython-311.pyc index ca67819e..3ac96660 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/colorx.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/colorx.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/crop.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/crop.cpython-311.pyc index f3a89cf0..0542f2d0 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/crop.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/crop.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/even_size.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/even_size.cpython-311.pyc index 9f9432d4..5ff7de5d 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/even_size.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/even_size.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/fadein.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/fadein.cpython-311.pyc index fa24871c..720658f1 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/fadein.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/fadein.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/fadeout.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/fadeout.cpython-311.pyc index e83dff7f..7acead3d 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/fadeout.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/fadeout.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/freeze.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/freeze.cpython-311.pyc index ef963f9d..d37dddce 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/freeze.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/freeze.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/freeze_region.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/freeze_region.cpython-311.pyc index 30f28a9e..03a47b4e 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/freeze_region.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/freeze_region.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/gamma_corr.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/gamma_corr.cpython-311.pyc index ef3781a3..958902c6 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/gamma_corr.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/gamma_corr.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/headblur.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/headblur.cpython-311.pyc index 8a8b261e..afd97919 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/headblur.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/headblur.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/invert_colors.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/invert_colors.cpython-311.pyc index c93f7059..22a4d5d7 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/invert_colors.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/invert_colors.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/loop.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/loop.cpython-311.pyc index dd5beb93..a3403bce 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/loop.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/loop.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/lum_contrast.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/lum_contrast.cpython-311.pyc index 77f76346..52a6f503 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/lum_contrast.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/lum_contrast.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/make_loopable.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/make_loopable.cpython-311.pyc index 423a02a0..1f1b4a31 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/make_loopable.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/make_loopable.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/margin.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/margin.cpython-311.pyc index 83fec236..ade19bad 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/margin.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/margin.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/mask_and.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/mask_and.cpython-311.pyc index 83f35e9c..57baebef 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/mask_and.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/mask_and.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/mask_color.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/mask_color.cpython-311.pyc index 894014dd..be651bef 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/mask_color.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/mask_color.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/mask_or.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/mask_or.cpython-311.pyc index b3805a0c..2af6b359 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/mask_or.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/mask_or.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/mirror_x.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/mirror_x.cpython-311.pyc index 0b0c96dd..756071b8 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/mirror_x.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/mirror_x.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/mirror_y.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/mirror_y.cpython-311.pyc index c359901a..6e6efda2 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/mirror_y.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/mirror_y.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/painting.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/painting.cpython-311.pyc index fc8ba203..3a6fbbe9 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/painting.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/painting.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/resize.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/resize.cpython-311.pyc index 89aa6c13..2df230e7 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/resize.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/resize.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/rotate.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/rotate.cpython-311.pyc index 4a3d9c99..5cefcf5c 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/rotate.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/rotate.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/scroll.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/scroll.cpython-311.pyc index 7db99db9..46671040 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/scroll.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/scroll.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/speedx.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/speedx.cpython-311.pyc index 776db3a9..b8e320c6 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/speedx.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/speedx.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/supersample.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/supersample.cpython-311.pyc index 3e9a06d9..41c75d1f 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/supersample.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/supersample.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/time_mirror.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/time_mirror.cpython-311.pyc index 084a99c0..7ffc8ee6 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/time_mirror.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/time_mirror.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/time_symmetrize.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/time_symmetrize.cpython-311.pyc index 75d4bc15..00c1c380 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/time_symmetrize.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/__pycache__/time_symmetrize.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/fx/all/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/fx/all/__pycache__/__init__.cpython-311.pyc index 8f19a86f..de7f6e39 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/fx/all/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/fx/all/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/ImageSequenceClip.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/ImageSequenceClip.cpython-311.pyc index 32b55b06..4fd8e0ef 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/ImageSequenceClip.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/ImageSequenceClip.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/VideoFileClip.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/VideoFileClip.cpython-311.pyc index 0c27bfae..848523d9 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/VideoFileClip.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/VideoFileClip.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/__init__.cpython-311.pyc index 530ba2f9..43790ef4 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/downloader.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/downloader.cpython-311.pyc index 4ef1436f..e6e2d840 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/downloader.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/downloader.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/ffmpeg_reader.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/ffmpeg_reader.cpython-311.pyc index 4a6e3846..bae41966 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/ffmpeg_reader.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/ffmpeg_reader.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/ffmpeg_tools.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/ffmpeg_tools.cpython-311.pyc index 6a1b13c7..3b6f2c42 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/ffmpeg_tools.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/ffmpeg_tools.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/ffmpeg_writer.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/ffmpeg_writer.cpython-311.pyc index 2f19f50f..db7aae99 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/ffmpeg_writer.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/ffmpeg_writer.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/gif_writers.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/gif_writers.cpython-311.pyc index f0f31d81..acdecf25 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/gif_writers.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/gif_writers.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/html_tools.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/html_tools.cpython-311.pyc index fce904a5..d1ca5081 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/html_tools.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/html_tools.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/preview.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/preview.cpython-311.pyc index af5b07c0..60c03301 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/preview.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/preview.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/sliders.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/sliders.cpython-311.pyc index c9336c69..281ab887 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/io/__pycache__/sliders.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/io/__pycache__/sliders.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/tools/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/tools/__pycache__/__init__.cpython-311.pyc index 90443c10..5c3cce11 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/tools/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/tools/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/moviepy/video/tools/__pycache__/drawing.cpython-311.pyc b/.venv/Lib/site-packages/moviepy/video/tools/__pycache__/drawing.cpython-311.pyc index a7bd9450..b7e376ab 100644 Binary files a/.venv/Lib/site-packages/moviepy/video/tools/__pycache__/drawing.cpython-311.pyc and b/.venv/Lib/site-packages/moviepy/video/tools/__pycache__/drawing.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/__pycache__/__init__.cpython-311.pyc index a836ffff..c4f46499 100644 Binary files a/.venv/Lib/site-packages/openai/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/__pycache__/_base_client.cpython-311.pyc b/.venv/Lib/site-packages/openai/__pycache__/_base_client.cpython-311.pyc index 8d93c2da..8bd37a89 100644 Binary files a/.venv/Lib/site-packages/openai/__pycache__/_base_client.cpython-311.pyc and b/.venv/Lib/site-packages/openai/__pycache__/_base_client.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/__pycache__/_client.cpython-311.pyc b/.venv/Lib/site-packages/openai/__pycache__/_client.cpython-311.pyc index d3a357af..6d8004ce 100644 Binary files a/.venv/Lib/site-packages/openai/__pycache__/_client.cpython-311.pyc and b/.venv/Lib/site-packages/openai/__pycache__/_client.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/__pycache__/_compat.cpython-311.pyc b/.venv/Lib/site-packages/openai/__pycache__/_compat.cpython-311.pyc index a7f9bbf3..e1d6a6a7 100644 Binary files a/.venv/Lib/site-packages/openai/__pycache__/_compat.cpython-311.pyc and b/.venv/Lib/site-packages/openai/__pycache__/_compat.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/__pycache__/_constants.cpython-311.pyc b/.venv/Lib/site-packages/openai/__pycache__/_constants.cpython-311.pyc index 939a7dbf..0d0a0e6f 100644 Binary files a/.venv/Lib/site-packages/openai/__pycache__/_constants.cpython-311.pyc and b/.venv/Lib/site-packages/openai/__pycache__/_constants.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/__pycache__/_exceptions.cpython-311.pyc b/.venv/Lib/site-packages/openai/__pycache__/_exceptions.cpython-311.pyc index 4575746d..5b01edf2 100644 Binary files a/.venv/Lib/site-packages/openai/__pycache__/_exceptions.cpython-311.pyc and b/.venv/Lib/site-packages/openai/__pycache__/_exceptions.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/__pycache__/_files.cpython-311.pyc b/.venv/Lib/site-packages/openai/__pycache__/_files.cpython-311.pyc index f02cb51a..39094fd1 100644 Binary files a/.venv/Lib/site-packages/openai/__pycache__/_files.cpython-311.pyc and b/.venv/Lib/site-packages/openai/__pycache__/_files.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/__pycache__/_legacy_response.cpython-311.pyc b/.venv/Lib/site-packages/openai/__pycache__/_legacy_response.cpython-311.pyc index e8e32abf..cfade3fb 100644 Binary files a/.venv/Lib/site-packages/openai/__pycache__/_legacy_response.cpython-311.pyc and b/.venv/Lib/site-packages/openai/__pycache__/_legacy_response.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/__pycache__/_models.cpython-311.pyc b/.venv/Lib/site-packages/openai/__pycache__/_models.cpython-311.pyc index 41a10f6b..6d9c55c8 100644 Binary files a/.venv/Lib/site-packages/openai/__pycache__/_models.cpython-311.pyc and b/.venv/Lib/site-packages/openai/__pycache__/_models.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/__pycache__/_module_client.cpython-311.pyc b/.venv/Lib/site-packages/openai/__pycache__/_module_client.cpython-311.pyc index 4136443d..9d916cc9 100644 Binary files a/.venv/Lib/site-packages/openai/__pycache__/_module_client.cpython-311.pyc and b/.venv/Lib/site-packages/openai/__pycache__/_module_client.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/__pycache__/_qs.cpython-311.pyc b/.venv/Lib/site-packages/openai/__pycache__/_qs.cpython-311.pyc index a5a99e8f..64157ecd 100644 Binary files a/.venv/Lib/site-packages/openai/__pycache__/_qs.cpython-311.pyc and b/.venv/Lib/site-packages/openai/__pycache__/_qs.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/__pycache__/_resource.cpython-311.pyc b/.venv/Lib/site-packages/openai/__pycache__/_resource.cpython-311.pyc index 2aa056d0..605a0ea0 100644 Binary files a/.venv/Lib/site-packages/openai/__pycache__/_resource.cpython-311.pyc and b/.venv/Lib/site-packages/openai/__pycache__/_resource.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/__pycache__/_response.cpython-311.pyc b/.venv/Lib/site-packages/openai/__pycache__/_response.cpython-311.pyc index 18479d8e..a25a39d3 100644 Binary files a/.venv/Lib/site-packages/openai/__pycache__/_response.cpython-311.pyc and b/.venv/Lib/site-packages/openai/__pycache__/_response.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/__pycache__/_streaming.cpython-311.pyc b/.venv/Lib/site-packages/openai/__pycache__/_streaming.cpython-311.pyc index 428eed35..0ffa1c36 100644 Binary files a/.venv/Lib/site-packages/openai/__pycache__/_streaming.cpython-311.pyc and b/.venv/Lib/site-packages/openai/__pycache__/_streaming.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/__pycache__/_types.cpython-311.pyc b/.venv/Lib/site-packages/openai/__pycache__/_types.cpython-311.pyc index 05627a5b..d80f8ca8 100644 Binary files a/.venv/Lib/site-packages/openai/__pycache__/_types.cpython-311.pyc and b/.venv/Lib/site-packages/openai/__pycache__/_types.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/__pycache__/_version.cpython-311.pyc b/.venv/Lib/site-packages/openai/__pycache__/_version.cpython-311.pyc index 106808b0..94008167 100644 Binary files a/.venv/Lib/site-packages/openai/__pycache__/_version.cpython-311.pyc and b/.venv/Lib/site-packages/openai/__pycache__/_version.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/__pycache__/pagination.cpython-311.pyc b/.venv/Lib/site-packages/openai/__pycache__/pagination.cpython-311.pyc index c6400ff5..2d6ecf0b 100644 Binary files a/.venv/Lib/site-packages/openai/__pycache__/pagination.cpython-311.pyc and b/.venv/Lib/site-packages/openai/__pycache__/pagination.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/__pycache__/version.cpython-311.pyc b/.venv/Lib/site-packages/openai/__pycache__/version.cpython-311.pyc index 39ac5883..9361a114 100644 Binary files a/.venv/Lib/site-packages/openai/__pycache__/version.cpython-311.pyc and b/.venv/Lib/site-packages/openai/__pycache__/version.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/_extras/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/_extras/__pycache__/__init__.cpython-311.pyc index 63122aa3..a9ffa2ad 100644 Binary files a/.venv/Lib/site-packages/openai/_extras/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/_extras/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/_extras/__pycache__/_common.cpython-311.pyc b/.venv/Lib/site-packages/openai/_extras/__pycache__/_common.cpython-311.pyc index f00e2d04..8becc447 100644 Binary files a/.venv/Lib/site-packages/openai/_extras/__pycache__/_common.cpython-311.pyc and b/.venv/Lib/site-packages/openai/_extras/__pycache__/_common.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/_extras/__pycache__/numpy_proxy.cpython-311.pyc b/.venv/Lib/site-packages/openai/_extras/__pycache__/numpy_proxy.cpython-311.pyc index 98778cef..56f79674 100644 Binary files a/.venv/Lib/site-packages/openai/_extras/__pycache__/numpy_proxy.cpython-311.pyc and b/.venv/Lib/site-packages/openai/_extras/__pycache__/numpy_proxy.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/_extras/__pycache__/pandas_proxy.cpython-311.pyc b/.venv/Lib/site-packages/openai/_extras/__pycache__/pandas_proxy.cpython-311.pyc index 256716d8..78764ed2 100644 Binary files a/.venv/Lib/site-packages/openai/_extras/__pycache__/pandas_proxy.cpython-311.pyc and b/.venv/Lib/site-packages/openai/_extras/__pycache__/pandas_proxy.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/_utils/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/_utils/__pycache__/__init__.cpython-311.pyc index 42d9ddb9..a7b47687 100644 Binary files a/.venv/Lib/site-packages/openai/_utils/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/_utils/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/_utils/__pycache__/_logs.cpython-311.pyc b/.venv/Lib/site-packages/openai/_utils/__pycache__/_logs.cpython-311.pyc index 395512e9..7f7dec8d 100644 Binary files a/.venv/Lib/site-packages/openai/_utils/__pycache__/_logs.cpython-311.pyc and b/.venv/Lib/site-packages/openai/_utils/__pycache__/_logs.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/_utils/__pycache__/_proxy.cpython-311.pyc b/.venv/Lib/site-packages/openai/_utils/__pycache__/_proxy.cpython-311.pyc index 39e250f1..e0ce749e 100644 Binary files a/.venv/Lib/site-packages/openai/_utils/__pycache__/_proxy.cpython-311.pyc and b/.venv/Lib/site-packages/openai/_utils/__pycache__/_proxy.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/_utils/__pycache__/_streams.cpython-311.pyc b/.venv/Lib/site-packages/openai/_utils/__pycache__/_streams.cpython-311.pyc index a980145d..c3c458cb 100644 Binary files a/.venv/Lib/site-packages/openai/_utils/__pycache__/_streams.cpython-311.pyc and b/.venv/Lib/site-packages/openai/_utils/__pycache__/_streams.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/_utils/__pycache__/_sync.cpython-311.pyc b/.venv/Lib/site-packages/openai/_utils/__pycache__/_sync.cpython-311.pyc index ac56aed5..8436c6be 100644 Binary files a/.venv/Lib/site-packages/openai/_utils/__pycache__/_sync.cpython-311.pyc and b/.venv/Lib/site-packages/openai/_utils/__pycache__/_sync.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/_utils/__pycache__/_transform.cpython-311.pyc b/.venv/Lib/site-packages/openai/_utils/__pycache__/_transform.cpython-311.pyc index 2586bf28..f08f759e 100644 Binary files a/.venv/Lib/site-packages/openai/_utils/__pycache__/_transform.cpython-311.pyc and b/.venv/Lib/site-packages/openai/_utils/__pycache__/_transform.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/_utils/__pycache__/_typing.cpython-311.pyc b/.venv/Lib/site-packages/openai/_utils/__pycache__/_typing.cpython-311.pyc index 664a6b28..5aaf29ad 100644 Binary files a/.venv/Lib/site-packages/openai/_utils/__pycache__/_typing.cpython-311.pyc and b/.venv/Lib/site-packages/openai/_utils/__pycache__/_typing.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/_utils/__pycache__/_utils.cpython-311.pyc b/.venv/Lib/site-packages/openai/_utils/__pycache__/_utils.cpython-311.pyc index 34254ae9..1ba13f4e 100644 Binary files a/.venv/Lib/site-packages/openai/_utils/__pycache__/_utils.cpython-311.pyc and b/.venv/Lib/site-packages/openai/_utils/__pycache__/_utils.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/lib/__pycache__/_old_api.cpython-311.pyc b/.venv/Lib/site-packages/openai/lib/__pycache__/_old_api.cpython-311.pyc index f216495b..152f3096 100644 Binary files a/.venv/Lib/site-packages/openai/lib/__pycache__/_old_api.cpython-311.pyc and b/.venv/Lib/site-packages/openai/lib/__pycache__/_old_api.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/lib/__pycache__/azure.cpython-311.pyc b/.venv/Lib/site-packages/openai/lib/__pycache__/azure.cpython-311.pyc index b9e5a97b..9cf1af21 100644 Binary files a/.venv/Lib/site-packages/openai/lib/__pycache__/azure.cpython-311.pyc and b/.venv/Lib/site-packages/openai/lib/__pycache__/azure.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/lib/streaming/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/lib/streaming/__pycache__/__init__.cpython-311.pyc index c0666de7..1db49fef 100644 Binary files a/.venv/Lib/site-packages/openai/lib/streaming/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/lib/streaming/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/lib/streaming/__pycache__/_assistants.cpython-311.pyc b/.venv/Lib/site-packages/openai/lib/streaming/__pycache__/_assistants.cpython-311.pyc index 93b9d187..a017817f 100644 Binary files a/.venv/Lib/site-packages/openai/lib/streaming/__pycache__/_assistants.cpython-311.pyc and b/.venv/Lib/site-packages/openai/lib/streaming/__pycache__/_assistants.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/__pycache__/__init__.cpython-311.pyc index a047378f..d2e562b8 100644 Binary files a/.venv/Lib/site-packages/openai/resources/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/__pycache__/batches.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/__pycache__/batches.cpython-311.pyc index f9675efd..b389de41 100644 Binary files a/.venv/Lib/site-packages/openai/resources/__pycache__/batches.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/__pycache__/batches.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/__pycache__/completions.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/__pycache__/completions.cpython-311.pyc index 482341be..6f3021dc 100644 Binary files a/.venv/Lib/site-packages/openai/resources/__pycache__/completions.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/__pycache__/completions.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/__pycache__/embeddings.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/__pycache__/embeddings.cpython-311.pyc index ef7ab8ba..b4976c99 100644 Binary files a/.venv/Lib/site-packages/openai/resources/__pycache__/embeddings.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/__pycache__/embeddings.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/__pycache__/files.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/__pycache__/files.cpython-311.pyc index 0e9890f7..9a16eee4 100644 Binary files a/.venv/Lib/site-packages/openai/resources/__pycache__/files.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/__pycache__/files.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/__pycache__/images.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/__pycache__/images.cpython-311.pyc index 383a98f8..06b02b23 100644 Binary files a/.venv/Lib/site-packages/openai/resources/__pycache__/images.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/__pycache__/images.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/__pycache__/models.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/__pycache__/models.cpython-311.pyc index fb5426fe..0a44521a 100644 Binary files a/.venv/Lib/site-packages/openai/resources/__pycache__/models.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/__pycache__/models.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/__pycache__/moderations.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/__pycache__/moderations.cpython-311.pyc index e6950d77..24979d95 100644 Binary files a/.venv/Lib/site-packages/openai/resources/__pycache__/moderations.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/__pycache__/moderations.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/audio/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/audio/__pycache__/__init__.cpython-311.pyc index 880a2595..7b1d311e 100644 Binary files a/.venv/Lib/site-packages/openai/resources/audio/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/audio/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/audio/__pycache__/audio.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/audio/__pycache__/audio.cpython-311.pyc index cf767c57..475ed66d 100644 Binary files a/.venv/Lib/site-packages/openai/resources/audio/__pycache__/audio.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/audio/__pycache__/audio.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/audio/__pycache__/speech.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/audio/__pycache__/speech.cpython-311.pyc index b6a9011c..7319a243 100644 Binary files a/.venv/Lib/site-packages/openai/resources/audio/__pycache__/speech.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/audio/__pycache__/speech.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/audio/__pycache__/transcriptions.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/audio/__pycache__/transcriptions.cpython-311.pyc index bf5dcfcd..91cdb3c6 100644 Binary files a/.venv/Lib/site-packages/openai/resources/audio/__pycache__/transcriptions.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/audio/__pycache__/transcriptions.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/audio/__pycache__/translations.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/audio/__pycache__/translations.cpython-311.pyc index e42ec5e9..69841fc2 100644 Binary files a/.venv/Lib/site-packages/openai/resources/audio/__pycache__/translations.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/audio/__pycache__/translations.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/beta/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/beta/__pycache__/__init__.cpython-311.pyc index e0bbc4a9..c23a8929 100644 Binary files a/.venv/Lib/site-packages/openai/resources/beta/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/beta/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/beta/__pycache__/assistants.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/beta/__pycache__/assistants.cpython-311.pyc index e63b8298..9893dba1 100644 Binary files a/.venv/Lib/site-packages/openai/resources/beta/__pycache__/assistants.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/beta/__pycache__/assistants.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/beta/__pycache__/beta.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/beta/__pycache__/beta.cpython-311.pyc index 67f9c547..f6b7bb9f 100644 Binary files a/.venv/Lib/site-packages/openai/resources/beta/__pycache__/beta.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/beta/__pycache__/beta.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/beta/threads/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/beta/threads/__pycache__/__init__.cpython-311.pyc index aeb13484..6b964f90 100644 Binary files a/.venv/Lib/site-packages/openai/resources/beta/threads/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/beta/threads/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/beta/threads/__pycache__/messages.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/beta/threads/__pycache__/messages.cpython-311.pyc index e792128d..87b89cc4 100644 Binary files a/.venv/Lib/site-packages/openai/resources/beta/threads/__pycache__/messages.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/beta/threads/__pycache__/messages.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/beta/threads/__pycache__/threads.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/beta/threads/__pycache__/threads.cpython-311.pyc index 36ea6bd5..3665a691 100644 Binary files a/.venv/Lib/site-packages/openai/resources/beta/threads/__pycache__/threads.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/beta/threads/__pycache__/threads.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/beta/threads/runs/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/beta/threads/runs/__pycache__/__init__.cpython-311.pyc index 1be45dbc..883fa97c 100644 Binary files a/.venv/Lib/site-packages/openai/resources/beta/threads/runs/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/beta/threads/runs/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/beta/threads/runs/__pycache__/runs.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/beta/threads/runs/__pycache__/runs.cpython-311.pyc index 2c485a27..3085fcf7 100644 Binary files a/.venv/Lib/site-packages/openai/resources/beta/threads/runs/__pycache__/runs.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/beta/threads/runs/__pycache__/runs.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/beta/threads/runs/__pycache__/steps.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/beta/threads/runs/__pycache__/steps.cpython-311.pyc index f9f635f8..6775a575 100644 Binary files a/.venv/Lib/site-packages/openai/resources/beta/threads/runs/__pycache__/steps.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/beta/threads/runs/__pycache__/steps.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/beta/vector_stores/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/beta/vector_stores/__pycache__/__init__.cpython-311.pyc index de8f357c..53a96673 100644 Binary files a/.venv/Lib/site-packages/openai/resources/beta/vector_stores/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/beta/vector_stores/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/beta/vector_stores/__pycache__/file_batches.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/beta/vector_stores/__pycache__/file_batches.cpython-311.pyc index e36e4b06..6d90a4b2 100644 Binary files a/.venv/Lib/site-packages/openai/resources/beta/vector_stores/__pycache__/file_batches.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/beta/vector_stores/__pycache__/file_batches.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/beta/vector_stores/__pycache__/files.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/beta/vector_stores/__pycache__/files.cpython-311.pyc index 41f6f131..da9be763 100644 Binary files a/.venv/Lib/site-packages/openai/resources/beta/vector_stores/__pycache__/files.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/beta/vector_stores/__pycache__/files.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/beta/vector_stores/__pycache__/vector_stores.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/beta/vector_stores/__pycache__/vector_stores.cpython-311.pyc index 63433c40..5adeef66 100644 Binary files a/.venv/Lib/site-packages/openai/resources/beta/vector_stores/__pycache__/vector_stores.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/beta/vector_stores/__pycache__/vector_stores.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/chat/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/chat/__pycache__/__init__.cpython-311.pyc index 4a260928..fe0770ed 100644 Binary files a/.venv/Lib/site-packages/openai/resources/chat/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/chat/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/chat/__pycache__/chat.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/chat/__pycache__/chat.cpython-311.pyc index 7cbe314c..a681b5c7 100644 Binary files a/.venv/Lib/site-packages/openai/resources/chat/__pycache__/chat.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/chat/__pycache__/chat.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/chat/__pycache__/completions.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/chat/__pycache__/completions.cpython-311.pyc index f886aa6c..b2f734e9 100644 Binary files a/.venv/Lib/site-packages/openai/resources/chat/__pycache__/completions.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/chat/__pycache__/completions.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/fine_tuning/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/fine_tuning/__pycache__/__init__.cpython-311.pyc index c1c93aee..ae1496d0 100644 Binary files a/.venv/Lib/site-packages/openai/resources/fine_tuning/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/fine_tuning/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/fine_tuning/__pycache__/fine_tuning.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/fine_tuning/__pycache__/fine_tuning.cpython-311.pyc index 307c7d09..210e28c1 100644 Binary files a/.venv/Lib/site-packages/openai/resources/fine_tuning/__pycache__/fine_tuning.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/fine_tuning/__pycache__/fine_tuning.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/fine_tuning/jobs/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/fine_tuning/jobs/__pycache__/__init__.cpython-311.pyc index 68c21a95..a19ecd6b 100644 Binary files a/.venv/Lib/site-packages/openai/resources/fine_tuning/jobs/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/fine_tuning/jobs/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/fine_tuning/jobs/__pycache__/checkpoints.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/fine_tuning/jobs/__pycache__/checkpoints.cpython-311.pyc index 2ba4e6bf..7291e2ab 100644 Binary files a/.venv/Lib/site-packages/openai/resources/fine_tuning/jobs/__pycache__/checkpoints.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/fine_tuning/jobs/__pycache__/checkpoints.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/resources/fine_tuning/jobs/__pycache__/jobs.cpython-311.pyc b/.venv/Lib/site-packages/openai/resources/fine_tuning/jobs/__pycache__/jobs.cpython-311.pyc index 35a241b5..d1c447eb 100644 Binary files a/.venv/Lib/site-packages/openai/resources/fine_tuning/jobs/__pycache__/jobs.cpython-311.pyc and b/.venv/Lib/site-packages/openai/resources/fine_tuning/jobs/__pycache__/jobs.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/__init__.cpython-311.pyc index 6db69ec6..53c533ba 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/batch.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/batch.cpython-311.pyc index 467e6b38..e13ef900 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/batch.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/batch.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/batch_create_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/batch_create_params.cpython-311.pyc index 6c5033de..3c2f37f8 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/batch_create_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/batch_create_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/batch_error.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/batch_error.cpython-311.pyc index 20d64f99..0e200e2e 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/batch_error.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/batch_error.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/batch_list_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/batch_list_params.cpython-311.pyc index 517230d7..c388922f 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/batch_list_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/batch_list_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/batch_request_counts.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/batch_request_counts.cpython-311.pyc index 120af815..8c62477b 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/batch_request_counts.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/batch_request_counts.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/chat_model.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/chat_model.cpython-311.pyc index 79cbe7fa..4007ffdd 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/chat_model.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/chat_model.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/completion.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/completion.cpython-311.pyc index 04555f98..83043025 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/completion.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/completion.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/completion_choice.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/completion_choice.cpython-311.pyc index 0041ca6d..640fd874 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/completion_choice.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/completion_choice.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/completion_create_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/completion_create_params.cpython-311.pyc index ceb82658..ede70278 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/completion_create_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/completion_create_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/completion_usage.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/completion_usage.cpython-311.pyc index c11959da..7afcf0cf 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/completion_usage.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/completion_usage.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/create_embedding_response.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/create_embedding_response.cpython-311.pyc index bda65892..d188fdfd 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/create_embedding_response.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/create_embedding_response.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/embedding.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/embedding.cpython-311.pyc index 8a1e8b2d..50e1c0b3 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/embedding.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/embedding.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/embedding_create_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/embedding_create_params.cpython-311.pyc index 9aa651bc..f758faf5 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/embedding_create_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/embedding_create_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/file_content.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/file_content.cpython-311.pyc index 9903a43f..d53e00a5 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/file_content.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/file_content.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/file_create_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/file_create_params.cpython-311.pyc index 9c212ca7..908f689e 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/file_create_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/file_create_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/file_deleted.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/file_deleted.cpython-311.pyc index 7b8e166d..63fea59b 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/file_deleted.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/file_deleted.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/file_list_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/file_list_params.cpython-311.pyc index 82c4ac5f..4e0002a8 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/file_list_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/file_list_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/file_object.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/file_object.cpython-311.pyc index 497abbdd..737e8ac7 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/file_object.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/file_object.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/image.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/image.cpython-311.pyc index 9a4e4acd..59ff4cc8 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/image.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/image.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/image_create_variation_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/image_create_variation_params.cpython-311.pyc index 69a05153..4701e357 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/image_create_variation_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/image_create_variation_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/image_edit_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/image_edit_params.cpython-311.pyc index 3b5ac623..d08f5fb7 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/image_edit_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/image_edit_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/image_generate_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/image_generate_params.cpython-311.pyc index 9e0374cf..bce6b094 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/image_generate_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/image_generate_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/images_response.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/images_response.cpython-311.pyc index 7bcc171e..67895139 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/images_response.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/images_response.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/model.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/model.cpython-311.pyc index 2439d794..286b40e9 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/model.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/model.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/model_deleted.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/model_deleted.cpython-311.pyc index 893738a3..bb2a0b95 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/model_deleted.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/model_deleted.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/moderation.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/moderation.cpython-311.pyc index eb24bab8..5f7ce5d1 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/moderation.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/moderation.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/moderation_create_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/moderation_create_params.cpython-311.pyc index 0ac567e6..cfb5bf34 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/moderation_create_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/moderation_create_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/__pycache__/moderation_create_response.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/__pycache__/moderation_create_response.cpython-311.pyc index 32afab16..8202262f 100644 Binary files a/.venv/Lib/site-packages/openai/types/__pycache__/moderation_create_response.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/__pycache__/moderation_create_response.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/audio/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/audio/__pycache__/__init__.cpython-311.pyc index 15a80a31..423c9194 100644 Binary files a/.venv/Lib/site-packages/openai/types/audio/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/audio/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/audio/__pycache__/speech_create_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/audio/__pycache__/speech_create_params.cpython-311.pyc index 06059a5f..114895e1 100644 Binary files a/.venv/Lib/site-packages/openai/types/audio/__pycache__/speech_create_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/audio/__pycache__/speech_create_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/audio/__pycache__/transcription.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/audio/__pycache__/transcription.cpython-311.pyc index a807b9d0..45ca3ac0 100644 Binary files a/.venv/Lib/site-packages/openai/types/audio/__pycache__/transcription.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/audio/__pycache__/transcription.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/audio/__pycache__/transcription_create_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/audio/__pycache__/transcription_create_params.cpython-311.pyc index 300d6750..2d537131 100644 Binary files a/.venv/Lib/site-packages/openai/types/audio/__pycache__/transcription_create_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/audio/__pycache__/transcription_create_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/audio/__pycache__/translation.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/audio/__pycache__/translation.cpython-311.pyc index e1a51a6b..b9454238 100644 Binary files a/.venv/Lib/site-packages/openai/types/audio/__pycache__/translation.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/audio/__pycache__/translation.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/audio/__pycache__/translation_create_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/audio/__pycache__/translation_create_params.cpython-311.pyc index a2ec5b35..86a50dfc 100644 Binary files a/.venv/Lib/site-packages/openai/types/audio/__pycache__/translation_create_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/audio/__pycache__/translation_create_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/__init__.cpython-311.pyc index 3f90ab1b..1a5723f1 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant.cpython-311.pyc index 7d75c870..83d76bdc 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_create_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_create_params.cpython-311.pyc index d0ddaaba..7557dfdd 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_create_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_create_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_deleted.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_deleted.cpython-311.pyc index dfb70fd4..1bd9078a 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_deleted.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_deleted.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_list_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_list_params.cpython-311.pyc index 302f7291..9d22e5c4 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_list_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_list_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_response_format.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_response_format.cpython-311.pyc index d2987554..a41d43e5 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_response_format.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_response_format.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_response_format_option.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_response_format_option.cpython-311.pyc index 429f4a07..45375b65 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_response_format_option.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_response_format_option.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_response_format_option_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_response_format_option_param.cpython-311.pyc index d9bb4157..7797cddc 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_response_format_option_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_response_format_option_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_response_format_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_response_format_param.cpython-311.pyc index 9d0d6d14..5e928b8c 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_response_format_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_response_format_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_stream_event.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_stream_event.cpython-311.pyc index e16b5bde..94472002 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_stream_event.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_stream_event.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool.cpython-311.pyc index 0cc129a5..b996331c 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice.cpython-311.pyc index acc95d6c..43869c71 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_function.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_function.cpython-311.pyc index 125fce47..8018ce15 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_function.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_function.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_function_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_function_param.cpython-311.pyc index fa8799b4..22a9f373 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_function_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_function_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_option.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_option.cpython-311.pyc index 37ed9703..79ead258 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_option.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_option.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_option_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_option_param.cpython-311.pyc index c216c790..aa57ec47 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_option_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_option_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_param.cpython-311.pyc index cc00d002..13fb9726 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_choice_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_param.cpython-311.pyc index 97a85415..02ec2171 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_tool_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_update_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_update_params.cpython-311.pyc index 4c78123b..82b63964 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_update_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/assistant_update_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/code_interpreter_tool.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/code_interpreter_tool.cpython-311.pyc index 50233041..95397f24 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/code_interpreter_tool.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/code_interpreter_tool.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/code_interpreter_tool_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/code_interpreter_tool_param.cpython-311.pyc index 605d958a..e793fbc0 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/code_interpreter_tool_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/code_interpreter_tool_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/file_search_tool.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/file_search_tool.cpython-311.pyc index 7028aebe..b24bf5c6 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/file_search_tool.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/file_search_tool.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/file_search_tool_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/file_search_tool_param.cpython-311.pyc index 83261462..6fdc0e5d 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/file_search_tool_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/file_search_tool_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/function_tool.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/function_tool.cpython-311.pyc index 24524f2b..878e8b13 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/function_tool.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/function_tool.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/function_tool_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/function_tool_param.cpython-311.pyc index ab06e968..7c6fa569 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/function_tool_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/function_tool_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/thread.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/thread.cpython-311.pyc index 4119b073..e19e1901 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/thread.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/thread.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/thread_create_and_run_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/thread_create_and_run_params.cpython-311.pyc index bbba5c99..a6df2355 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/thread_create_and_run_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/thread_create_and_run_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/thread_create_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/thread_create_params.cpython-311.pyc index 12d78f60..7b8c96f6 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/thread_create_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/thread_create_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/thread_deleted.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/thread_deleted.cpython-311.pyc index 7c8399a3..a99420b6 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/thread_deleted.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/thread_deleted.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/thread_update_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/thread_update_params.cpython-311.pyc index c314e832..b6eb4737 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/thread_update_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/thread_update_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/vector_store.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/vector_store.cpython-311.pyc index 319a48a7..511d9b9c 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/vector_store.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/vector_store.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/vector_store_create_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/vector_store_create_params.cpython-311.pyc index cde70818..16113537 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/vector_store_create_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/vector_store_create_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/vector_store_deleted.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/vector_store_deleted.cpython-311.pyc index 3bfe24ac..82212f75 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/vector_store_deleted.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/vector_store_deleted.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/vector_store_list_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/vector_store_list_params.cpython-311.pyc index eff50f4f..4bc72039 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/vector_store_list_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/vector_store_list_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/__pycache__/vector_store_update_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/__pycache__/vector_store_update_params.cpython-311.pyc index fe5e02be..52b8b1b4 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/__pycache__/vector_store_update_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/__pycache__/vector_store_update_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/__init__.cpython-311.pyc index 206520e4..6cdac54d 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/annotation.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/annotation.cpython-311.pyc index fe3f7f21..4f333307 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/annotation.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/annotation.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/annotation_delta.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/annotation_delta.cpython-311.pyc index 8ce3e5fb..ec0b80a6 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/annotation_delta.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/annotation_delta.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/file_citation_annotation.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/file_citation_annotation.cpython-311.pyc index a0450825..f0e6be4b 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/file_citation_annotation.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/file_citation_annotation.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/file_citation_delta_annotation.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/file_citation_delta_annotation.cpython-311.pyc index 5e901d5f..7884c6df 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/file_citation_delta_annotation.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/file_citation_delta_annotation.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/file_path_annotation.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/file_path_annotation.cpython-311.pyc index 1ce163d3..e40dc5d8 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/file_path_annotation.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/file_path_annotation.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/file_path_delta_annotation.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/file_path_delta_annotation.cpython-311.pyc index 96d8154e..0a28adc2 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/file_path_delta_annotation.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/file_path_delta_annotation.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/image_file.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/image_file.cpython-311.pyc index ddcc9403..813cde20 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/image_file.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/image_file.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/image_file_content_block.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/image_file_content_block.cpython-311.pyc index af0c713a..fb7ab3b4 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/image_file_content_block.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/image_file_content_block.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/image_file_delta.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/image_file_delta.cpython-311.pyc index c79d1e21..604f4d09 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/image_file_delta.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/image_file_delta.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/image_file_delta_block.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/image_file_delta_block.cpython-311.pyc index 25f6e06a..2f336ffd 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/image_file_delta_block.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/image_file_delta_block.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message.cpython-311.pyc index 24687726..0e992a04 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_content.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_content.cpython-311.pyc index df4f63f9..20a8c374 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_content.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_content.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_content_delta.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_content_delta.cpython-311.pyc index 89496844..fe8f242f 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_content_delta.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_content_delta.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_create_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_create_params.cpython-311.pyc index c87da808..1ce279e7 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_create_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_create_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_deleted.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_deleted.cpython-311.pyc index 92e8023e..5c2b95ab 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_deleted.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_deleted.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_delta.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_delta.cpython-311.pyc index 5f11c8a8..bd2f5583 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_delta.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_delta.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_delta_event.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_delta_event.cpython-311.pyc index 812d004b..d8416380 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_delta_event.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_delta_event.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_list_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_list_params.cpython-311.pyc index 20e336ca..e0c38e47 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_list_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_list_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_update_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_update_params.cpython-311.pyc index 2a9fce17..d6bbe1f4 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_update_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/message_update_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/required_action_function_tool_call.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/required_action_function_tool_call.cpython-311.pyc index 5ea608c9..e9956213 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/required_action_function_tool_call.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/required_action_function_tool_call.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run.cpython-311.pyc index f476dbb3..f7549626 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run_create_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run_create_params.cpython-311.pyc index 0ce9f026..b7737c2f 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run_create_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run_create_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run_list_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run_list_params.cpython-311.pyc index 0253b112..c7bf6f45 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run_list_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run_list_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run_status.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run_status.cpython-311.pyc index 1bf51713..db67769f 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run_status.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run_status.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run_submit_tool_outputs_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run_submit_tool_outputs_params.cpython-311.pyc index df8c2150..40c801a6 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run_submit_tool_outputs_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run_submit_tool_outputs_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run_update_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run_update_params.cpython-311.pyc index 9ccebc90..9704803b 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run_update_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/run_update_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/text.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/text.cpython-311.pyc index b6613eb2..ba2b53bf 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/text.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/text.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/text_content_block.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/text_content_block.cpython-311.pyc index 7ffb7d18..be78fe28 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/text_content_block.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/text_content_block.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/text_delta.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/text_delta.cpython-311.pyc index 86de0b5d..4266813c 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/text_delta.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/text_delta.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/text_delta_block.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/text_delta_block.cpython-311.pyc index aff4173c..4689bd6d 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/text_delta_block.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/__pycache__/text_delta_block.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/__init__.cpython-311.pyc index 08a0dae7..94b945ed 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_logs.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_logs.cpython-311.pyc index 1eba2df5..b5736402 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_logs.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_logs.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_output_image.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_output_image.cpython-311.pyc index 4ea08e08..fb17f6e0 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_output_image.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_output_image.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_tool_call.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_tool_call.cpython-311.pyc index 474bf2d1..835e831f 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_tool_call.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_tool_call.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_tool_call_delta.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_tool_call_delta.cpython-311.pyc index 707732b4..d83c2552 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_tool_call_delta.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/code_interpreter_tool_call_delta.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/file_search_tool_call.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/file_search_tool_call.cpython-311.pyc index 78571317..a881ff30 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/file_search_tool_call.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/file_search_tool_call.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/file_search_tool_call_delta.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/file_search_tool_call_delta.cpython-311.pyc index c6413e63..1783ce2e 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/file_search_tool_call_delta.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/file_search_tool_call_delta.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/function_tool_call.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/function_tool_call.cpython-311.pyc index 654c12ac..caccf482 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/function_tool_call.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/function_tool_call.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/function_tool_call_delta.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/function_tool_call_delta.cpython-311.pyc index a520f860..5a8e8efe 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/function_tool_call_delta.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/function_tool_call_delta.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/message_creation_step_details.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/message_creation_step_details.cpython-311.pyc index c97c0259..54bb3e0d 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/message_creation_step_details.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/message_creation_step_details.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/run_step.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/run_step.cpython-311.pyc index f419a45b..37bef27c 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/run_step.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/run_step.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_delta.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_delta.cpython-311.pyc index 2d4a75b8..b83c786f 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_delta.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_delta.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_delta_event.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_delta_event.cpython-311.pyc index 260c6a69..1c302b3e 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_delta_event.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_delta_event.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_delta_message_delta.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_delta_message_delta.cpython-311.pyc index 9c038da1..bfa0ab96 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_delta_message_delta.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/run_step_delta_message_delta.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/step_list_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/step_list_params.cpython-311.pyc index d833bde3..d32bec56 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/step_list_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/step_list_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/tool_call.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/tool_call.cpython-311.pyc index 7f855a78..7f4240c7 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/tool_call.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/tool_call.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/tool_call_delta.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/tool_call_delta.cpython-311.pyc index 29ac53d9..ee27e87c 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/tool_call_delta.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/tool_call_delta.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/tool_call_delta_object.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/tool_call_delta_object.cpython-311.pyc index 4a645991..d4bbe233 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/tool_call_delta_object.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/tool_call_delta_object.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/tool_calls_step_details.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/tool_calls_step_details.cpython-311.pyc index 3a70aa54..b4602f53 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/tool_calls_step_details.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/threads/runs/__pycache__/tool_calls_step_details.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/__init__.cpython-311.pyc index a2ba586f..86aab937 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/file_batch_create_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/file_batch_create_params.cpython-311.pyc index 43137224..d2b140cd 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/file_batch_create_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/file_batch_create_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/file_batch_list_files_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/file_batch_list_files_params.cpython-311.pyc index acd61633..656dc3fe 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/file_batch_list_files_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/file_batch_list_files_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/file_create_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/file_create_params.cpython-311.pyc index 20b696db..4f526553 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/file_create_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/file_create_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/file_list_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/file_list_params.cpython-311.pyc index a6cbb02b..f1da7588 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/file_list_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/file_list_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/vector_store_file.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/vector_store_file.cpython-311.pyc index bf1594fb..84012d9f 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/vector_store_file.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/vector_store_file.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/vector_store_file_batch.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/vector_store_file_batch.cpython-311.pyc index 0113e4a5..d5f7cdbe 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/vector_store_file_batch.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/vector_store_file_batch.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/vector_store_file_deleted.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/vector_store_file_deleted.cpython-311.pyc index 912c0857..aa65f70f 100644 Binary files a/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/vector_store_file_deleted.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/beta/vector_stores/__pycache__/vector_store_file_deleted.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/__init__.cpython-311.pyc index 47a4beb6..0f292634 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion.cpython-311.pyc index c9b8ea51..758a581a 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_assistant_message_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_assistant_message_param.cpython-311.pyc index bb446c39..b7f636c6 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_assistant_message_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_assistant_message_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_chunk.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_chunk.cpython-311.pyc index 7ee87335..ce12716e 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_chunk.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_chunk.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_image_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_image_param.cpython-311.pyc index fd76d0ba..bee44f34 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_image_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_image_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_param.cpython-311.pyc index b88b59f7..5e1a2f9a 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_text_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_text_param.cpython-311.pyc index a3fa0717..fd245b08 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_text_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_text_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_function_call_option_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_function_call_option_param.cpython-311.pyc index 4ee8259f..6c0dc963 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_function_call_option_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_function_call_option_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_function_message_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_function_message_param.cpython-311.pyc index b0e1a026..331ce28b 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_function_message_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_function_message_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_message.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_message.cpython-311.pyc index ad5c1f5a..68010a3c 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_message.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_message.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_message_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_message_param.cpython-311.pyc index 38e2176c..bf8f68e8 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_message_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_message_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_message_tool_call.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_message_tool_call.cpython-311.pyc index 6561e1b1..fb5ded40 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_message_tool_call.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_message_tool_call.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_message_tool_call_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_message_tool_call_param.cpython-311.pyc index 26fc7dbc..474922f6 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_message_tool_call_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_message_tool_call_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_named_tool_choice_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_named_tool_choice_param.cpython-311.pyc index 6cf2b506..82a0958e 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_named_tool_choice_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_named_tool_choice_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_role.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_role.cpython-311.pyc index 2c515254..ff591692 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_role.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_role.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_system_message_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_system_message_param.cpython-311.pyc index 72af4725..38d3bc8e 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_system_message_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_system_message_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_token_logprob.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_token_logprob.cpython-311.pyc index f2707485..4caceb40 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_token_logprob.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_token_logprob.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_tool_choice_option_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_tool_choice_option_param.cpython-311.pyc index b9199b13..c0f6126f 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_tool_choice_option_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_tool_choice_option_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_tool_message_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_tool_message_param.cpython-311.pyc index 59ad92fe..69b865d1 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_tool_message_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_tool_message_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_tool_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_tool_param.cpython-311.pyc index 4ed39439..df1436f8 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_tool_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_tool_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_user_message_param.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_user_message_param.cpython-311.pyc index a34edc4e..5d2e2913 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_user_message_param.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/chat_completion_user_message_param.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/chat/__pycache__/completion_create_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/chat/__pycache__/completion_create_params.cpython-311.pyc index 29ed5af9..5736beb1 100644 Binary files a/.venv/Lib/site-packages/openai/types/chat/__pycache__/completion_create_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/chat/__pycache__/completion_create_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/__init__.cpython-311.pyc index b27a1dad..e0aa6eab 100644 Binary files a/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job.cpython-311.pyc index 1644c1b2..b19b41b7 100644 Binary files a/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_event.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_event.cpython-311.pyc index 9490a918..f13b0dce 100644 Binary files a/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_event.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_event.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_integration.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_integration.cpython-311.pyc index 47282376..982c60a0 100644 Binary files a/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_integration.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_integration.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_wandb_integration.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_wandb_integration.cpython-311.pyc index e1c1027f..454281cd 100644 Binary files a/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_wandb_integration.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_wandb_integration.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_wandb_integration_object.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_wandb_integration_object.cpython-311.pyc index 5ab5ad90..98034658 100644 Binary files a/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_wandb_integration_object.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/fine_tuning_job_wandb_integration_object.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/job_create_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/job_create_params.cpython-311.pyc index 74d33dcc..1b7b2bab 100644 Binary files a/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/job_create_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/job_create_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/job_list_events_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/job_list_events_params.cpython-311.pyc index f267fbf5..611e2343 100644 Binary files a/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/job_list_events_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/job_list_events_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/job_list_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/job_list_params.cpython-311.pyc index 7918985d..ca258f3d 100644 Binary files a/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/job_list_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/fine_tuning/__pycache__/job_list_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/fine_tuning/jobs/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/fine_tuning/jobs/__pycache__/__init__.cpython-311.pyc index 2e9726df..b84297e2 100644 Binary files a/.venv/Lib/site-packages/openai/types/fine_tuning/jobs/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/fine_tuning/jobs/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/fine_tuning/jobs/__pycache__/checkpoint_list_params.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/fine_tuning/jobs/__pycache__/checkpoint_list_params.cpython-311.pyc index 1ff44b5b..dc350b3b 100644 Binary files a/.venv/Lib/site-packages/openai/types/fine_tuning/jobs/__pycache__/checkpoint_list_params.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/fine_tuning/jobs/__pycache__/checkpoint_list_params.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/fine_tuning/jobs/__pycache__/fine_tuning_job_checkpoint.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/fine_tuning/jobs/__pycache__/fine_tuning_job_checkpoint.cpython-311.pyc index 1fc1cc7d..09b36d6b 100644 Binary files a/.venv/Lib/site-packages/openai/types/fine_tuning/jobs/__pycache__/fine_tuning_job_checkpoint.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/fine_tuning/jobs/__pycache__/fine_tuning_job_checkpoint.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/shared/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/shared/__pycache__/__init__.cpython-311.pyc index 3f979fe3..51d10f32 100644 Binary files a/.venv/Lib/site-packages/openai/types/shared/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/shared/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/shared/__pycache__/error_object.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/shared/__pycache__/error_object.cpython-311.pyc index ca4ebe62..5b7e0428 100644 Binary files a/.venv/Lib/site-packages/openai/types/shared/__pycache__/error_object.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/shared/__pycache__/error_object.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/shared/__pycache__/function_definition.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/shared/__pycache__/function_definition.cpython-311.pyc index 344e4555..6f896611 100644 Binary files a/.venv/Lib/site-packages/openai/types/shared/__pycache__/function_definition.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/shared/__pycache__/function_definition.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/shared/__pycache__/function_parameters.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/shared/__pycache__/function_parameters.cpython-311.pyc index ca0c28f0..9829e985 100644 Binary files a/.venv/Lib/site-packages/openai/types/shared/__pycache__/function_parameters.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/shared/__pycache__/function_parameters.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/shared_params/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/shared_params/__pycache__/__init__.cpython-311.pyc index 15ad2646..26cec41e 100644 Binary files a/.venv/Lib/site-packages/openai/types/shared_params/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/shared_params/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/shared_params/__pycache__/function_definition.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/shared_params/__pycache__/function_definition.cpython-311.pyc index 4c8e7615..fe2eef23 100644 Binary files a/.venv/Lib/site-packages/openai/types/shared_params/__pycache__/function_definition.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/shared_params/__pycache__/function_definition.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/openai/types/shared_params/__pycache__/function_parameters.cpython-311.pyc b/.venv/Lib/site-packages/openai/types/shared_params/__pycache__/function_parameters.cpython-311.pyc index 8ea08b27..af2fcf4a 100644 Binary files a/.venv/Lib/site-packages/openai/types/shared_params/__pycache__/function_parameters.cpython-311.pyc and b/.venv/Lib/site-packages/openai/types/shared_params/__pycache__/function_parameters.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/proglog/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/proglog/__pycache__/__init__.cpython-311.pyc index f5f9d988..4d4a3fee 100644 Binary files a/.venv/Lib/site-packages/proglog/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/proglog/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/proglog/__pycache__/proglog.cpython-311.pyc b/.venv/Lib/site-packages/proglog/__pycache__/proglog.cpython-311.pyc index 712b8bcf..cecd8f64 100644 Binary files a/.venv/Lib/site-packages/proglog/__pycache__/proglog.cpython-311.pyc and b/.venv/Lib/site-packages/proglog/__pycache__/proglog.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/proglog/__pycache__/version.cpython-311.pyc b/.venv/Lib/site-packages/proglog/__pycache__/version.cpython-311.pyc index 02572967..696442c3 100644 Binary files a/.venv/Lib/site-packages/proglog/__pycache__/version.cpython-311.pyc and b/.venv/Lib/site-packages/proglog/__pycache__/version.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame-2.5.2.dist-info/INSTALLER b/.venv/Lib/site-packages/pygame-2.5.2.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/Lib/site-packages/pygame-2.5.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/Lib/site-packages/pygame-2.5.2.dist-info/METADATA b/.venv/Lib/site-packages/pygame-2.5.2.dist-info/METADATA new file mode 100644 index 00000000..cb3c18d2 --- /dev/null +++ b/.venv/Lib/site-packages/pygame-2.5.2.dist-info/METADATA @@ -0,0 +1,305 @@ +Metadata-Version: 2.1 +Name: pygame +Version: 2.5.2 +Summary: Python Game Development +Home-page: https://www.pygame.org +Author: A community project. +Author-email: pygame@pygame.org +License: LGPL +Project-URL: Documentation, https://pygame.org/docs +Project-URL: Bug Tracker, https://github.com/pygame/pygame/issues +Project-URL: Source, https://github.com/pygame/pygame +Project-URL: Twitter, https://twitter.com/pygame_org +Classifier: Development Status :: 6 - Mature +Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) +Classifier: Programming Language :: Assembly +Classifier: Programming Language :: C +Classifier: Programming Language :: Cython +Classifier: Programming Language :: Objective C +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Games/Entertainment +Classifier: Topic :: Multimedia :: Sound/Audio +Classifier: Topic :: Multimedia :: Sound/Audio :: MIDI +Classifier: Topic :: Multimedia :: Sound/Audio :: Players +Classifier: Topic :: Multimedia :: Graphics +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Digital Camera +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Screen Capture +Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion +Classifier: Topic :: Multimedia :: Graphics :: Viewers +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX +Classifier: Operating System :: Unix +Classifier: Operating System :: MacOS +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst + +.. image:: https://raw.githubusercontent.com/pygame/pygame/main/docs/reST/_static/pygame_logo.svg + :alt: pygame + :target: https://www.pygame.org/ + + +|AppVeyorBuild| |PyPiVersion| |PyPiLicense| +|Python3| |GithubCommits| |BlackFormatBadge| + +Pygame_ is a free and open-source cross-platform library +for the development of multimedia applications like video games using Python. +It uses the `Simple DirectMedia Layer library`_ and several other +popular libraries to abstract the most common functions, making writing +these programs a more intuitive task. + +`We need your help`_ to make pygame the best it can be! +New contributors are welcome. + + +Installation +------------ + +Before installing pygame, you must check that Python is installed +on your machine. To find out, open a command prompt (if you have +Windows) or a terminal (if you have MacOS or Linux) and type this: +:: + + python --version + + +If a message such as "Python 3.8.10" appears, it means that Python +is correctly installed. If an error message appears, it means that +it is not installed yet. You must then go to the `official website +`_ and follow the instructions. + +Once Python is installed, you have to perform a final check: you have +to see if pip is installed. Generally, pip is pre-installed with +Python but we are never sure. Same as for Python, type the following +command: +:: + + pip --version + + +If a message such as "pip 20.0.2 from /usr/lib/python3/dist-packages/pip +(python 3.8)" appears, you are ready to install pygame! To install +it, enter this command: +:: + + pip install pygame + + +Help +---- + +If you are just getting started with pygame, you should be able to +get started fairly quickly. Pygame comes with many tutorials and +introductions. There is also full reference documentation for the +entire library. Browse the documentation on the `docs page`_. You +can also browse the documentation locally by running +``python -m pygame.docs`` in your terminal. If the docs aren't found +locally, it'll launch the online website instead. + +The online documentation stays up to date with the development version +of pygame on github. This may be a bit newer than the version of pygame +you are using. To upgrade to the latest full release, run +``pip install pygame --upgrade`` in your terminal. + +Best of all, the examples directory has many playable small programs +which can get you started playing with the code right away. + +Pygame is a powerful library for game development, offering a wide +range of features to simplify your coding journey. Let's delve into +what Pygame has to offer: + +Graphics: With Pygame, creating dynamic and engaging graphics has +never been easier. The library provides simple yet effective tools for +2D graphics and animation, including support for images, rectangles, +and polygon shapes. Whether you're a seasoned game developer or just +starting out, Pygame has you covered. + +Sound: Pygame also includes support for playing and manipulating sound +and music, making it easy to add sound effects and background music to +your games. With support for WAV, MP3, and OGG file formats, you have +plenty of options to choose from. + +Input: Pygame provides intuitive functions for handling keyboard, mouse, +and joystick input, allowing you to quickly and easily implement player +controls in your games. No more struggling with complex input code, Pygame +makes it simple. + +Game Development: Lastly, Pygame provides a comprehensive suite of tools +and features specifically designed for game development. From collision +detection to sprite management, Pygame has everything you need to create +exciting and engaging games. Whether you're building a platformer, puzzle +game, or anything in between, Pygame has you covered. + +Building From Source +-------------------- + +If you want to use features that are currently in development, +or you want to contribute to pygame, you will need to build pygame +locally from its source code, rather than pip installing it. + +Installing from source is fairly automated. The most work will +involve compiling and installing all the pygame dependencies. Once +that is done, run the ``setup.py`` script which will attempt to +auto-configure, build, and install pygame. + +Much more information about installing and compiling is available +on the `Compilation wiki page`_. + +Contribute +---------- +| Thank you for thinking of contributing! +| +| To contribute to the main `project documentation `_, see ``docs/README.md`` or view more detailed instructions `here `_. +| +| New to contributing to Open Source Free Libre software? +| There is a draft of `"Let's write a unit test!" `_ which is a step by step guide on how to write your first unit test in python for pygame, which is very similar to how you would do it for other projects. +| +| Want or need to compile pygame from source? +| `See the compilation page `_ for more detailed instructions. +| +| For a detailed developer guide on "How to Hack Pygame": +| Head to the `Hacking Page `_. +| +| Beginner developers looking for ways to contribute to the project can look at issues labeled `"good first issue" `_ or `"Difficulty: Easy" `_. +| +| To submit patches and report bugs: +| Visit the `Bugs & Patches `_ page for detailed instructions. +| +| `See the info page `_ for more info and ways to get in touch with the Pygame team. + + +Credits +------- + +Thanks to everyone who has helped contribute to this library. +Special thanks are also in order. + +* Marcus Von Appen: many changes, and fixes, 1.7.1+ freebsd maintainer +* Lenard Lindstrom: the 1.8+ windows maintainer, many changes, and fixes +* Brian Fisher for svn auto builder, bug tracker and many contributions +* Rene Dudfield: many changes, and fixes, 1.7+ release manager/maintainer +* Phil Hassey for his work on the pygame.org website +* DR0ID for his work on the sprite module +* Richard Goedeken for his smoothscale function +* Ulf Ekström for his pixel perfect collision detection code +* Pete Shinners: original author +* David Clark for filling the right-hand-man position +* Ed Boraas and Francis Irving: Debian packages +* Maxim Sobolev: FreeBSD packaging +* Bob Ippolito: MacOS and OS X porting (much work!) +* Jan Ekhol, Ray Kelm, and Peter Nicolai: putting up with early design ideas +* Nat Pryce for starting our unit tests +* Dan Richter for documentation work +* TheCorruptor for his incredible logos and graphics +* Nicholas Dudfield: many test improvements +* Alex Folkner for pygame-ctypes + +Thanks to those sending in patches and fixes: Niki Spahiev, Gordon +Tyler, Nathaniel Pryce, Dave Wallace, John Popplewell, Michael Urman, +Andrew Straw, Michael Hudson, Ole Martin Bjoerndalen, Herve Cauwelier, +James Mazer, Lalo Martins, Timothy Stranex, Chad Lester, Matthias +Spiller, Bo Jangeborg, Dmitry Borisov, Campbell Barton, Diego Essaya, +Eyal Lotem, Regis Desgroppes, Emmanuel Hainry, Randy Kaelber +Matthew L Daniel, Nirav Patel, Forrest Voight, Charlie Nolan, +Frankie Robertson, John Krukoff, Lorenz Quack, Nick Irvine, +Michael George, Saul Spatz, Thomas Ibbotson, Tom Rothamel, Evan Kroske, +Cambell Barton. + +And our bug hunters above and beyond: Angus, Guillaume Proux, Frank +Raiser, Austin Henry, Kaweh Kazemi, Arturo Aldama, Mike Mulcheck, +Michael Benfield, David Lau + +There's many more folks out there who've submitted helpful ideas, kept +this project going, and basically made our life easier. Thanks! + +Many thank you's for people making documentation comments, and adding to the +pygame.org wiki. + +Also many thanks for people creating games and putting them on the +pygame.org website for others to learn from and enjoy. + +Lots of thanks to James Paige for hosting the pygame bugzilla. + +Also a big thanks to Roger Dingledine and the crew at SEUL.ORG for our +excellent hosting. + +Dependencies +------------ + +Pygame is obviously strongly dependent on SDL and Python. It also +links to and embeds several other smaller libraries. The font +module relies on SDL_ttf, which is dependent on freetype. The mixer +(and mixer.music) modules depend on SDL_mixer. The image module +depends on SDL_image, which also can use libjpeg and libpng. The +transform module has an embedded version of SDL_rotozoom for its +own rotozoom function. The surfarray module requires the Python +NumPy package for its multidimensional numeric arrays. +Dependency versions: + + ++----------+------------------------+ +| CPython | >= 3.6 (Or use PyPy3) | ++----------+------------------------+ +| SDL | >= 2.0.8 | ++----------+------------------------+ +| SDL_mixer| >= 2.0.0 | ++----------+------------------------+ +| SDL_image| >= 2.0.2 | ++----------+------------------------+ +| SDL_ttf | >= 2.0.11 | ++----------+------------------------+ +| SDL_gfx | (Optional, vendored in)| ++----------+------------------------+ +| NumPy | >= 1.6.2 (Optional) | ++----------+------------------------+ + + + +License +------- + +This library is distributed under `GNU LGPL version 2.1`_, which can +be found in the file ``docs/LGPL.txt``. We reserve the right to place +future versions of this library under a different license. + +This basically means you can use pygame in any project you want, +but if you make any changes or additions to pygame itself, those +must be released with a compatible license (preferably submitted +back to the pygame project). Closed source and commercial games are fine. + +The programs in the ``examples`` subdirectory are in the public domain. + +See docs/licenses for licenses of dependencies. + + +.. |AppVeyorBuild| image:: https://ci.appveyor.com/api/projects/status/x4074ybuobsh4myx?svg=true + :target: https://ci.appveyor.com/project/pygame/pygame + +.. |PyPiVersion| image:: https://img.shields.io/pypi/v/pygame.svg?v=1 + :target: https://pypi.python.org/pypi/pygame + +.. |PyPiLicense| image:: https://img.shields.io/pypi/l/pygame.svg?v=1 + :target: https://pypi.python.org/pypi/pygame + +.. |Python3| image:: https://img.shields.io/badge/python-3-blue.svg?v=1 + +.. |GithubCommits| image:: https://img.shields.io/github/commits-since/pygame/pygame/2.1.2.svg + :target: https://github.com/pygame/pygame/compare/2.1.2...main + +.. |BlackFormatBadge| image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/psf/black + +.. _pygame: https://www.pygame.org +.. _Simple DirectMedia Layer library: https://www.libsdl.org +.. _We need your help: https://www.pygame.org/contribute.html +.. _Compilation wiki page: https://www.pygame.org/wiki/Compilation +.. _docs page: https://www.pygame.org/docs/ +.. _GNU LGPL version 2.1: https://www.gnu.org/copyleft/lesser.html diff --git a/.venv/Lib/site-packages/pygame-2.5.2.dist-info/RECORD b/.venv/Lib/site-packages/pygame-2.5.2.dist-info/RECORD new file mode 100644 index 00000000..156e0a1b --- /dev/null +++ b/.venv/Lib/site-packages/pygame-2.5.2.dist-info/RECORD @@ -0,0 +1,771 @@ +../../include/site/python3.11/pygame/_blit_info.h,sha256=wRHRXxQ9k7NBMHHPymtStYuI8Prwqbdu0jE0gouqUYc,470 +../../include/site/python3.11/pygame/_camera.h,sha256=T0VYAfQxm0c4zww_BZaJGz4exa4z0FdEf3RSN_W2E-E,839 +../../include/site/python3.11/pygame/_pygame.h,sha256=UhRJF2E7W8CVfKAW5cySBOxf8tvzyAX0tDjEhUrJPeM,11726 +../../include/site/python3.11/pygame/_surface.h,sha256=Bbi9rW0SqwGs6THID0l6eB5d-5h-kxW7517TdqoxEZM,957 +../../include/site/python3.11/pygame/camera.h,sha256=TKCzoB86CTE0ER6OSCMXjfqaeMemP1ToK1x1gWM1Fb4,6556 +../../include/site/python3.11/pygame/font.h,sha256=VHcKhYtIHduegTXEf1hbmbxCwN5IrqsJch2HaxEtB6I,348 +../../include/site/python3.11/pygame/freetype.h,sha256=LbGY6saj9oakyoeGSlWlirSHySOpeoTKKOF-DO4zLRs,3245 +../../include/site/python3.11/pygame/include/_pygame.h,sha256=CAfNLuuTvJHpHNyeDcFCA3OcY5NVtg-fcKiOAyvYV58,30436 +../../include/site/python3.11/pygame/include/bitmask.h,sha256=tGzYwZ407sMIHDQG7xeXBQCRmhZZ4wp-yTerhUmIlCU,4952 +../../include/site/python3.11/pygame/include/pgcompat.h,sha256=l-At6iLGU8EeMqYdsDMATS2zfkviXWe1Rs7lnOwO7oc,1944 +../../include/site/python3.11/pygame/include/pgimport.h,sha256=3VrUyOZC6kbEdOcXn1QbOZ3nusBAuFSQxYgDadHNTPI,2636 +../../include/site/python3.11/pygame/include/pgplatform.h,sha256=JHCLb_nbc-WTNu2bDQWYBfVug1ypJpM9pnIWRhj4QW8,2378 +../../include/site/python3.11/pygame/include/pygame.h,sha256=OsEc_zNPFlXo4owOhGRqH4cbuYMtNJ_7K9d_Te--OEU,1245 +../../include/site/python3.11/pygame/include/pygame_bufferproxy.h,sha256=Poh7HsIjugo3NFeKjItZe40_BavF5V7re8bVFjkpmfU,1834 +../../include/site/python3.11/pygame/include/pygame_font.h,sha256=JKPbDFQdh_BAuz5F9S37iBSZbfjnL2scUlkS1geLd4s,1501 +../../include/site/python3.11/pygame/include/pygame_freetype.h,sha256=VNyvy7xukNOXymg3IMZne9-iVu3sI5LvKLagfkWpKAk,1346 +../../include/site/python3.11/pygame/include/pygame_mask.h,sha256=ONXIz3M3MPF4BlPSS2xRquysEYjZZf7AP2JRro8j4I0,1303 +../../include/site/python3.11/pygame/include/pygame_mixer.h,sha256=HthA7STa9TLomwQQswroyAmAd72pywDu_UCLfIV71is,2021 +../../include/site/python3.11/pygame/include/sse2neon.h,sha256=DcazZmLfny6MVJFIUlWbsdI39ZWQWGwmCJDuFEVZsw0,237885 +../../include/site/python3.11/pygame/mask.h,sha256=Y7OqzNUqQQHchUsSlvd-ja5d9IgAfSV2uFlaa8_5Lys,153 +../../include/site/python3.11/pygame/mixer.h,sha256=HJMd0Ho0DrdGBdPMDo_egSqkVxZnUZPMEQyPJMIw6_M,348 +../../include/site/python3.11/pygame/palette.h,sha256=dzARYIsQdHAaV8ypCrQbYRWFisXYXABD4ToMlzYKojg,7057 +../../include/site/python3.11/pygame/pgarrinter.h,sha256=alsw7p6X7ukOB1o3curyrjWOcGHgVCQgCvS1D9FtiRc,1060 +../../include/site/python3.11/pygame/pgbufferproxy.h,sha256=tqMDkdkH40QoYJ3NtTjiknAnSMh0i1sfNMaow3npvKI,179 +../../include/site/python3.11/pygame/pgcompat.h,sha256=Ro6kJ6ak2LQSHR4LmwnulolGuQO0KW6UZiu02iP-1_I,737 +../../include/site/python3.11/pygame/pgopengl.h,sha256=bbIysbLph5paPfeE2nnrQBIrq8iZz4T4pGfz2mYPuRw,606 +../../include/site/python3.11/pygame/pgplatform.h,sha256=LuZxNMbDYRCgjDk1_ruNTOiQsyO_FcI9qxNtJeAUpXQ,553 +../../include/site/python3.11/pygame/pygame.h,sha256=AQcZWIoAWGmN9fYBynCXxc81hd9lcwxBwWeq8WZjTKE,1083 +../../include/site/python3.11/pygame/scrap.h,sha256=d36ZWp5LM7o9RRcHCFiHwAT0aJZN_c17wTNu_LYHuEE,4704 +../../include/site/python3.11/pygame/simd_blitters.h,sha256=8Bv4j0uHf4ErZMeJeXjnrnVvKBQdSLmKcrEVgbYJlPc,2494 +../../include/site/python3.11/pygame/surface.h,sha256=6Goq2WvnwB_mYosiq4wZMIzPXn7uoCyA_fTK4uKtf6I,14584 +pygame-2.5.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pygame-2.5.2.dist-info/METADATA,sha256=UnQNan6erv0dG6BJvA0R3AxWMqP0X2VvNBCWwZbYCGA,13332 +pygame-2.5.2.dist-info/RECORD,, +pygame-2.5.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pygame-2.5.2.dist-info/WHEEL,sha256=badvNS-y9fEq0X-qzdZYvql_JFjI7Xfw-wR8FsjoK0I,102 +pygame-2.5.2.dist-info/entry_points.txt,sha256=dLqlr3Z1J3rgwMU_2X_oMIHnHCLXfrFyfzD2q6ewoOI,63 +pygame-2.5.2.dist-info/top_level.txt,sha256=ABXdFGIAE2g9m2VOzQPaLa917r6XEu6d96RqIzvAWCs,7 +pygame/SDL2.dll,sha256=Lk6CO0bpWimtTOTnE0QXsM1gFF_v5gaSDvbcDrz7ACE,2499072 +pygame/SDL2_image.dll,sha256=HjZK91_uDINQb739TVsOOGxOnGoz3b3axh3bEx42AZQ,125440 +pygame/SDL2_mixer.dll,sha256=Kg_F6fcsLq7DJAy4K3WUpYzNpglIWYHyVrlNCk3Y1vg,291840 +pygame/SDL2_ttf.dll,sha256=_N-r386GjrM_dRQCX_WcG7bEGPG81qziMAqc1AU-HWM,1552384 +pygame/__init__.py,sha256=cfk8mST6YzXpJH83W86aq5Sj6HzZYCZ8RYJUUR3KRt4,9483 +pygame/__init__.pyi,sha256=javlxYV7i0wDGZ69E_J2x1PLmXoEN-pfcF0MCcLqKbY,20400 +pygame/__pycache__/__init__.cpython-311.pyc,, +pygame/__pycache__/_camera_opencv.cpython-311.pyc,, +pygame/__pycache__/_camera_vidcapture.cpython-311.pyc,, +pygame/__pycache__/camera.cpython-311.pyc,, +pygame/__pycache__/colordict.cpython-311.pyc,, +pygame/__pycache__/cursors.cpython-311.pyc,, +pygame/__pycache__/draw_py.cpython-311.pyc,, +pygame/__pycache__/fastevent.cpython-311.pyc,, +pygame/__pycache__/freetype.cpython-311.pyc,, +pygame/__pycache__/ftfont.cpython-311.pyc,, +pygame/__pycache__/locals.cpython-311.pyc,, +pygame/__pycache__/macosx.cpython-311.pyc,, +pygame/__pycache__/midi.cpython-311.pyc,, +pygame/__pycache__/pkgdata.cpython-311.pyc,, +pygame/__pycache__/sndarray.cpython-311.pyc,, +pygame/__pycache__/sprite.cpython-311.pyc,, +pygame/__pycache__/surfarray.cpython-311.pyc,, +pygame/__pycache__/sysfont.cpython-311.pyc,, +pygame/__pycache__/version.cpython-311.pyc,, +pygame/__pyinstaller/__init__.py,sha256=-c4Zo8nQGKAm8wc_LDscxMtK7zr_YhZwRnC9CMruUBE,72 +pygame/__pyinstaller/__pycache__/__init__.cpython-311.pyc,, +pygame/__pyinstaller/__pycache__/hook-pygame.cpython-311.pyc,, +pygame/__pyinstaller/hook-pygame.py,sha256=F54YZHmQpTIBH265sM0r5_8_YZYWo0IBhG_QVAQybgI,1368 +pygame/_camera.cp311-win_amd64.pyd,sha256=mjm_K-B_nrg-4zO-w6uLGVBWZn9eFA6gJe081XsFPVs,31232 +pygame/_camera_opencv.py,sha256=ulz0gAMwYQRyhA2AEd_EE7kq1Pacn2e0NqgOtrEC2Lk,5461 +pygame/_camera_vidcapture.py,sha256=MJih74Ya6djH-mZZj4OYfRKJOH-SD0VUHNaUcxwX7N8,3402 +pygame/_common.pyi,sha256=YPuDlQLtP8KzA-qA9LnYkOhS9zzSvA5ENwWHI2WSvoI,1349 +pygame/_freetype.cp311-win_amd64.pyd,sha256=iEU72lvhdhgcGJxRaE-YOPN6Z8Fe_O1H2BYp2bAMQsg,78336 +pygame/_sdl2/__init__.py,sha256=gmSh3cXyxHqEXwbck_mNOmoW--itmGMwonEHQnY49Zo,248 +pygame/_sdl2/__init__.pyi,sha256=fWR2P9epfYw8F_xyyMBoUxDZwJTIroizEnkN_eLf2rw,98 +pygame/_sdl2/__pycache__/__init__.cpython-311.pyc,, +pygame/_sdl2/audio.cp311-win_amd64.pyd,sha256=7WbCho_m0bocgn4f54-CTxSy1FC-VmxVArkF_7Wwlu8,167936 +pygame/_sdl2/audio.pyi,sha256=DRRipL7rySE-0TddObb1A-l99rWQPK4YI36hvyHj4dw,1300 +pygame/_sdl2/controller.cp311-win_amd64.pyd,sha256=9BTrv6XkoaOEPWWZcAFV6pOTop1NrkaFImC1AEE8ZWE,104960 +pygame/_sdl2/controller.pyi,sha256=8WZ0Qf5iHIJTElgu29LeAhZO3Htbo00b8oL3aohK5P4,1165 +pygame/_sdl2/mixer.cp311-win_amd64.pyd,sha256=0i3uvRgrOS_kwrtujmqPZjySL5J7oykTKbZ6RxNl0As,146432 +pygame/_sdl2/sdl2.cp311-win_amd64.pyd,sha256=XA1jQHN0CwQo4Ycehwq1CIoA1W_K5jjQlGx_QAuuDpY,46080 +pygame/_sdl2/sdl2.pyi,sha256=n0Qa04-g3FdR7wiMEC_4ZhmAojvQTXGjTl1z13OWU70,338 +pygame/_sdl2/touch.cp311-win_amd64.pyd,sha256=6Kn5K18KdJMnnIof0NPFukC8DqXuYfFmnYSUaFfRt5s,13824 +pygame/_sdl2/touch.pyi,sha256=-vYGJsSC18E2gitH10HIv_V_DUc6DIeCb55zuQp3jPc,231 +pygame/_sdl2/video.cp311-win_amd64.pyd,sha256=HnlFhzAQBqzeRLXW6CIO5bXtFmG0jHZm71_H2KrzmSE,228352 +pygame/_sdl2/video.pyi,sha256=1ziRZwVFLhFTYubfNBwtyIF7hBWlCiscXul7V4dMcS8,4677 +pygame/_sprite.cp311-win_amd64.pyd,sha256=lJ7N_uj8z4RLwNs2sxjq0x7EmrbpqLh1p8Z6UB-sPmg,320512 +pygame/base.cp311-win_amd64.pyd,sha256=xBHS4r40VNlKWa8PeOC9dzcVbU8ku006q_h1u1HoCmk,30720 +pygame/base.pyi,sha256=MPRqq61kkfR-s7IXcoZyahrRP3VzERTED46GCuVacpg,586 +pygame/bufferproxy.cp311-win_amd64.pyd,sha256=v_zR7FXZJUliMcvVCxpGcOANUYpNpjgue8hZ898hGi4,18432 +pygame/bufferproxy.pyi,sha256=znPTlMWUbbJI2iCUz8F3pY0guxHOzVnQhpnUaPhfB2E,458 +pygame/camera.py,sha256=ydsKm1IR8OhHCcNEmYqzZz3IE_fw3xvbq_QDiteB9k0,5781 +pygame/camera.pyi,sha256=yeFKyldBEFDOUwMRXsqPcDdH2hOgeoth9fOuBJ8Dc8Y,1624 +pygame/color.cp311-win_amd64.pyd,sha256=zQmD_IAzCNZdC4rOPEIwqy3TuQm_7taUmNOohQu4dQ0,35328 +pygame/color.pyi,sha256=rBSvRlXAFlP4kzuVdI_z5n9TkCcZpz6_2XEAvYHG0gk,2050 +pygame/colordict.py,sha256=xS-mwTxatoVa3Howpp53otRNZ3p-b1MzZChlvy0cuic,25773 +pygame/constants.cp311-win_amd64.pyd,sha256=lPamD3tptOSZby7NUrdRJ8KuM3NYe-M1pjsg1MwXwL4,50688 +pygame/constants.pyi,sha256=PqbIVEVcVE9pKTaCFd9RExuYFf4i-1H_u_zjx6KP44U,9902 +pygame/cursors.py,sha256=2IhUpWjuLs4y_Rcs16tts4zBfLZtUSz9LzmWimN4piM,18208 +pygame/cursors.pyi,sha256=rvpf35D0IPYU2SBH30K46RRmigQfQH4SyQfb7TwnNiQ,2090 +pygame/display.cp311-win_amd64.pyd,sha256=T_0uEa62FoSCuIJzkWarP0r6TZTfWYRQgXQLsDCTlFc,44544 +pygame/display.pyi,sha256=5rC8JySsJTs0GWDaAG29lQan4HhtzIA0ilVvsi3Vvqs,2273 +pygame/docs/__main__.py,sha256=1dEMTojlXYV8qZ_T9h2f_AYdpPwwyCqmHBC8iuJ2KGI,995 +pygame/docs/__pycache__/__main__.cpython-311.pyc,, +pygame/docs/generated/LGPL.txt,sha256=oZDcnIBDdV2Q-LCnX6ZrnkLUr0yYC_XdxjPwEk2zzuc,26430 +pygame/docs/generated/_images/AdvancedInputOutput1.gif,sha256=uSCxW5dFtO7PYQyoJglWJTe_aRYFfOav5DjnPkKBzKg,5649 +pygame/docs/generated/_images/AdvancedInputOutput11.gif,sha256=uSCxW5dFtO7PYQyoJglWJTe_aRYFfOav5DjnPkKBzKg,5649 +pygame/docs/generated/_images/AdvancedInputOutput2.gif,sha256=2UMDweIgRefzHFTJcJgbDsWgy-SlyOYaw2AKD6NKwmM,72233 +pygame/docs/generated/_images/AdvancedInputOutput21.gif,sha256=2UMDweIgRefzHFTJcJgbDsWgy-SlyOYaw2AKD6NKwmM,72233 +pygame/docs/generated/_images/AdvancedInputOutput3.gif,sha256=ez4Y7Yy0LsNH6UPYybfGckLmzutcLh7VIisajgFa4O8,6294 +pygame/docs/generated/_images/AdvancedInputOutput31.gif,sha256=ez4Y7Yy0LsNH6UPYybfGckLmzutcLh7VIisajgFa4O8,6294 +pygame/docs/generated/_images/AdvancedInputOutput4.gif,sha256=2-rK9cGngrgpJLW404oI3lG4NQnoT-UNwzA3Up48YIc,29185 +pygame/docs/generated/_images/AdvancedInputOutput41.gif,sha256=2-rK9cGngrgpJLW404oI3lG4NQnoT-UNwzA3Up48YIc,29185 +pygame/docs/generated/_images/AdvancedInputOutput5.gif,sha256=C6N4d_JD4QNMjLwPdINCOFWufyD4vM7b5LpR2GaHMcY,37349 +pygame/docs/generated/_images/AdvancedInputOutput51.gif,sha256=C6N4d_JD4QNMjLwPdINCOFWufyD4vM7b5LpR2GaHMcY,37349 +pygame/docs/generated/_images/AdvancedOutputAlpha1.gif,sha256=MH3CmK658WpN_u56GL9CV4YMFky9GLaKh0CAcixkMrA,14915 +pygame/docs/generated/_images/AdvancedOutputAlpha11.gif,sha256=MH3CmK658WpN_u56GL9CV4YMFky9GLaKh0CAcixkMrA,14915 +pygame/docs/generated/_images/AdvancedOutputAlpha2.gif,sha256=9E2YAIULWHZsAELfqLf5gdqL7-uE-Ebg4uqKgbSyVYk,71819 +pygame/docs/generated/_images/AdvancedOutputAlpha21.gif,sha256=9E2YAIULWHZsAELfqLf5gdqL7-uE-Ebg4uqKgbSyVYk,71819 +pygame/docs/generated/_images/AdvancedOutputAlpha3.gif,sha256=7gHdbhSKaUpyXgOC7GSusTdN7oJQvjGFY_o8DQNZ2Fc,30380 +pygame/docs/generated/_images/AdvancedOutputAlpha31.gif,sha256=7gHdbhSKaUpyXgOC7GSusTdN7oJQvjGFY_o8DQNZ2Fc,30380 +pygame/docs/generated/_images/AdvancedOutputProcess1.gif,sha256=QihTI3ThxtEnPjQ0qZaYF4rKj5GxSwDfFgfUmFjvcOw,15951 +pygame/docs/generated/_images/AdvancedOutputProcess11.gif,sha256=QihTI3ThxtEnPjQ0qZaYF4rKj5GxSwDfFgfUmFjvcOw,15951 +pygame/docs/generated/_images/AdvancedOutputProcess2.gif,sha256=qP09Je0xWwST1CoAs9BCy7_vq6OB69opna8kejTln50,1868 +pygame/docs/generated/_images/AdvancedOutputProcess21.gif,sha256=qP09Je0xWwST1CoAs9BCy7_vq6OB69opna8kejTln50,1868 +pygame/docs/generated/_images/AdvancedOutputProcess3.gif,sha256=4WhoBbEheizUdZfs_pFEBGGAm9aoetw3tJhgcNgVIyY,1912 +pygame/docs/generated/_images/AdvancedOutputProcess31.gif,sha256=4WhoBbEheizUdZfs_pFEBGGAm9aoetw3tJhgcNgVIyY,1912 +pygame/docs/generated/_images/AdvancedOutputProcess4.gif,sha256=m-gUJNOn6AuXT7FpKF6HRR8A6ytWorY9Y07N2uZaSIQ,14500 +pygame/docs/generated/_images/AdvancedOutputProcess41.gif,sha256=m-gUJNOn6AuXT7FpKF6HRR8A6ytWorY9Y07N2uZaSIQ,14500 +pygame/docs/generated/_images/AdvancedOutputProcess5.gif,sha256=GCi9KGUIhQTFg-HLEnp2GEgrnQl8_2KftS3N_UkuEH8,16896 +pygame/docs/generated/_images/AdvancedOutputProcess51.gif,sha256=GCi9KGUIhQTFg-HLEnp2GEgrnQl8_2KftS3N_UkuEH8,16896 +pygame/docs/generated/_images/AdvancedOutputProcess6.gif,sha256=nzV_M0JoA4aDyGwpe8lWoUFd5c1PK-WxUI-lF8WzfQQ,34058 +pygame/docs/generated/_images/AdvancedOutputProcess61.gif,sha256=nzV_M0JoA4aDyGwpe8lWoUFd5c1PK-WxUI-lF8WzfQQ,34058 +pygame/docs/generated/_images/Bagic-INPUT-resultscreen.png,sha256=RDZbxtVyFMJXdZA8wouSyJJjXf2MQ2WYTBzVotsDH88,5973 +pygame/docs/generated/_images/Bagic-INPUT-resultscreen1.png,sha256=RDZbxtVyFMJXdZA8wouSyJJjXf2MQ2WYTBzVotsDH88,5973 +pygame/docs/generated/_images/Bagic-INPUT-sourcecode.png,sha256=3F2c3AnravGgsRMxaNXlaekyqrhMl2cwYySJxhj7L0A,77061 +pygame/docs/generated/_images/Bagic-INPUT-sourcecode1.png,sha256=3F2c3AnravGgsRMxaNXlaekyqrhMl2cwYySJxhj7L0A,77061 +pygame/docs/generated/_images/Bagic-PROCESS-resultscreen.png,sha256=hQ1m6S1xhXpcaf0g50VRoOagmsiZZpUeOBx7QgG7Lqs,5348 +pygame/docs/generated/_images/Bagic-PROCESS-resultscreen1.png,sha256=hQ1m6S1xhXpcaf0g50VRoOagmsiZZpUeOBx7QgG7Lqs,5348 +pygame/docs/generated/_images/Bagic-PROCESS-sourcecode.png,sha256=vj0D6wrXFNjIHKmRFZrltZH4nH51zG6YSy94ID2fWos,66070 +pygame/docs/generated/_images/Bagic-PROCESS-sourcecode1.png,sha256=vj0D6wrXFNjIHKmRFZrltZH4nH51zG6YSy94ID2fWos,66070 +pygame/docs/generated/_images/Bagic-ouput-result-screen.png,sha256=Ig1vKczM-l0ebtdjYEHdwcJuqt8IoyUiK-RANEC065k,4819 +pygame/docs/generated/_images/Bagic-ouput-result-screen1.png,sha256=Ig1vKczM-l0ebtdjYEHdwcJuqt8IoyUiK-RANEC065k,4819 +pygame/docs/generated/_images/Basic-ouput-sourcecode.png,sha256=B6OVjvOtA2ZwiEwJyYkK1-tsGyN13y8O9kls1OYvzTo,57466 +pygame/docs/generated/_images/Basic-ouput-sourcecode1.png,sha256=B6OVjvOtA2ZwiEwJyYkK1-tsGyN13y8O9kls1OYvzTo,57466 +pygame/docs/generated/_images/angle_to.png,sha256=vP3M5zZVFf-ooagw-hRTlnhBLbGQvX9gxO7nX_gBgbc,25349 +pygame/docs/generated/_images/camera_average.jpg,sha256=dkXZ7NdHmM69rbcYCpu0vKtqmHF3qh9nPUgZX3wlWDc,20881 +pygame/docs/generated/_images/camera_background.jpg,sha256=exoGN5fT9IKQyMJK_3VrEjfKTvr5yMeoSLCQplD0hes,7493 +pygame/docs/generated/_images/camera_green.jpg,sha256=NpIuT5qRzN5I7TFLva8m_kCAo1cwOuR5R5-Du9kaEo0,10219 +pygame/docs/generated/_images/camera_hsv.jpg,sha256=tfL0KJyxSk5A_KjVZR7MdV-qegBhej5HlXXw2CnoZR8,36673 +pygame/docs/generated/_images/camera_mask.jpg,sha256=0u0yMCldZMvSW1vyO2KK32D-fVYuYpXlNzmWwbdZ__s,18779 +pygame/docs/generated/_images/camera_rgb.jpg,sha256=GN_1jI8mnDJm1bRbjNBmpJETDSAKcVAS9BxmycYMMv0,32488 +pygame/docs/generated/_images/camera_thresh.jpg,sha256=WBYm8M-TxnuKCYEBvmu68iO_r9EYucnENZ5Ew8i6tqk,4346 +pygame/docs/generated/_images/camera_thresholded.jpg,sha256=OMh-3zXV2a-aahnMQlE7ihvxwXy1UADb15c3LzIWgh0,23678 +pygame/docs/generated/_images/camera_yuv.jpg,sha256=Gp0omp1py-_j6Qpv95VIo6EmDO2u5VY83fPWA2Rd6Bk,20105 +pygame/docs/generated/_images/chimpshot.gif,sha256=Yc_ufSFTkZ5NA1IogV2juH5Cr4_ykoI7QcXQvfGYBfc,46010 +pygame/docs/generated/_images/draw_module_example.png,sha256=jAhc1HG8RXjjPnjn9AwncGwfi4dKbaO7rQgqcd5OCeQ,6476 +pygame/docs/generated/_images/intro_ball.gif,sha256=vEs0-OG_j55JZJML48IXhHsN4ZMIWrqlS7dfxd9-hxc,5015 +pygame/docs/generated/_images/intro_blade.jpg,sha256=Aj59Tt9z1mdJeDK89HbWaQ7DVTDKbzoUDT-vNcJnYQo,2631 +pygame/docs/generated/_images/intro_freedom.jpg,sha256=RL-jChKVMdqoS7BN5NGV7hjlxSgU4qaKrj4ZXH2zsI8,7050 +pygame/docs/generated/_images/introduction-Battleship.png,sha256=6iHEhqo_HnXRfmQv9yriYMc3dX8Q2TwGbSGDQREFkN4,165586 +pygame/docs/generated/_images/introduction-Battleship1.png,sha256=6iHEhqo_HnXRfmQv9yriYMc3dX8Q2TwGbSGDQREFkN4,165586 +pygame/docs/generated/_images/introduction-PuyoPuyo.png,sha256=OEMjFSzQc8vJLQryUdkp7lJ3DhIw_yEo5-5nm2vBfrs,31388 +pygame/docs/generated/_images/introduction-PuyoPuyo1.png,sha256=OEMjFSzQc8vJLQryUdkp7lJ3DhIw_yEo5-5nm2vBfrs,31388 +pygame/docs/generated/_images/introduction-TPS.png,sha256=M4ioZMyjR2n7pQIp8UhGRV4m2V_rcXJCuo5lU3V7yGw,136031 +pygame/docs/generated/_images/introduction-TPS1.png,sha256=M4ioZMyjR2n7pQIp8UhGRV4m2V_rcXJCuo5lU3V7yGw,136031 +pygame/docs/generated/_images/joystick_calls.png,sha256=oNAQgfZ8GM5_Z17hoNVrLf1tYeRnths60ZrlhJduWhs,30004 +pygame/docs/generated/_images/pygame_lofi.png,sha256=QBECBUalJHRExXhf7nt-QpsVcu9LQ3RSAYVVS04AX3M,134242 +pygame/docs/generated/_images/pygame_logo.png,sha256=Jc1Lz47pY3mHjEFNzQXV9gx-olUUYCGuFYrutncaTXE,132068 +pygame/docs/generated/_images/pygame_powered.png,sha256=LgbswFcg647alSC5SawDPH96xuTMcWmMqVA6Zvs2K3Q,179911 +pygame/docs/generated/_images/pygame_powered_lowres.png,sha256=9go5WMiAE4fTEmB9w-zatEfROGl1IUWJGBpKoLqy-wU,179911 +pygame/docs/generated/_images/pygame_tiny.png,sha256=BXPk3OkSWdSkqjMkCQ1Dt5WjxZfb4zj4c2ir9U9_Y7Q,15310 +pygame/docs/generated/_images/surfarray_allblack.png,sha256=XEUO2hKFfTfZMyaqbPvM0u3zmETfl___AANBkHn6y-w,125 +pygame/docs/generated/_images/surfarray_flipped.png,sha256=UZ1FpljGrdAnB1UCUTuAfeJJxyqI-_0duSCKqy9UN3w,50835 +pygame/docs/generated/_images/surfarray_redimg.png,sha256=6tlO_tZokQTsfgvD3yNW21nHA5uA9hDWoaL7POrr_qE,23443 +pygame/docs/generated/_images/surfarray_rgbarray.png,sha256=8US5r3GcG_jZBncK-47HG2JLB2ZwQjaSBWhb3ynNs9w,50897 +pygame/docs/generated/_images/surfarray_scaledown.png,sha256=Z68XSoPUvV5bYIhJdkmhZYr42JpQKnC8g9hmN18iaWI,15109 +pygame/docs/generated/_images/surfarray_scaleup.png,sha256=sdxQlmVoRhlF_ocD2ecre0q51Q2LLoCxNsJaI8O1NYI,67759 +pygame/docs/generated/_images/surfarray_soften.png,sha256=XNzAZzfLUqn-QIQ25TxB2ujoDZkPEIRI6QSDnjJMIk0,47540 +pygame/docs/generated/_images/surfarray_striped.png,sha256=iH7gLZhBu5aATV-vfrsQmW30KdsMQoYB_LD-efRzl_Y,392 +pygame/docs/generated/_images/surfarray_xfade.png,sha256=uD8g8Ueqc3IMZaOqiD4n4sZmg5I4j798oIzl1pWDM70,41834 +pygame/docs/generated/_images/tom_basic.png,sha256=RzKBFmep1ksfD5QrJVW7JzdHvDN1_4ayUfGyVN-8Wms,5139 +pygame/docs/generated/_images/tom_event-flowchart.png,sha256=sG8YOH8YX2yTtx4-agBUWIcT8-jj1m6uKCF98QxmbKQ,5528 +pygame/docs/generated/_images/tom_formulae.png,sha256=6k8VDsueGVOh01ZrAVLE5miliOWKhQJqIrlPW_JEmXk,6763 +pygame/docs/generated/_images/tom_radians.png,sha256=BkBTx4OoiSXO5d6sMG01MyYBtO7EmDvgJixbTRKMm9Q,17409 +pygame/docs/generated/_sources/c_api.rst.txt,sha256=vSJF6tvDzMDdGTnXyyAi-Zz-iApxeuO-n5jTvceAQNw,473 +pygame/docs/generated/_sources/filepaths.rst.txt,sha256=sou-1N5amW1JXHqwKUU0BqYQmm2c5qDjjFKVIKIppfo,899 +pygame/docs/generated/_sources/index.rst.txt,sha256=UMDM3eMRAtTSwIiQfMXkaCKNWW6dlsnqR6MpxfGAMyI,5965 +pygame/docs/generated/_sources/logos.rst.txt,sha256=oJNZMoKbfJ9EuXTeipAahQGSpPkTaAcyHEIGWJPdWW8,1337 +pygame/docs/generated/_sources/ref/bufferproxy.rst.txt,sha256=V5gSzq__85alqL5Is05MTmpDv5NUHbLuuW4PYH98MzI,4708 +pygame/docs/generated/_sources/ref/camera.rst.txt,sha256=VGSdYbW2ii37WG3kC8HGn2Ao-TPRPYXTLez9VnwI364,9628 +pygame/docs/generated/_sources/ref/cdrom.rst.txt,sha256=0FlYODuxoQOsTYpZ9EiDYmUD9PIQrAeAlsXE8I67RQk,9068 +pygame/docs/generated/_sources/ref/color.rst.txt,sha256=DbdHOccIvmf8D8ERvo-fHKKvqKmZYb3tcNbQkSh_dj8,10798 +pygame/docs/generated/_sources/ref/color_list.rst.txt,sha256=XLIKLmTx_liiWk0gdKlrElBlCNZD0_sIvAg_El6tgfE,96353 +pygame/docs/generated/_sources/ref/cursors.rst.txt,sha256=dRrEevF8Uaho0It82zk-f1ySA9Pa_y3kUhM4mEm-zTQ,9415 +pygame/docs/generated/_sources/ref/display.rst.txt,sha256=AXirEKIGsJlszFewn2XoB0Jb_KM272_985BdEhqgL9U,29188 +pygame/docs/generated/_sources/ref/draw.rst.txt,sha256=O-i4wVe9KULlpBvhdKrKbAYXiszO2_FUDhjzoEArmI0,24657 +pygame/docs/generated/_sources/ref/event.rst.txt,sha256=ls05WLm5Yc0yd7t0-roayj9aNyW4awdWD5kjVruciL4,22208 +pygame/docs/generated/_sources/ref/examples.rst.txt,sha256=044bT9v4pQYRZqmaAwrdAI_GV3HLiwRlWHKYturVbN4,14095 +pygame/docs/generated/_sources/ref/fastevent.rst.txt,sha256=6gyem0cRZ-syKMDcQ6qexC83dJMro1O9-XHY_k2_HNg,3545 +pygame/docs/generated/_sources/ref/font.rst.txt,sha256=AGiup5hzk_KnfOElwadQGmzkzG9XcRex2yq9EvsNB-o,18217 +pygame/docs/generated/_sources/ref/freetype.rst.txt,sha256=R5RnUREdR9b5NPNsPYYTthl_ExWUcmX7vclWFjeNpyM,31386 +pygame/docs/generated/_sources/ref/gfxdraw.rst.txt,sha256=bQYID2bOb6xETZoDKM0mRtaCU_QD48MjQmCV8lvEErE,21842 +pygame/docs/generated/_sources/ref/image.rst.txt,sha256=4NJf_cJ9VVHm7VD1MD4msBUvjytoaCTkB83W6vubfkE,13395 +pygame/docs/generated/_sources/ref/joystick.rst.txt,sha256=sXgDyfTcwBdasNxdzW9r0Xnesxo6iwz23OBYG6SB0ws,19389 +pygame/docs/generated/_sources/ref/key.rst.txt,sha256=ntU4tjZxkI-fF0m4zQBlo5KpZuxt3bpOc3BlNAdrpFI,16327 +pygame/docs/generated/_sources/ref/locals.rst.txt,sha256=VZi8cE2ZlPei4WdzSsok0fU_BOgPhlOmjfTCTSvl0N8,1022 +pygame/docs/generated/_sources/ref/mask.rst.txt,sha256=RAVpZZNK2i-ltHlCbNZgFa80x2RAXc_4lYkPgAVoyjs,24220 +pygame/docs/generated/_sources/ref/math.rst.txt,sha256=YbCqnfzaJeI1k8AYI45w8rokZ_x9f2NdnkoK2njrv3k,38384 +pygame/docs/generated/_sources/ref/midi.rst.txt,sha256=jIJ5qOZNvlMtBH0TA2icWTSB4_7CcUmU6_y9uVhGiak,14358 +pygame/docs/generated/_sources/ref/mixer.rst.txt,sha256=tCsbvTknDWeWJTeXfgUFs4UByImuBeeg6FNgevABeZ0,22461 +pygame/docs/generated/_sources/ref/mouse.rst.txt,sha256=pzAlscsACbo_6p1pbazcbUaJW2vEf1Zx--jwy3ktA3k,8129 +pygame/docs/generated/_sources/ref/music.rst.txt,sha256=lLimQ-C1JXLMGw-gIHIsZ7r0bbav7SPTovbCqiXuO5Q,9531 +pygame/docs/generated/_sources/ref/overlay.rst.txt,sha256=loD8HVw0KQnsaPPisw_Xe8yms59CEAbdsboXwhS7zqk,2659 +pygame/docs/generated/_sources/ref/pixelarray.rst.txt,sha256=p0HY0TLZFoZAayVW9FxW8t0G4-Cjblhfd07uTkXqJK0,10204 +pygame/docs/generated/_sources/ref/pixelcopy.rst.txt,sha256=SMb-VGMZSxBBWgAjsFg2ucaQp80KglnIo56AkS7O-vE,4531 +pygame/docs/generated/_sources/ref/pygame.rst.txt,sha256=mEu8Av5H35_dC0JNXu9q1W6xnwgwvRr8ywaxNUEOcu0,15129 +pygame/docs/generated/_sources/ref/rect.rst.txt,sha256=Qf24A1_9KRJgS1-L5xlHoRWKQbDI0bE4rViR8Zc1aBM,21147 +pygame/docs/generated/_sources/ref/scrap.rst.txt,sha256=eeN2xL1riH4ZKbpNJSAXVlG1Ew2JYn1B-JiSBmdtZ_Q,7988 +pygame/docs/generated/_sources/ref/sdl2_controller.rst.txt,sha256=RUs3ssClJMfMjncOXVpj0iTUkmSnLHa9pvH_D6DcylY,9401 +pygame/docs/generated/_sources/ref/sdl2_video.rst.txt,sha256=1WSISwTtmvOX6n_wEzAFk5Yj7XgmeG5GdrPIZB8K6RQ,9117 +pygame/docs/generated/_sources/ref/sndarray.rst.txt,sha256=9N1u6w7AxTr943nJiSUoDyNi-8qE_tWJeVU0rVEQYi0,3260 +pygame/docs/generated/_sources/ref/sprite.rst.txt,sha256=2RfcFwVV0xc3duWp_n_sVJSPFa9U_SOZKRb0AkTIGOM,30575 +pygame/docs/generated/_sources/ref/surface.rst.txt,sha256=eNBOZq2pkRHQcVtErIZs8jiG-nhAXSq3CR-Bup7_OQ8,36859 +pygame/docs/generated/_sources/ref/surfarray.rst.txt,sha256=rBeiZncxG9HIetGkZS0IcwxIlHqQgCxpiZxO6bSvY8s,12251 +pygame/docs/generated/_sources/ref/tests.rst.txt,sha256=vzkEPNYlI2274wXW3FKI1c9Qqmo293E7_eZhIIV9Xs0,4636 +pygame/docs/generated/_sources/ref/time.rst.txt,sha256=WJTVcpIu2GT39OD3k5MYQ3gXTqQJwJJaiNjA7GvWMlk,5624 +pygame/docs/generated/_sources/ref/touch.rst.txt,sha256=v7V0P85KlxiQIBLjaVyhTtH1fmDmqif-kDLhV0O6x4s,1957 +pygame/docs/generated/_sources/ref/transform.rst.txt,sha256=iZRvH-iMBYCszGMcoMQhut5OQDrGo07ElfF7jtX_mdM,13055 +pygame/docs/generated/_static/basic.css,sha256=sAj59T-GAN18hejRlkVoHGWW1U4oam_yVWMgFt5P4xc,15597 +pygame/docs/generated/_static/doctools.js,sha256=tcrUIItYleYYKj1roqKMOLpMPtfd_0Y1g5qkMO7llhQ,10766 +pygame/docs/generated/_static/documentation_options.js,sha256=7YP9mP_0AQlotXr2BHmLPANT3kMK4mLSdQOiHEewdoM,435 +pygame/docs/generated/_static/file.png,sha256=XEvJoWrr84xLlQ9ZuOUByjZJUyjLnrYiIYvOkGSjXj4,286 +pygame/docs/generated/_static/jquery-3.5.1.js,sha256=QWo7LDvxbWT2tbbQ97B53yJnYU3WhH_C8ycbRAkjPDc,287630 +pygame/docs/generated/_static/jquery.js,sha256=9_aliU8dGd2tb6OSsuzixeV4y_faTqgFtohetphbbj0,89476 +pygame/docs/generated/_static/language_data.js,sha256=JUzCtS3qbjtQkX7mhfWeiEGT3a8lHfhiLzC_G3YxgnU,11151 +pygame/docs/generated/_static/legacy_logos.zip,sha256=69C68jO62qau7eQx3z6U9pLnDhAMXK72RZ4PatlSMdY,51315 +pygame/docs/generated/_static/minus.png,sha256=R-f8UNs2mfHKQc6aL_ogLADF0dUYDFX2K6hZsb1swAg,90 +pygame/docs/generated/_static/plus.png,sha256=VBFRmblqEwy6AhR8R8DetD3Mm58ItRYruoZCs0mArGM,90 +pygame/docs/generated/_static/pygame.css,sha256=RTUqrXsr09Rdj6xtpM2Y_RZK90S1zXOpOAo2TV-0Gho,12699 +pygame/docs/generated/_static/pygame.ico,sha256=YeIWletq938Rg_G11m_1iGL_zw9T_U1QXZhjnbHjJSU,1078 +pygame/docs/generated/_static/pygame_lofi.png,sha256=QBECBUalJHRExXhf7nt-QpsVcu9LQ3RSAYVVS04AX3M,134242 +pygame/docs/generated/_static/pygame_lofi.svg,sha256=5eTaA6Alehzg0SuUSEFa7uONJe-0WHthEXNKPj75j5o,59281 +pygame/docs/generated/_static/pygame_logo.png,sha256=Jc1Lz47pY3mHjEFNzQXV9gx-olUUYCGuFYrutncaTXE,132068 +pygame/docs/generated/_static/pygame_logo.svg,sha256=3Oxb-IF0TZVcfuYPz-bHbd-OYLNLkTVuxiY1vWAmHUk,59262 +pygame/docs/generated/_static/pygame_powered.png,sha256=LgbswFcg647alSC5SawDPH96xuTMcWmMqVA6Zvs2K3Q,179911 +pygame/docs/generated/_static/pygame_powered.svg,sha256=1_0Cxa_PMRGgGvd2KktrKYSajaxGH_3FKaNWIpxeUbU,102819 +pygame/docs/generated/_static/pygame_powered_lowres.png,sha256=9go5WMiAE4fTEmB9w-zatEfROGl1IUWJGBpKoLqy-wU,179911 +pygame/docs/generated/_static/pygame_tiny.png,sha256=BXPk3OkSWdSkqjMkCQ1Dt5WjxZfb4zj4c2ir9U9_Y7Q,15310 +pygame/docs/generated/_static/pygments.css,sha256=DgUJ36vDxEt3r0T7AvUzwp5bippLlpnRon3w9Jh2zbk,5003 +pygame/docs/generated/_static/reset.css,sha256=wqvSs8L_cB2K6bR903cOJkEtfJi0LpNQFl3nC_kZQr4,1083 +pygame/docs/generated/_static/searchtools.js,sha256=1rXuIe3XtGwCnFERMmcZ3OxcX1I2hwSpOy1khcsiQUw,16634 +pygame/docs/generated/_static/tooltip.css,sha256=UkuHG9X2M7DaTSMJX3-mW-4LV8wBtFxfG5k_pr7xrc4,798 +pygame/docs/generated/_static/underscore-1.13.1.js,sha256=zBD3mc0Pa2X5XEASRFSX5bo8ufUZZKlGiUCye96YtIc,68420 +pygame/docs/generated/_static/underscore.js,sha256=IY-xwfxy6a9rhm9DC-Kmf6N2OStNsvTb8ydyZxtq5Vw,19530 +pygame/docs/generated/c_api.html,sha256=Lw1AQDc-3yIjvgQcKLJPAa8VOy-ybWqAaPK_TO4jWNk,7460 +pygame/docs/generated/c_api/base.html,sha256=ZQiHuN7o82l4qmRbSV6oOjjaUg3vWn9Dlbh7h09m2no,32404 +pygame/docs/generated/c_api/bufferproxy.html,sha256=2ArK8yfTJMplk-iyDJkKcNfYz_k4XKKllLaVdMkmRno,11959 +pygame/docs/generated/c_api/color.html,sha256=KlXoi-1JLhSq4sgFQmES-7IVxcwzWbyAJy42w_nI-nk,10410 +pygame/docs/generated/c_api/display.html,sha256=aPZgjsFdizAFxgJdxMVO-t5zU7jisTW_a3CQ4A1EXMY,10887 +pygame/docs/generated/c_api/event.html,sha256=xCXCaqgVBvVMhQJxpHdj2W31V65ixhsVTICxusFxoBQ,11867 +pygame/docs/generated/c_api/freetype.html,sha256=jhYyED9wC4urWPDHb_alRrGdvr3OVZEKvxkrXKFpPoU,11333 +pygame/docs/generated/c_api/mixer.html,sha256=ff1fpp9Kfqzd109XP3U_Kmo0k7qtukXPUdJCWiSS_Nc,15337 +pygame/docs/generated/c_api/rect.html,sha256=0mxwUjUHMU2JeAsjcBY0EN2KCQx1k0rUdOlQUlhMHYs,13827 +pygame/docs/generated/c_api/rwobject.html,sha256=76BaoydVPkxHnIkRvt704gQA7tjsF5yEuHyT6mkwR10,15250 +pygame/docs/generated/c_api/slots.html,sha256=3f_T9j4r-E5NfJraJmdLKN9UuMkjH7khhatJcezxnhA,6776 +pygame/docs/generated/c_api/surface.html,sha256=Xb0f2_9rIV1Zl6IXsq6aWSOCcN4z7oPWuWK1t42B8tk,14867 +pygame/docs/generated/c_api/surflock.html,sha256=YPAd1PmCkujnAtV-gIb-ssaII1oS5MUescXeddgulm4,16769 +pygame/docs/generated/c_api/version.html,sha256=xaKdtBHq7neRkkLGtPO0xLASVrazGDetRwnKjfumC7g,8182 +pygame/docs/generated/filepaths.html,sha256=tng2esapDYC9hUDhUN5lzVecNNFY9r4RA-IlPJmLRXs,6443 +pygame/docs/generated/genindex.html,sha256=DyShsSlYaU3GcJTTjNbnWHq554WNzXskdy4WapN51Xg,124089 +pygame/docs/generated/index.html,sha256=fUMts7EcGaYmDYOZRctSoRf-T5ObfOcKrmRNYj5UCjI,24814 +pygame/docs/generated/logos.html,sha256=xU61j6JXTfN_wujbO0XICNFsIvLdGC8-jOuWP4K21UM,7489 +pygame/docs/generated/py-modindex.html,sha256=KmQRs8Cy_iU01sKPBZz866xFA_oDeSrTBw2tDh30BLE,11506 +pygame/docs/generated/ref/bufferproxy.html,sha256=lRVOJaZFUIXk9aGoxBaF3AKTxBLnJ-0wj9tCFl--ZJM,17724 +pygame/docs/generated/ref/camera.html,sha256=_roq9rpJ7gBdrQW7BAt2fXeAbsScHJaU7fYP1EgYn-E,27345 +pygame/docs/generated/ref/cdrom.html,sha256=VU_VIR8tZGUdm1ecMvlnBSr2LmkCoamG5k58mJNdvY8,33363 +pygame/docs/generated/ref/color.html,sha256=Q4XEuSQwCGjFWYAaQtxDL432PRSEsn2A13o-tJULulk,36746 +pygame/docs/generated/ref/color_list.html,sha256=YfCMiLQSHwUbqvPbT-GSRp4S8FqmnMRwi14GKn0rUwk,177048 +pygame/docs/generated/ref/cursors.html,sha256=gvgKgSiyaK59oEjCPUWzUB1FV-3QmHJuYH6xtmsVSj0,35542 +pygame/docs/generated/ref/display.html,sha256=2Mr6PZvbQvq62HcD1WaDISymbH3NALLu1Bau_YB-25Q,77376 +pygame/docs/generated/ref/draw.html,sha256=5hGggOPVpdx5e6TrQ5mP18lglYBUMJ3o-Rut28DbUmg,83951 +pygame/docs/generated/ref/event.html,sha256=pMKDkbmZlBf2LncB7USBMg4HXJ5gGILildOY9bCnVa0,70290 +pygame/docs/generated/ref/examples.html,sha256=f6eV54NptZfxomYBVtUQ_JSX6aYqP1c5hmSPcKNmvP4,48526 +pygame/docs/generated/ref/fastevent.html,sha256=1cEZkxfAUpuIrN1K46GxvsPH-3UKxr1O4093BYFSjKc,15067 +pygame/docs/generated/ref/font.html,sha256=0hZdZ7d8e3WkKG-hgksBB_IbXPRi14j3kyVOKPWHg7E,49968 +pygame/docs/generated/ref/freetype.html,sha256=TOik0DKcHynRB6NcmFxbm-xw3V73MI8k0v1E4mUezF4,100700 +pygame/docs/generated/ref/gfxdraw.html,sha256=zJcjKFKVAvvQMJIoTBZLsyp327iCxjvd872M_R2MkOA,79775 +pygame/docs/generated/ref/image.html,sha256=NEkAhFkE_-mTkoAEYwklIouEeA6Qs_uQt6DTYxaRC70,40396 +pygame/docs/generated/ref/joystick.html,sha256=B4d53VargBmuFJd4U9WGp6eUlMk4c2S8_Qaxr_3zU2Y,96474 +pygame/docs/generated/ref/key.html,sha256=An3D5u04eBNZhk4QfeigZmre_t2cQS7LUgP4tcPP8vU,42670 +pygame/docs/generated/ref/locals.html,sha256=MI2KfqzXDcAZ7_kSdaNoU3zzCjtDhrqwIg2n_w_UxgM,8586 +pygame/docs/generated/ref/mask.html,sha256=24NSviDE4wevWJVx3DDiT1GAnZ1zSp8vbMoXMlQSbek,80452 +pygame/docs/generated/ref/math.html,sha256=7IFwnaa43qpMY46TUeV7jsEG_W8nHVuZXjUZxgNJmlQ,123821 +pygame/docs/generated/ref/midi.html,sha256=KvMp4wnP6r44dpjDmMpB3OyDpLnBxD5LfjCulwbAGBc,52564 +pygame/docs/generated/ref/mixer.html,sha256=3pQ_i7f6CPOR4Yufn1F99Nt8AcAwldlLv2ib6tGH6Cc,64104 +pygame/docs/generated/ref/mouse.html,sha256=hVw7tO13mcpnKn6HQEvhDuW5SgtrqiIvY-mG8CwrL2Q,27459 +pygame/docs/generated/ref/music.html,sha256=10qOoR4JhawCVvLbmUelkMRTgJR2idTa5H4DwTs8UNw,33604 +pygame/docs/generated/ref/overlay.html,sha256=_qUGykGQyJHPiaZ_siFz0A9W3lPhFGs2kcfMBbqfwD0,10980 +pygame/docs/generated/ref/pixelarray.html,sha256=99izSXe-5NKMAieADRmYOhFom-l9B8Jm33Pu414Pwm0,31443 +pygame/docs/generated/ref/pixelcopy.html,sha256=2PAOs685k9rzfZgmx8KVKCqps8CVfMG3XnJd6WkeeMc,15087 +pygame/docs/generated/ref/pygame.html,sha256=qF-OSBBD77GtKpbfTM9NkSq6ATGf7yTlESiAO32oVuQ,50074 +pygame/docs/generated/ref/rect.html,sha256=DGVfrhhDCKAB4JLs-gIneDnVFr9yeU3vBoUZpibqNzM,70016 +pygame/docs/generated/ref/scrap.html,sha256=E_1LBW1VlStnkiRr-_uC8cyPplvGGWScYSYubwm2gcc,30535 +pygame/docs/generated/ref/sdl2_controller.html,sha256=9QfcQ0CXxPqGM25RCZ74OlZ-gykwefTCOq1TSBV6aWI,36827 +pygame/docs/generated/ref/sdl2_video.html,sha256=j-avD6hIBb4GlBA5mpOvUxvEVdNEj2FUQ7fmf1kq8zY,61947 +pygame/docs/generated/ref/sndarray.html,sha256=Y8lPHYULXhpCFNCYBBallTO8kRf45DUCtOaS_QBKzZY,14688 +pygame/docs/generated/ref/sprite.html,sha256=XVOaLraWS8CB-EWzCV5WTx2X1dyg8sLn8vlSPnrcwe4,96169 +pygame/docs/generated/ref/surface.html,sha256=Sfft_roDCcP-JoDSUWi_2v8YGJPq4tw7LUVuCSnPyhA,89941 +pygame/docs/generated/ref/surfarray.html,sha256=OguY3VVXSvN7yVfmDSI5BkTTlnnyXCylyc6CEU7kS7M,38289 +pygame/docs/generated/ref/tests.html,sha256=bZZSF54FYxLJiBWx5INWruxkYnQrr-bQoNh3x0f8zjo,18545 +pygame/docs/generated/ref/time.html,sha256=dqF0oQnTGiPqHrazsN2jXPdBA-j_UxuIGnTdyluioEA,20013 +pygame/docs/generated/ref/touch.html,sha256=tpnqDNbbZxo42Cr6HousY7ia0oenXlDjiqwvvGxYewE,12966 +pygame/docs/generated/ref/transform.html,sha256=ZjppqbU1t1yH5SOAIlTfkWE7Ml1LglFg2NXeUumvuNA,42911 +pygame/docs/generated/search.html,sha256=8iHkNUKOcyvxUmqijrnSWPfbnXn2S47bFZyON4LEEYE,3238 +pygame/docs/generated/searchindex.js,sha256=7Su5v3D7gWib5W9fAJ2iqwmYws0fy3-iOA5Hlt-u7Hw,205268 +pygame/docs/generated/tut/CameraIntro.html,sha256=gjsPbd6i8r3CuB39NSS3ebKfc81q8zKx7EcFYai5_zI,38418 +pygame/docs/generated/tut/ChimpLineByLine.html,sha256=TXzXpdckuDrvUtQpt2vxHMQnAU8uWx7FiTxp_CyH2bY,58902 +pygame/docs/generated/tut/DisplayModes.html,sha256=NCz7oHKK5-D-8gxm_7lFHLh73WDykwwahfA2n1ZbRWs,22284 +pygame/docs/generated/tut/ImportInit.html,sha256=PV-Q6A40ebgIkgJnrQDC1rMYMw_Yi9JszEyY4FtK8T0,9719 +pygame/docs/generated/tut/MakeGames.html,sha256=ejBUx-g7g_2nrZ5adF3sDEwS2828JT_EhmJlCwJByh0,14897 +pygame/docs/generated/tut/MoveIt.html,sha256=dupaxzbHu-Mr-ZCm5LOzoJ-gT9mmynkyWaU-EGOYZHw,67410 +pygame/docs/generated/tut/PygameIntro.html,sha256=F3P59SRoi_zi0hMMm8DBLUtMTWQkI9pOMvXDwx0fA1o,29318 +pygame/docs/generated/tut/SpriteIntro.html,sha256=B05DgGzG691HnGf8xXQaBvZ0UZKHYIdtFKIn9Pb53rs,44403 +pygame/docs/generated/tut/SurfarrayIntro.html,sha256=U3W-sVjy9qgH9kTjzwXaGBlc-Q9x6avaZvN0Yr8Hn8A,51181 +pygame/docs/generated/tut/chimp.py.html,sha256=gDYuo9jwQHNo8PrdnsFYA3kAqwjh3x52D9mO-UvCNQE,36445 +pygame/docs/generated/tut/newbieguide.html,sha256=kon8-c8fmp9ds3s6M8DDJGEBwN2jMDroWfxWwOj1OFI,45961 +pygame/docs/generated/tut/tom_games2.html,sha256=XdeJpGj0BzUDozpS2S3mGIRfmm_yeivX-mxU9LWW-Qk,17751 +pygame/docs/generated/tut/tom_games3.html,sha256=9aNjVFxrhR79nTOaZCem5hWcolnooGmctUlOuuQqP2I,15361 +pygame/docs/generated/tut/tom_games4.html,sha256=KHIl__Sz-fhLwXeP5pvDXokaDgQSCWnDx40ThLHEbXY,19258 +pygame/docs/generated/tut/tom_games5.html,sha256=K1-_6ASM8ngUfHyFZJEvsqzUQeNltibs4hX_-c_LmX8,21519 +pygame/docs/generated/tut/tom_games6.html,sha256=yFDau-HVpkYB3jpbnP_zA5e8_3CnC_fBtWRDzHKo31I,53288 +pygame/draw.cp311-win_amd64.pyd,sha256=b2PRY0K5X-GncnWxpI3Q5XPkwEFAit0d1ptiF7eK1wI,49152 +pygame/draw.pyi,sha256=VtcYPysR7rBEPcB8XZrTmkvgvBZDoLPKI6mwhFIWubM,1694 +pygame/draw_py.py,sha256=ME-6PCJ7TRsIYoExnwgvEwj3iGTFPgjkNepQag8Nh1g,18662 +pygame/event.cp311-win_amd64.pyd,sha256=Sf0rb8JUN4p2YRt75VcoHtD_h5_9W82JV474Q6EBpaw,44032 +pygame/event.pyi,sha256=4v_0k2oj2RLibGr_hg5585mvi9CHaAB1tN7wN286Db0,1488 +pygame/examples/README.rst,sha256=K1xE9Fz9XWB05ZKpu4b96ycF19_DY5a3Y-Plv-oeqww,4174 +pygame/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pygame/examples/__pycache__/__init__.cpython-311.pyc,, +pygame/examples/__pycache__/aacircle.cpython-311.pyc,, +pygame/examples/__pycache__/aliens.cpython-311.pyc,, +pygame/examples/__pycache__/arraydemo.cpython-311.pyc,, +pygame/examples/__pycache__/audiocapture.cpython-311.pyc,, +pygame/examples/__pycache__/blend_fill.cpython-311.pyc,, +pygame/examples/__pycache__/blit_blends.cpython-311.pyc,, +pygame/examples/__pycache__/camera.cpython-311.pyc,, +pygame/examples/__pycache__/chimp.cpython-311.pyc,, +pygame/examples/__pycache__/cursors.cpython-311.pyc,, +pygame/examples/__pycache__/dropevent.cpython-311.pyc,, +pygame/examples/__pycache__/eventlist.cpython-311.pyc,, +pygame/examples/__pycache__/font_viewer.cpython-311.pyc,, +pygame/examples/__pycache__/fonty.cpython-311.pyc,, +pygame/examples/__pycache__/freetype_misc.cpython-311.pyc,, +pygame/examples/__pycache__/glcube.cpython-311.pyc,, +pygame/examples/__pycache__/go_over_there.cpython-311.pyc,, +pygame/examples/__pycache__/grid.cpython-311.pyc,, +pygame/examples/__pycache__/headless_no_windows_needed.cpython-311.pyc,, +pygame/examples/__pycache__/joystick.cpython-311.pyc,, +pygame/examples/__pycache__/liquid.cpython-311.pyc,, +pygame/examples/__pycache__/mask.cpython-311.pyc,, +pygame/examples/__pycache__/midi.cpython-311.pyc,, +pygame/examples/__pycache__/moveit.cpython-311.pyc,, +pygame/examples/__pycache__/music_drop_fade.cpython-311.pyc,, +pygame/examples/__pycache__/pixelarray.cpython-311.pyc,, +pygame/examples/__pycache__/playmus.cpython-311.pyc,, +pygame/examples/__pycache__/resizing_new.cpython-311.pyc,, +pygame/examples/__pycache__/scaletest.cpython-311.pyc,, +pygame/examples/__pycache__/scrap_clipboard.cpython-311.pyc,, +pygame/examples/__pycache__/scroll.cpython-311.pyc,, +pygame/examples/__pycache__/setmodescale.cpython-311.pyc,, +pygame/examples/__pycache__/sound.cpython-311.pyc,, +pygame/examples/__pycache__/sound_array_demos.cpython-311.pyc,, +pygame/examples/__pycache__/sprite_texture.cpython-311.pyc,, +pygame/examples/__pycache__/stars.cpython-311.pyc,, +pygame/examples/__pycache__/testsprite.cpython-311.pyc,, +pygame/examples/__pycache__/textinput.cpython-311.pyc,, +pygame/examples/__pycache__/vgrade.cpython-311.pyc,, +pygame/examples/__pycache__/video.cpython-311.pyc,, +pygame/examples/aacircle.py,sha256=cKBEPuz4nVNgiyxf9SFI4c4P8jFmvJMHh_rlKDahm9I,1037 +pygame/examples/aliens.py,sha256=CbZE5cSiR0GZaoUDXUOnao_0fLsMloxaFUJqaI9J6lU,12225 +pygame/examples/arraydemo.py,sha256=G4ZYl8JqPNnbEJl6Ed7UCBfJvrz9QtHauFoFus87WB0,3255 +pygame/examples/audiocapture.py,sha256=hkq7mMKSmSvfHHD5UiIER4928il2l0mqdahkJViHHr8,1561 +pygame/examples/blend_fill.py,sha256=CCQJraeaBLEea-2_lWaUxVlcjM5rxy5UykRIjWCUAu0,3399 +pygame/examples/blit_blends.py,sha256=han3N9jK8dy57EePLJNJ73MEjfl34ypahoY7oGwZwt0,6318 +pygame/examples/camera.py,sha256=5205gt_W9EoTartrtQtXrdD-AkCLx6r8DkBaAvdiFO4,3905 +pygame/examples/chimp.py,sha256=2uBvczKQW8XEG87yJlf4CB46RR4b1a5iPjMi_C--fAY,5912 +pygame/examples/cursors.py,sha256=I4CUYQQjZgtI-cs3Y3Wa-XeW-muolENya5ZlAm4EttM,7938 +pygame/examples/data/BGR.png,sha256=DvOrlW5BJdat94nNV8XEETBLRrSWRV7byQsMPsA69uw,244 +pygame/examples/data/alien1.gif,sha256=8Wveo1zpLVaFCtYITm_SoYqjy8L-TDuaZOcNa8Osqsw,3826 +pygame/examples/data/alien1.jpg,sha256=HOjXjmW4Ofsu_en9WNrkuIp_DCwupXcFB0Yt_cqV9rA,3103 +pygame/examples/data/alien1.png,sha256=femzLssV7oGvT3S2tyviyq7qO32QfhBDtMOR3ENBCLs,3522 +pygame/examples/data/alien2.gif,sha256=0MPpVYzvjAECy0pd7YRFKCEzzIYDKEJt70rbjlLbTZM,3834 +pygame/examples/data/alien2.png,sha256=FKGYDI2FBBR1Z56BLn357PNfh3-M38gAJpSQL8BpKYY,3526 +pygame/examples/data/alien3.gif,sha256=bFCRGZOQPaadCKIc-tlqoUjHdsi5IzR0E-2SjpPEvmA,3829 +pygame/examples/data/alien3.png,sha256=a51Tb9E4IvoICGzQChHq51RKVQJLf1GOCEeqA5yYfnk,3518 +pygame/examples/data/arraydemo.bmp,sha256=xM4-n_hRCQFZlfwwdTK6eaBweycUc863TgSFbWp3dbA,76854 +pygame/examples/data/asprite.bmp,sha256=97XMpKq9lLpMuv8UveCf8UJEAxheBhPUjHfMRQBkUx4,578 +pygame/examples/data/background.gif,sha256=-3kZwt99MFUBbBo-kHvPZXVlFrSB34XVNQWWxfHb970,9133 +pygame/examples/data/black.ppm,sha256=Yu8BwDOeFwOnVYjdWTMo7Tl1xcx2a7J38zZP-JllcMQ,6203 +pygame/examples/data/blue.gif,sha256=hqbgDzCeUz0NHjAQHYURIxSOpRbpHf6QeFch8ux_dAE,84 +pygame/examples/data/blue.mpg,sha256=XDj1CRPt1MWxspCfA3oqb822nlZgQ7CyyEuVJwlgmpg,6144 +pygame/examples/data/bomb.gif,sha256=TZ60QP1S2QBN6QPNSqBwS5VyebZA93iu8ZMUXzEg2QA,1170 +pygame/examples/data/boom.wav,sha256=kfoWs0VVDGHv0JSa46nXZBGyw70-jpfPq_B31qNA_F8,12562 +pygame/examples/data/brick.png,sha256=K_mshK0aL81nzOjAorTXyPps6n9mvofLeOWFXFpVjYA,170 +pygame/examples/data/car_door.wav,sha256=TwYWVqme5NqVVID1N4es92RSKEdTYkxbNx6dNamK-_4,3910 +pygame/examples/data/chimp.png,sha256=gFY5lDOflZ5fCMXpL9_HmipP4-3ALn_r6cCB9yTZKBk,826 +pygame/examples/data/city.png,sha256=c0Nu2o7x7QmvGMDmDCaPnhvJ8tPNuguKKpI_Z-NfQ40,143 +pygame/examples/data/crimson.pnm,sha256=o9ziiY4ox_cCmEo07w08SQckCQTRttxtLgKBE0VmZY8,3124 +pygame/examples/data/cursor.png,sha256=3RDqIuKTXH8Bs67n_ZwEbuS09dQtJeKRgSMR6D9gtWQ,2708 +pygame/examples/data/danger.gif,sha256=m0CBKalFbkqlohgOmrwkwVOfqBhRWonb7xm1pzbDy2Q,2761 +pygame/examples/data/explosion1.gif,sha256=WYcdwbZqmYdaaaPYFiR5vka0Anp4F4nnNlpSSx_1xug,6513 +pygame/examples/data/fist.png,sha256=X0VOsy6fP0UGqBjy7baoBX8XAXyp_1_s2tOItbtA7EI,86196 +pygame/examples/data/green.pcx,sha256=si9WT7dyn3nsXoh34UBW0yOCKWbC-Rz0fKkc_7TDRbY,320 +pygame/examples/data/grey.pgm,sha256=uWTtnBH-Fv605OtEJzS9fG5ns9XaeUHq2YeAC_cdkKU,4155 +pygame/examples/data/house_lo.mp3,sha256=R0nZUXymMp_XLPU8S1yvsiVeWT6MKLt5Rjp-WSnVrLQ,116320 +pygame/examples/data/house_lo.ogg,sha256=64FiQ1Zjq-cOj6Bmya_v3ZjEWmBaGZlTl19udKaz6sU,31334 +pygame/examples/data/house_lo.wav,sha256=B1BwfFaPIsSxaash-igVI_YE9SQd1BCXRTnSAKsNunY,78464 +pygame/examples/data/laplacian.png,sha256=uWI8dPstqMEPVuFPGtm-guu48T2-L3kn99rWA3ZhZ-Q,253 +pygame/examples/data/liquid.bmp,sha256=qtzPXhq0dr2ORNCCZ6gY2loT2Tsu0Dx5YvXB548I1Xg,11734 +pygame/examples/data/midikeys.png,sha256=9HCCmMHvlubR6G9a0jMv1C-AKeBzYfb5jjNhol2Mdqw,19666 +pygame/examples/data/player1.gif,sha256=3ZTVWGxnedKqtf3R-X1omPC0Y8jUSPGgHBAzeGhnV4c,3470 +pygame/examples/data/punch.wav,sha256=A0F1xT8aIZ6aNI_5McMqLygb1EfmdIzPi4kWkU4EwQc,4176 +pygame/examples/data/purple.xpm,sha256=3r6_3v6tob2qy-1hrQ3ujYHpuFb9UQ7LuNsHWq9mj5A,1249 +pygame/examples/data/red.jpg,sha256=mgaTBGP_k55FcqJIL7eV4jYll80zaZHPHfFtXAOLnF8,1251 +pygame/examples/data/sans.ttf,sha256=nrZ6FRet4dwlvA7xOReYCP2QwyGebk0iVJaSFbtpOhM,133088 +pygame/examples/data/scarlet.webp,sha256=iLN1RrY8LCSUnDrwYvWC99v_pLGy0iN8winH7VAyVL0,82 +pygame/examples/data/secosmic_lo.wav,sha256=-EIFkzj7k5qEqG04n7mnUGUp1SsyCJ4n08TzPT600DY,18700 +pygame/examples/data/shot.gif,sha256=bF2eY629zQzvDu83AKpveSFhJq5G4QpOE98A0tvbPFI,129 +pygame/examples/data/static.png,sha256=Xe4wN80awt7nTNiLemoSNTEKlAbGFW7djNETP8IleNs,1202 +pygame/examples/data/teal.svg,sha256=nkksR3fo0NPwC9sVXQPrPR_QrvqRiUB1vC4I-K83dho,313 +pygame/examples/data/turquoise.tif,sha256=4OkIy6CDPMv77tRR_wA9ZHA6qZzG3pjZ-1m1mNB7bcI,1186 +pygame/examples/data/whiff.wav,sha256=FMWM3XnYtce6mHFXQCYPgzT-xu-Q4DJybZfpPjG8cpE,5850 +pygame/examples/data/yellow.tga,sha256=EhxUG3SMO6bbHxr4yFggnKrsC1mYZVq-L6znAsR3z8I,3116 +pygame/examples/dropevent.py,sha256=WMvQbhrHNuSsEyv2hcFr4K4Q83MbZCKtsFNpfuH_SDU,2187 +pygame/examples/eventlist.py,sha256=aPTb0B3DAGnuG76Bz6l8aj6-r3VQ314o9LAiJEl2VHM,5912 +pygame/examples/font_viewer.py,sha256=51i0Y_0c06MRpVBLZymuP3aMEpf7SKVcJ1eKSI3n5Lo,9817 +pygame/examples/fonty.py,sha256=qiYuIracT_jwH5HFx9-tLcwc8qlgALwmL93aZjcUQrU,2073 +pygame/examples/freetype_misc.py,sha256=Fd3USUExyXsmQWWO9I8f2TMLXDooCAebYx10Pnnmbxk,3659 +pygame/examples/glcube.py,sha256=UONh_9RvLhCG8qLQUys4sE2xXY8ukf2zs0KXr_IRE5k,16860 +pygame/examples/go_over_there.py,sha256=D9F75dsbw0nKJf1aA_mWKLkfLpRuhiE5YtULy71jEoM,2139 +pygame/examples/grid.py,sha256=9dEYCiBjkNxjvk5aMukZxyGtaSVUIWXR4MCN56JEsYY,1745 +pygame/examples/headless_no_windows_needed.py,sha256=Lf-FVBNEHON53nSPLFR5DXHVua5S5N_4LX3gd_yRf_o,1299 +pygame/examples/joystick.py,sha256=pIw3_JRd9R0bpu9LPMiMuN53LVZliWehR3MJ5SFcmzo,5252 +pygame/examples/liquid.py,sha256=mGAniBgkpFYhNyM4XMoFRtlwCbQtd5aZaWCI6C23Bak,2544 +pygame/examples/mask.py,sha256=henO1A-xYDZrB6yqYVOnXkVr-8JeZbNXDODhGmczUgU,5725 +pygame/examples/midi.py,sha256=iQh1tPyyi7zejua6VJa--77aPW-i8Bb8TokRtfaHLRo,31267 +pygame/examples/moveit.py,sha256=cLYWPTnqRVud-Lbq9bVd2S1W8KA8eSExjXmFof35wdI,3330 +pygame/examples/music_drop_fade.py,sha256=bzhtDpDkjyod6Jb-rLU6KAh3KAiHQLFP1XVx717fPt0,9110 +pygame/examples/pixelarray.py,sha256=UQzu0tO7g8EOMoQPVLoFRUvMv1ePd9ybkGUg1PKYoXA,3453 +pygame/examples/playmus.py,sha256=xPhC5wCIyEOjiJkz-ZNtRUl9kladd6m0ZU_jl6QSEko,5215 +pygame/examples/resizing_new.py,sha256=8p6Sy8s74A49OXeboxmWnKGQJVO99ATE4nm7A_ADTxY,1046 +pygame/examples/scaletest.py,sha256=Iq8w-K4pf92-2oee7caNcW9KaOIosArD4AL6roR-svY,4826 +pygame/examples/scrap_clipboard.py,sha256=L2tOzkBSxV3f3hva8XiUibMmYW3oxEMlVUmnX97dXlI,3033 +pygame/examples/scroll.py,sha256=2mKc79QAHLWMO8mG50jmuWTXAvZXdtVsqwjKVFA18RY,6642 +pygame/examples/setmodescale.py,sha256=6GCUOLrGp7KITI1qoGj1hJqNxkouUqmdfYgXlD7IYns,1801 +pygame/examples/sound.py,sha256=CwQ3hSKjD_sHmXEBLfimdbt18cQcXwk5QQ7jfnOuSS0,1172 +pygame/examples/sound_array_demos.py,sha256=Pp6ZsH2WRn_T2z5JNSdpEwILbTh2t_60F5tvNL7zt_s,5756 +pygame/examples/sprite_texture.py,sha256=XCbP01L_tVSylh67kvnPfbU7jLVGXfCwLwZ-HnPa1rU,2667 +pygame/examples/stars.py,sha256=fB6OkpKUYh2WtI04nM32_8A5QDrrw8Xlg_ZCTmVfl6w,2714 +pygame/examples/testsprite.py,sha256=vQYCsn8Blzc5UpULlaAKbDsPTkP3Cgo0lJQ0nNhEbpg,6845 +pygame/examples/textinput.py,sha256=vf887f8nAInO66p4qwZbJu316I2bWmUkmZHi50hu9TQ,7579 +pygame/examples/vgrade.py,sha256=RZDRrsAi0bTg6SAh8UJqRQx8viMwjmLSyK20dCql7us,3263 +pygame/examples/video.py,sha256=v_81PS92Mxad1KelrHOAOGwLsIjE46GIM7HwUj9oSPo,4357 +pygame/fastevent.py,sha256=NOVGX3eAvQGCSHDOZZJ_VuWSyekqdPO1Wrr2MvCEQeU,1694 +pygame/fastevent.pyi,sha256=3FmWstDsaRS5TOqExgceS8jeZnkkYhSrYIgTEqSKkU0,249 +pygame/font.cp311-win_amd64.pyd,sha256=vVauI09qkGDZelQ9LXXfm6Ki1n5_jE5EAn4UcM5P_aQ,24576 +pygame/font.pyi,sha256=18UBtw7fOoyRsYvPtm2Pr_H7nKw061niIO6xG2H5Tb8,2073 +pygame/freesansbold.ttf,sha256=v5JRJp8R5LNVgqmTdglt7uPQxJc6RZy9l7C-vAH0QK0,98600 +pygame/freetype.dll,sha256=HEhzktbQaXC6PHtScFiB8fsGn2ByQ0mSdsLwwDPH328,654336 +pygame/freetype.py,sha256=OyLcjKlZXyhQEV2EZ0xMY57HypqGA77K14CJGMxw5H8,2224 +pygame/freetype.pyi,sha256=JdvIk4o8UhhVo2AQnqnJhCmKuv3YRCfWtnSHnkeZZPg,3506 +pygame/ftfont.py,sha256=IwVFbugqIjksMflM4V1jtMPcQO5D_rButgCAJhkxlrw,6134 +pygame/gfxdraw.cp311-win_amd64.pyd,sha256=0jOYA3XAF5aK1VXxZ5mevON-znF0XlwF7MrE3vyr7YE,58880 +pygame/gfxdraw.pyi,sha256=GbHQUuqOdJAxe6tQfp1EfhL3-P6vBltbayaMBeFjsVY,2500 +pygame/image.cp311-win_amd64.pyd,sha256=Sq1ZP1SjlTh06j9VqFJQmh_3_awh7-PVxfhAuf1YnV4,29696 +pygame/image.pyi,sha256=5bCTCOTJ5azmeIV82settKwR4aKfOA_kB_1mSiBTqSs,1705 +pygame/imageext.cp311-win_amd64.pyd,sha256=Jsr-cKTzD3ia9GX0OwGKcYZG_R7IH17_wtVuyRr-LzQ,17408 +pygame/joystick.cp311-win_amd64.pyd,sha256=8nrsWZPEaNfetMxm5X1Fut7bdbZX6Y-qOP40OZYaPdw,20480 +pygame/joystick.pyi,sha256=Lr0DiEuJjbuyGEc1fCPkzVMYPu_qPGUky_x-k5KikPQ,1348 +pygame/key.cp311-win_amd64.pyd,sha256=4IQGcXbrNhGBlhl2Pg3wbG5qWTxlS9th7PEsZNF8O8k,20992 +pygame/key.pyi,sha256=7Ei4SZ3tuWMb9XAtRZm2lw9Sd5BDwjzM3QS1-WRrcBY,562 +pygame/libjpeg-9.dll,sha256=OiJK9UDJZXSAD16az2SyzfuQYOcnkZ7BT70Yeptb_mk,244224 +pygame/libmodplug-1.dll,sha256=DBqQMoEuxMIAA6mXQj5ntx7LXlnWLNwYpb9ZEXapAQ4,265216 +pygame/libogg-0.dll,sha256=V6vE9qmszdCL-aKwIqZmQMxialvU2sbHxPBqXfYe4f4,25600 +pygame/libopus-0.dll,sha256=dxyueUEPf8xPmToQWhjE7Z6Mvd1vgHpCIo2V9XWAiAY,368128 +pygame/libopusfile-0.dll,sha256=zKrKgYEL0tHKtGkrQlOmOfjVUWmW2w4k2IHv0-_cxqQ,46592 +pygame/libpng16-16.dll,sha256=5oi0pNGPS2zMmcbKSYD1EhjLglYQd1GS2bYLLwXv8tU,210944 +pygame/libtiff-5.dll,sha256=6_6XrF7ya5SUWvPbX_0RCkuOktwCVZv4HMsz8NXrzpU,432640 +pygame/libwebp-7.dll,sha256=mlNWO2BY9w8nJQKbfdL-lvhpwg6AkAMc0wPplN_ge1A,447488 +pygame/locals.py,sha256=IO2_D-d3Z5FStP6QDjiyN_W0QPq0i_hHuCQWhxdFRSs,1195 +pygame/locals.pyi,sha256=fLWVStrayTJQMr7r5z45ywHMIOgEYIqLYJkyjIcUsGg,9925 +pygame/macosx.py,sha256=GJYmXKulp9UCu5NgYMtHIzp22ij-QZBhNOLbrJdgtsM,329 +pygame/mask.cp311-win_amd64.pyd,sha256=rnhDcVeEnYmDgNAFrzwTlXFLH721_1utjq778oWvlvA,55296 +pygame/mask.pyi,sha256=bBxSevY0teCHn86xPnQIgx10xLd5HX2O3Ohkfrz_m5E,2304 +pygame/math.cp311-win_amd64.pyd,sha256=JwrXC4MkDAKaofeZabSGJ2F5YJMxaCRMCHhlinokbfs,76288 +pygame/math.pyi,sha256=crW5wZF1MKl2sERrePqGW1roEXACWAbyreqbi2n9zZs,11685 +pygame/midi.py,sha256=GEmQFXnCclms235CT5ULsMs6fIbnrIpwo57J-Kbla_c,24341 +pygame/midi.pyi,sha256=LlPPnnXyHdklV5OTipShPxi8WUcc-NyasfLYxz-fcGw,1774 +pygame/mixer.cp311-win_amd64.pyd,sha256=s8k7Njhzh-PNmMVE8E4TojX6tFMQEF0sNAx4GT3C_Ho,37888 +pygame/mixer.pyi,sha256=HuPeysk7q79eWlPe2r54Z-j1Kgx1JKTQZnvXCESibjA,2856 +pygame/mixer_music.cp311-win_amd64.pyd,sha256=kgi3kFJALTCpsomJVDycZwCoy6oDy4GHl4PZ09QApMQ,20480 +pygame/mixer_music.pyi,sha256=gMklc23g-bBIAGa3giyM00yi2jLedrKipx9_8lebvFY,691 +pygame/mouse.cp311-win_amd64.pyd,sha256=aGV9p_osMmsaVXao0XRBXXZ1uKSdWOgHLtrx_DKIl28,19456 +pygame/mouse.pyi,sha256=a9Y_UtpHhMVQqpE3LnkAaI3EoVjQMdVNJNVeZ-eXTXE,1184 +pygame/newbuffer.cp311-win_amd64.pyd,sha256=5Vm0RkyUw8oakdL239DozrHxae7sAjahb-ggwwVgLSI,22016 +pygame/pixelarray.cp311-win_amd64.pyd,sha256=56ctK6mqzeezpoXA7wUGcCUFNYA-yiIT3uHpY6zWdBo,47616 +pygame/pixelarray.pyi,sha256=8bDvIdo9QPBfgSSU04kxZaYCkkdv_XyXRQtA28YI6P0,1226 +pygame/pixelcopy.cp311-win_amd64.pyd,sha256=CH8hKD9gSlHVjGkXBErPMxa1AmeF6czWxTnt_nvyOs0,26624 +pygame/pixelcopy.pyi,sha256=9P6c4l_16qAfJYsQ3IkJGPMKyp_weLB1e9iQIvLYDZM,534 +pygame/pkgdata.py,sha256=RTPgP8uEaPK9GdyeOSUzdN4N9LLdrUASHUXMoUersFY,2421 +pygame/portmidi.dll,sha256=yfjZBDrBVwsQ8QTy0ArseR9WJhyE7kB3O-c9Cjgi4BM,41984 +pygame/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pygame/pygame.ico,sha256=PBF9cw0Ca9Rw2pNmDD3iXvcYYQeI9ZzZ9vxtRLQRoJc,145516 +pygame/pygame_icon.bmp,sha256=Twnby8nv4HMhGka49n-47CPseDvwrSLZ0l1o9U2Bb5s,630 +pygame/pygame_icon.icns,sha256=4jwAo9VtMhTK9hq9ibt6MZ4_sd2VsZueWZ3YAMuTPgw,264148 +pygame/pygame_icon_mac.bmp,sha256=QrAs3kEF6v7wVMlIJgktI90aUdTg9RdTmp16d2HZhNg,262282 +pygame/pypm.cp311-win_amd64.pyd,sha256=me_uwg3QTW1udBkuPYbZ-VgExD4kJihtMMrkjw8M7uw,102912 +pygame/rect.cp311-win_amd64.pyd,sha256=7QH11MQNzHLsBw4SJ1Gp1bOgC4jQCRZNiSNnWwvk4WU,45568 +pygame/rect.pyi,sha256=gOICRGGXcziZKQvJrWVJ34H3wNl6-3Qp9uyWicbLHko,7138 +pygame/rwobject.cp311-win_amd64.pyd,sha256=acT_BtJREGgrWXpCzEEigHnQaqBRPvu3uJ4Kyhzm3X0,19456 +pygame/rwobject.pyi,sha256=7pjMcSV8digIazj9i0h5se7steQNUtBBBcK-f2sY-Yo,544 +pygame/scrap.cp311-win_amd64.pyd,sha256=4yhSdx8vhwEKvtXsq3onKKycHWJlcAox0g7j0AavOp8,18944 +pygame/scrap.pyi,sha256=xEbe7P1WNSmqeFQ-M3XCX3JztjHu1-m_0x8vkL6S4bA,366 +pygame/sndarray.py,sha256=hJMTnQBEbOBYzeBRdCiGHCr3S20NqpHVevX2WFxw51I,4083 +pygame/sndarray.pyi,sha256=-kUmCShhKSazBRhHberq4kDhQau0I-02LZHvfIVYi7A,337 +pygame/sprite.py,sha256=TPphm-rkggpgY_4hnZU-No1pKJV7Av0cXIL3ba1NZYM,62852 +pygame/sprite.pyi,sha256=mvgzcgxPmkS9JXT4YxjuarZSWI_AJLT1RrguRbJ9aNE,9618 +pygame/surface.cp311-win_amd64.pyd,sha256=ltI9o9sPPeHWr459oPpopzR4nDQ8PRV4GQm4D-ZWObw,239616 +pygame/surface.pyi,sha256=mKyduOgOgekHQ3JObqdjFMkTMI22Aeat2MCAkOTWseE,4726 +pygame/surfarray.py,sha256=v2OEMPerEmDeQDzBz-XIAnpbEYgS-viNlkZ7_zz5OSE,14427 +pygame/surfarray.pyi,sha256=g4w8kePGEZGMGhdtKo0ClZ3G-VPitz7DmrtIK6a7FGY,1287 +pygame/surflock.cp311-win_amd64.pyd,sha256=4vXOrJHI_z85ck1IZImBHntTEJIedxf1TxPudUKHRPA,13824 +pygame/surflock.pyi,sha256=p6HFejTvHw2HxSUOD4vW9HolANRfvGHjADdRh63skKQ,122 +pygame/sysfont.py,sha256=AgGBnxY8kfXTbNYdNxfWDgo6NIA7xzDD5SELRLGaqzc,15446 +pygame/tests/__init__.py,sha256=wfUhz-LZF-OXZNT81UfGdofNYPCUMJoF3nwgXpzg4sE,1251 +pygame/tests/__main__.py,sha256=xLKWh5Kk0hc-2vz_OrNsMkVproZ3ndWz6ZCMY7Myk5A,3788 +pygame/tests/__pycache__/__init__.cpython-311.pyc,, +pygame/tests/__pycache__/__main__.cpython-311.pyc,, +pygame/tests/__pycache__/base_test.cpython-311.pyc,, +pygame/tests/__pycache__/blit_test.cpython-311.pyc,, +pygame/tests/__pycache__/bufferproxy_test.cpython-311.pyc,, +pygame/tests/__pycache__/camera_test.cpython-311.pyc,, +pygame/tests/__pycache__/color_test.cpython-311.pyc,, +pygame/tests/__pycache__/constants_test.cpython-311.pyc,, +pygame/tests/__pycache__/controller_test.cpython-311.pyc,, +pygame/tests/__pycache__/cursors_test.cpython-311.pyc,, +pygame/tests/__pycache__/display_test.cpython-311.pyc,, +pygame/tests/__pycache__/docs_test.cpython-311.pyc,, +pygame/tests/__pycache__/draw_test.cpython-311.pyc,, +pygame/tests/__pycache__/event_test.cpython-311.pyc,, +pygame/tests/__pycache__/font_test.cpython-311.pyc,, +pygame/tests/__pycache__/freetype_tags.cpython-311.pyc,, +pygame/tests/__pycache__/freetype_test.cpython-311.pyc,, +pygame/tests/__pycache__/ftfont_tags.cpython-311.pyc,, +pygame/tests/__pycache__/ftfont_test.cpython-311.pyc,, +pygame/tests/__pycache__/gfxdraw_test.cpython-311.pyc,, +pygame/tests/__pycache__/image__save_gl_surface_test.cpython-311.pyc,, +pygame/tests/__pycache__/image_tags.cpython-311.pyc,, +pygame/tests/__pycache__/image_test.cpython-311.pyc,, +pygame/tests/__pycache__/imageext_tags.cpython-311.pyc,, +pygame/tests/__pycache__/imageext_test.cpython-311.pyc,, +pygame/tests/__pycache__/joystick_test.cpython-311.pyc,, +pygame/tests/__pycache__/key_test.cpython-311.pyc,, +pygame/tests/__pycache__/locals_test.cpython-311.pyc,, +pygame/tests/__pycache__/mask_test.cpython-311.pyc,, +pygame/tests/__pycache__/math_test.cpython-311.pyc,, +pygame/tests/__pycache__/midi_test.cpython-311.pyc,, +pygame/tests/__pycache__/mixer_music_tags.cpython-311.pyc,, +pygame/tests/__pycache__/mixer_music_test.cpython-311.pyc,, +pygame/tests/__pycache__/mixer_tags.cpython-311.pyc,, +pygame/tests/__pycache__/mixer_test.cpython-311.pyc,, +pygame/tests/__pycache__/mouse_test.cpython-311.pyc,, +pygame/tests/__pycache__/pixelarray_test.cpython-311.pyc,, +pygame/tests/__pycache__/pixelcopy_test.cpython-311.pyc,, +pygame/tests/__pycache__/rect_test.cpython-311.pyc,, +pygame/tests/__pycache__/rwobject_test.cpython-311.pyc,, +pygame/tests/__pycache__/scrap_tags.cpython-311.pyc,, +pygame/tests/__pycache__/scrap_test.cpython-311.pyc,, +pygame/tests/__pycache__/sndarray_tags.cpython-311.pyc,, +pygame/tests/__pycache__/sndarray_test.cpython-311.pyc,, +pygame/tests/__pycache__/sprite_test.cpython-311.pyc,, +pygame/tests/__pycache__/surface_test.cpython-311.pyc,, +pygame/tests/__pycache__/surfarray_tags.cpython-311.pyc,, +pygame/tests/__pycache__/surfarray_test.cpython-311.pyc,, +pygame/tests/__pycache__/surflock_test.cpython-311.pyc,, +pygame/tests/__pycache__/sysfont_test.cpython-311.pyc,, +pygame/tests/__pycache__/threads_test.cpython-311.pyc,, +pygame/tests/__pycache__/time_test.cpython-311.pyc,, +pygame/tests/__pycache__/touch_test.cpython-311.pyc,, +pygame/tests/__pycache__/transform_test.cpython-311.pyc,, +pygame/tests/__pycache__/version_test.cpython-311.pyc,, +pygame/tests/__pycache__/video_test.cpython-311.pyc,, +pygame/tests/base_test.py,sha256=d6POJDqd_uxot9yxTxRBW2thiOD4o0HYuIHBq84OBUg,22449 +pygame/tests/blit_test.py,sha256=tfZs2E_xD2LZQoYZWKx3hHcJ80hIDjqn6uBiDt4lN7I,4725 +pygame/tests/bufferproxy_test.py,sha256=bji75h8zDPEHNXSKEnT9Gb4T0_cMcAlDxqvPdlR5WU0,16451 +pygame/tests/camera_test.py,sha256=KdGG1pLTe8Eh2SHiEsH7DuLqwn-OsaLCHm4MGCKPAvg,70 +pygame/tests/color_test.py,sha256=uUzzV-njnROb2XDNMKmR4lUy1NS-NGsTtxadu-pkNZ4,49781 +pygame/tests/constants_test.py,sha256=kOOqPnXMMka37d4VUDo70j3i_zAN74Y5QuRLnNQsRao,9308 +pygame/tests/controller_test.py,sha256=a6NSWn1k1Rpt4HTYeOAbF-cDx0KfnKsrG6DwA0vk_0k,10834 +pygame/tests/cursors_test.py,sha256=qc-T5sdh25LO-KOK16CMp9FnWCXJSwdUo29YkePeSTs,7700 +pygame/tests/display_test.py,sha256=eYem018lDt73sJdLjk32x-xSlW_FRx1qZyzibHaZwFE,46427 +pygame/tests/docs_test.py,sha256=r2qa_ox8eg2_Y5Pb9-XzZExAhqJrdVMO30guO1o7PPM,1091 +pygame/tests/draw_test.py,sha256=_pwsaLBAVheyU7XhkZ8nUbzq9iPj_f654zXKv7EV8-c,235412 +pygame/tests/event_test.py,sha256=_ti2gVPoPwWofe9W7uY_EaOFZpc_0on8mSQa2sJ3UfE,32837 +pygame/tests/fixtures/fonts/A_PyGameMono-8.png,sha256=QmhReADwKrzW5RWnG1KHEtZIqpVtwWzhXmydX1su10c,92 +pygame/tests/fixtures/fonts/PlayfairDisplaySemibold.ttf,sha256=4Q60pnYY-7dwBTVr7PBSz5r-w_kfUzhal8i85ISG7V4,236636 +pygame/tests/fixtures/fonts/PyGameMono-18-100dpi.bdf,sha256=nm3okxnfAFtADlp7s2AY43zS49NYg9jq7GVzG2lPhOQ,1947 +pygame/tests/fixtures/fonts/PyGameMono-18-75dpi.bdf,sha256=4kB0uYeEpa3W-ZAomFMpc0hD-h6FnOh2m5IPi6xzfds,1648 +pygame/tests/fixtures/fonts/PyGameMono-8.bdf,sha256=aK0KV-_osDPTPiA1BUCgZHOmufy6J9Vh5pf1IAi0_yg,1365 +pygame/tests/fixtures/fonts/PyGameMono.otf,sha256=_Af4LyMEgKKGa8jDlfik89axhLc3HoS8aG5JHWN5sZw,3128 +pygame/tests/fixtures/fonts/test_fixed.otf,sha256=FWHmFsQUobgtbm370Y5XJv1lAokTreGR5fo4tuw3Who,58464 +pygame/tests/fixtures/fonts/test_sans.ttf,sha256=nrZ6FRet4dwlvA7xOReYCP2QwyGebk0iVJaSFbtpOhM,133088 +pygame/tests/fixtures/fonts/u13079_PyGameMono-8.png,sha256=x_D28PW8aKed8ZHBK6AISEZ9vlEV76Whi770ItTuFVU,89 +pygame/tests/fixtures/xbm_cursors/white_sizing.xbm,sha256=VLAS1A417T-Vg6GMsmicUCYpOhvGsrgJJYUvdFYYteY,366 +pygame/tests/fixtures/xbm_cursors/white_sizing_mask.xbm,sha256=CKQeiOtlFoJdAts83UmTEeVk-3pxgJ9Wu2QJaCjzAQM,391 +pygame/tests/font_test.py,sha256=UtIcHo9xuxFxDrzmrTsSTzvP7vzteegqUN5yWWSJVVE,27199 +pygame/tests/freetype_tags.py,sha256=NdjMDSYHfrhopKR0JuTeUfFX-AbcCu4fsXnS1a46iVM,182 +pygame/tests/freetype_test.py,sha256=GFZUMLZF-KjXEyOhq0FqI2Oe_nt1hbkPHTkSW7RN3pk,64896 +pygame/tests/ftfont_tags.py,sha256=IvteBUDEp4rv9q6FwlTpQ9X2px-XUromSMQ921VrhCU,180 +pygame/tests/ftfont_test.py,sha256=YZesw8r_NfqcoUsg78FbAh0kJaeC6iAzixHMsQcH3gY,421 +pygame/tests/gfxdraw_test.py,sha256=XWxHvtvkMWllmlCsGhaUGCdOFBAiOfJgYg_KnUj8Q_E,32354 +pygame/tests/image__save_gl_surface_test.py,sha256=5H8TeGZNRZzu5kJInWPe8AuuKqHv-utunadoBmn--CI,1198 +pygame/tests/image_tags.py,sha256=_WJGXgTOaUn4IG7fIk1sDKfDDZP3W8N6PkrrOpPT-U8,132 +pygame/tests/image_test.py,sha256=zJ6BngnIYbmBpoU5I0yT1Ed7fbBLOqGJW3Uqm81BfEg,42598 +pygame/tests/imageext_tags.py,sha256=-vnXr7O5F1NVrEDrOHBEYdaD-JiuBT9NI-lxGps-K1U,135 +pygame/tests/imageext_test.py,sha256=Dam4nzQG1dZtJ8rtAmSw2xdhIvENATklN81mVl5Mh4I,2852 +pygame/tests/joystick_test.py,sha256=XArf2gXSYYupNOuOekpKtGlJ_vaLWWUSl7hpKnBE3FU,6078 +pygame/tests/key_test.py,sha256=rZ9EPqi8q6VQI7kQzdjW8U0Ebqk2QYnU9D7i2TXsdDY,9172 +pygame/tests/locals_test.py,sha256=2g4vCW-wJG0U_wA7VP1kieV-wSsvGInyGkLW6OjvJ4o,417 +pygame/tests/mask_test.py,sha256=95xNu2gy1TVgxKNnDdcYXgo_rKaTdKPAqeeKn1jI_Xg,245880 +pygame/tests/math_test.py,sha256=n1vDugXES3Snzh0Hy5B4Gx4OOdoFkcsjliWwm-kmm0A,111475 +pygame/tests/midi_test.py,sha256=utmDFvk5wZCcXrTTGXnl4N6dMjmkXgrIV6nGtgcws6k,16905 +pygame/tests/mixer_music_tags.py,sha256=o0gsQDjuICFYw8j3WOlIluwk9fdA42ledU1U6DIJzNU,138 +pygame/tests/mixer_music_test.py,sha256=_awXVWYd9cdqdaHI0q4jzALvoZgfhvwp1ZlXrOJx-fI,15141 +pygame/tests/mixer_tags.py,sha256=qKcn8AD46H3V87xONG0iXlGH_FveeGBgf2gE1MMh2s0,132 +pygame/tests/mixer_test.py,sha256=0tm85emdyhCNGN52GVsv-TJ99aLBMqHM1v42dPLHRPQ,52645 +pygame/tests/mouse_test.py,sha256=7u-tkrioaNXON7kGTpINtpngRFKjVrb0C73hTsA4I5A,13148 +pygame/tests/pixelarray_test.py,sha256=NKVn_4NumiMz0CHUDm7DH-OcEZI0XpAZmjenAXwDJlY,62745 +pygame/tests/pixelcopy_test.py,sha256=7FBHF-v505KOdIzUe9JkIcRnD7k8pmWKFNOQW3PUmWE,25536 +pygame/tests/rect_test.py,sha256=Zc9eroRcI6MELp17efYQKju22DfnSbwfmt-1YVbho-Y,117703 +pygame/tests/run_tests__tests/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/__pycache__/__init__.cpython-311.pyc,, +pygame/tests/run_tests__tests/__pycache__/run_tests__test.cpython-311.pyc,, +pygame/tests/run_tests__tests/all_ok/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/all_ok/__pycache__/__init__.cpython-311.pyc,, +pygame/tests/run_tests__tests/all_ok/__pycache__/fake_2_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/all_ok/__pycache__/fake_3_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/all_ok/__pycache__/fake_4_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/all_ok/__pycache__/fake_5_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/all_ok/__pycache__/fake_6_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/all_ok/__pycache__/no_assertions__ret_code_of_1__test.cpython-311.pyc,, +pygame/tests/run_tests__tests/all_ok/__pycache__/zero_tests_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/all_ok/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/all_ok/fake_3_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/all_ok/fake_4_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/all_ok/fake_5_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/all_ok/fake_6_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/all_ok/no_assertions__ret_code_of_1__test.py,sha256=PNrfACCpcPnO964Oxv2-9l4ciuJ-Iqw3x8HDs-kebVg,797 +pygame/tests/run_tests__tests/all_ok/zero_tests_test.py,sha256=XzLaMjkygsvNkFEqnRU9y2Ijm6bfds9n5Z6mg_LOMJQ,545 +pygame/tests/run_tests__tests/everything/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/everything/__pycache__/__init__.cpython-311.pyc,, +pygame/tests/run_tests__tests/everything/__pycache__/fake_2_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/everything/__pycache__/incomplete_todo_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/everything/__pycache__/magic_tag_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/everything/__pycache__/sleep_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/everything/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/everything/incomplete_todo_test.py,sha256=71myeZtFerYY2rB-j60l5Ltz3FiRCuOR4evFXtJHC34,909 +pygame/tests/run_tests__tests/everything/magic_tag_test.py,sha256=SjIKB_7aLfGdih8cotQ34m1KbSEII_1wGQUBwrWeIyY,859 +pygame/tests/run_tests__tests/everything/sleep_test.py,sha256=AyGwZk5fQAkfeCr9VewdsuD_z5BzlVfkmbZD-XetB50,715 +pygame/tests/run_tests__tests/exclude/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/exclude/__pycache__/__init__.cpython-311.pyc,, +pygame/tests/run_tests__tests/exclude/__pycache__/fake_2_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/exclude/__pycache__/invisible_tag_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/exclude/__pycache__/magic_tag_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/exclude/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/exclude/invisible_tag_test.py,sha256=AdHFvOK-kCRi2iUs68So6Ngef6C_LEdx3QpMLjhKtmM,925 +pygame/tests/run_tests__tests/exclude/magic_tag_test.py,sha256=SjIKB_7aLfGdih8cotQ34m1KbSEII_1wGQUBwrWeIyY,859 +pygame/tests/run_tests__tests/failures1/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/failures1/__pycache__/__init__.cpython-311.pyc,, +pygame/tests/run_tests__tests/failures1/__pycache__/fake_2_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/failures1/__pycache__/fake_3_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/failures1/__pycache__/fake_4_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/failures1/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/failures1/fake_3_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/failures1/fake_4_test.py,sha256=xWpIVUpzevSs4bVeze48Q9jkZzss4szdw6eMOrJnZV8,949 +pygame/tests/run_tests__tests/incomplete/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/incomplete/__pycache__/__init__.cpython-311.pyc,, +pygame/tests/run_tests__tests/incomplete/__pycache__/fake_2_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/incomplete/__pycache__/fake_3_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/incomplete/fake_2_test.py,sha256=RVUuQZxqYScIUAflNIsXd7UE6Rxm6HHFZSi8cpz5m-k,889 +pygame/tests/run_tests__tests/incomplete/fake_3_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/incomplete_todo/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/incomplete_todo/__pycache__/__init__.cpython-311.pyc,, +pygame/tests/run_tests__tests/incomplete_todo/__pycache__/fake_2_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/incomplete_todo/__pycache__/fake_3_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/incomplete_todo/fake_2_test.py,sha256=71myeZtFerYY2rB-j60l5Ltz3FiRCuOR4evFXtJHC34,909 +pygame/tests/run_tests__tests/incomplete_todo/fake_3_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/infinite_loop/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/infinite_loop/__pycache__/__init__.cpython-311.pyc,, +pygame/tests/run_tests__tests/infinite_loop/__pycache__/fake_1_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/infinite_loop/__pycache__/fake_2_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/infinite_loop/fake_1_test.py,sha256=rNt-VaNziz7OmfbDXcbXbDIbwC_6ScFJ-MtenMjR68Y,906 +pygame/tests/run_tests__tests/infinite_loop/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/print_stderr/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/print_stderr/__pycache__/__init__.cpython-311.pyc,, +pygame/tests/run_tests__tests/print_stderr/__pycache__/fake_2_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/print_stderr/__pycache__/fake_3_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/print_stderr/__pycache__/fake_4_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/print_stderr/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/print_stderr/fake_3_test.py,sha256=6AGEff135DU_spRhZ09oDGXE4lZC3dlHU_phnfOyWYY,954 +pygame/tests/run_tests__tests/print_stderr/fake_4_test.py,sha256=xWpIVUpzevSs4bVeze48Q9jkZzss4szdw6eMOrJnZV8,949 +pygame/tests/run_tests__tests/print_stdout/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/print_stdout/__pycache__/__init__.cpython-311.pyc,, +pygame/tests/run_tests__tests/print_stdout/__pycache__/fake_2_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/print_stdout/__pycache__/fake_3_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/print_stdout/__pycache__/fake_4_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/print_stdout/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/print_stdout/fake_3_test.py,sha256=cruYqrh3O3MQ8fczEFloLpsrQrYmMOd6jgxMU6e5H8w,1012 +pygame/tests/run_tests__tests/print_stdout/fake_4_test.py,sha256=xWpIVUpzevSs4bVeze48Q9jkZzss4szdw6eMOrJnZV8,949 +pygame/tests/run_tests__tests/run_tests__test.py,sha256=9mDlobUHX5baqNIJvCo4hN7XGb5qRIRO_DFfNcOno5I,4315 +pygame/tests/run_tests__tests/timeout/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +pygame/tests/run_tests__tests/timeout/__pycache__/__init__.cpython-311.pyc,, +pygame/tests/run_tests__tests/timeout/__pycache__/fake_2_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/timeout/__pycache__/sleep_test.cpython-311.pyc,, +pygame/tests/run_tests__tests/timeout/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899 +pygame/tests/run_tests__tests/timeout/sleep_test.py,sha256=5EDW4U6kYN4QIid0IgHBypJ3T3a78pILILF41DPpujk,716 +pygame/tests/rwobject_test.py,sha256=LAJun6obwHADEiONe6F68WNM9qAuL7i4hqbPErjmox4,4323 +pygame/tests/scrap_tags.py,sha256=zHyLWy2JRyfw0DamlH9dz-MZq2R2uOryjH9JRu-RCkw,671 +pygame/tests/scrap_test.py,sha256=qt47IQLTs3jqf8FEP1mBC8gX6SzbeLsjJnIdszMhbYU,9160 +pygame/tests/sndarray_tags.py,sha256=ThDQxqGFaAembuWgdYGsFSWEppVezgXJ2htYRvvDaXE,190 +pygame/tests/sndarray_test.py,sha256=z0XU6Di2a0G2eHc2FXvqhoeGOCFeM8RoB9l6SJSBZyg,6290 +pygame/tests/sprite_test.py,sha256=08ZqxvMi7Bii4gGFVy2NdKJ3yWKz1an6AyEQs8Qhmqs,47210 +pygame/tests/surface_test.py,sha256=kYDBY8HgtjHyKvrmeS8di77cOxX_9qhdcsDQgKMNZog,165737 +pygame/tests/surfarray_tags.py,sha256=AwlglKM7DrjHvvcSMm-yXb-PSxsVhJkS6VE7Z8wOhes,260 +pygame/tests/surfarray_test.py,sha256=onPau7a-nZYZOuPm4q29PE130SWi8HDAxyRaWzW51DE,25753 +pygame/tests/surflock_test.py,sha256=dMZkzND7-R_z-GaxN4ZIcpNtW3PsK1i7kdnizpU21UY,4728 +pygame/tests/sysfont_test.py,sha256=uf7ISqCvoiegyUvYTqOT1-kYQi_POeejL1TqXXeYF9Q,1463 +pygame/tests/test_utils/__init__.py,sha256=_eQvMYQ_-gFuB1fiftJLjL0RYqV3tl5jRYeBUzI8x-4,4429 +pygame/tests/test_utils/__pycache__/__init__.cpython-311.pyc,, +pygame/tests/test_utils/__pycache__/arrinter.cpython-311.pyc,, +pygame/tests/test_utils/__pycache__/async_sub.cpython-311.pyc,, +pygame/tests/test_utils/__pycache__/buftools.cpython-311.pyc,, +pygame/tests/test_utils/__pycache__/endian.cpython-311.pyc,, +pygame/tests/test_utils/__pycache__/png.cpython-311.pyc,, +pygame/tests/test_utils/__pycache__/run_tests.cpython-311.pyc,, +pygame/tests/test_utils/__pycache__/test_machinery.cpython-311.pyc,, +pygame/tests/test_utils/__pycache__/test_runner.cpython-311.pyc,, +pygame/tests/test_utils/arrinter.py,sha256=xmfJDli6Q3SPBy5cLAF0SqTXlRc5QDZRhbDP50p5pjc,14698 +pygame/tests/test_utils/async_sub.py,sha256=2BqxoeKTo6sPlW2bwGCieEb0lwxZsHUHBnGFjSPny68,9128 +pygame/tests/test_utils/buftools.py,sha256=FMXLkVYStXRZSPkO1dpm0ZtD8P7Da7zYY_k8rprjo_s,23638 +pygame/tests/test_utils/endian.py,sha256=Rc7rl38YamHgi8EzB92Muu8C4XH6yltH9f5On7qfMpY,495 +pygame/tests/test_utils/png.py,sha256=f-s-AzEK5RD2X-Wjan-0m9kyvlqW04-eJ6WwKtxNZuQ,152365 +pygame/tests/test_utils/run_tests.py,sha256=XoPNITexQ3AOu4QT_eOQAnRLLj5ZPXLr-0eDDYFj7fU,12038 +pygame/tests/test_utils/test_machinery.py,sha256=4vsi3mOw_581298rsp4cmcQYpbB6P5PK3Eg0cTBy138,2429 +pygame/tests/test_utils/test_runner.py,sha256=ktlXMmx0LOD9gmeTXyk72Ozo1UkYlUUJGoYoohdMZfY,9328 +pygame/tests/threads_test.py,sha256=YMahjfAW7F381dFv05XvazR3cLYP3Sbby1h57tIqYK0,7840 +pygame/tests/time_test.py,sha256=lQTubG8CiDnTwHOcV0Esvk1QJX90vjbxZNDdorg1wZU,16065 +pygame/tests/touch_test.py,sha256=9e5LDHeZrtQSaUBrb9dLHT1YDuNuccSlYvcYIWYIm7k,3216 +pygame/tests/transform_test.py,sha256=EryZIaG25yunlIW9wS4pnQhtswVNI-IBp9oAJzTe7zY,53328 +pygame/tests/version_test.py,sha256=dvNIneFf1c4PAKa4xo1YLAlYRDFYL8_ZeUwI1FS55Is,1536 +pygame/tests/video_test.py,sha256=USdLAov8GnN_9vsXl_1pe0MI0ddM3KurURDhVQIV3nY,694 +pygame/threads/__init__.py,sha256=Uk3jRFbWy0uglYVYhb9XIgN3g6tT1GCt19YY2wzxC6U,8068 +pygame/threads/__pycache__/__init__.cpython-311.pyc,, +pygame/time.cp311-win_amd64.pyd,sha256=jTgH8QOJdKMbSIPHCrBrDXEGfMnMh-Eu_74U8dYCOuo,18944 +pygame/time.pyi,sha256=0dUmVQjxwyhXkSRGX0DnqM2-0JzOMIVbHx4psLfrlhI,501 +pygame/transform.cp311-win_amd64.pyd,sha256=Jy8ru6qCNbilb9UbUkVEdWTxLr7IblUaDyj9koXBeAk,58368 +pygame/transform.pyi,sha256=EsjIBP706hur5c6aKmUBMbCp9cPQIilWcqkSHkCeUk4,1999 +pygame/version.py,sha256=qL1Df5o1d03r5AkxvQH01nVkugxW64-zk3fKqCPwaKo,2526 +pygame/version.pyi,sha256=NvmU4694WwSRWMiZ5WL3APQYA2s_3xY4foOnPsncNuM,600 +pygame/zlib1.dll,sha256=sfWKF_O_1VUj5772haz1sy0cKm8lq9zUQmgSZv0mqwg,108544 diff --git a/.venv/Lib/site-packages/pygame-2.5.2.dist-info/REQUESTED b/.venv/Lib/site-packages/pygame-2.5.2.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/.venv/Lib/site-packages/pygame-2.5.2.dist-info/WHEEL b/.venv/Lib/site-packages/pygame-2.5.2.dist-info/WHEEL new file mode 100644 index 00000000..6d160455 --- /dev/null +++ b/.venv/Lib/site-packages/pygame-2.5.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.41.2) +Root-Is-Purelib: false +Tag: cp311-cp311-win_amd64 + diff --git a/.venv/Lib/site-packages/pygame-2.5.2.dist-info/entry_points.txt b/.venv/Lib/site-packages/pygame-2.5.2.dist-info/entry_points.txt new file mode 100644 index 00000000..05adb067 --- /dev/null +++ b/.venv/Lib/site-packages/pygame-2.5.2.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[pyinstaller40] +hook-dirs = pygame.__pyinstaller:get_hook_dirs diff --git a/.venv/Lib/site-packages/pygame-2.5.2.dist-info/top_level.txt b/.venv/Lib/site-packages/pygame-2.5.2.dist-info/top_level.txt new file mode 100644 index 00000000..0cb7ff1d --- /dev/null +++ b/.venv/Lib/site-packages/pygame-2.5.2.dist-info/top_level.txt @@ -0,0 +1 @@ +pygame diff --git a/.venv/Lib/site-packages/pygame/SDL2.dll b/.venv/Lib/site-packages/pygame/SDL2.dll new file mode 100644 index 00000000..95107d91 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/SDL2.dll differ diff --git a/.venv/Lib/site-packages/pygame/SDL2_image.dll b/.venv/Lib/site-packages/pygame/SDL2_image.dll new file mode 100644 index 00000000..d7016555 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/SDL2_image.dll differ diff --git a/.venv/Lib/site-packages/pygame/SDL2_mixer.dll b/.venv/Lib/site-packages/pygame/SDL2_mixer.dll new file mode 100644 index 00000000..859ef9fe Binary files /dev/null and b/.venv/Lib/site-packages/pygame/SDL2_mixer.dll differ diff --git a/.venv/Lib/site-packages/pygame/SDL2_ttf.dll b/.venv/Lib/site-packages/pygame/SDL2_ttf.dll new file mode 100644 index 00000000..fe2d4c74 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/SDL2_ttf.dll differ diff --git a/.venv/Lib/site-packages/pygame/__init__.py b/.venv/Lib/site-packages/pygame/__init__.py new file mode 100644 index 00000000..da8bff7c --- /dev/null +++ b/.venv/Lib/site-packages/pygame/__init__.py @@ -0,0 +1,345 @@ +# pygame - Python Game Library +# Copyright (C) 2000-2001 Pete Shinners +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Pete Shinners +# pete@shinners.org +"""Pygame is a set of Python modules designed for writing games. +It is written on top of the excellent SDL library. This allows you +to create fully featured games and multimedia programs in the python +language. The package is highly portable, with games running on +Windows, MacOS, OS X, BeOS, FreeBSD, IRIX, and Linux.""" + +import sys +import os + +# Choose Windows display driver +if os.name == "nt": + pygame_dir = os.path.split(__file__)[0] + + # pypy does not find the dlls, so we add package folder to PATH. + os.environ["PATH"] = os.environ["PATH"] + ";" + pygame_dir + + # windows store python does not find the dlls, so we run this + if sys.version_info > (3, 8): + os.add_dll_directory(pygame_dir) # only available in 3.8+ + + # cleanup namespace + del pygame_dir + +# when running under X11, always set the SDL window WM_CLASS to make the +# window managers correctly match the pygame window. +elif "DISPLAY" in os.environ and "SDL_VIDEO_X11_WMCLASS" not in os.environ: + os.environ["SDL_VIDEO_X11_WMCLASS"] = os.path.basename(sys.argv[0]) + + +def _attribute_undefined(name): + raise RuntimeError(f"{name} is not available") + + +class MissingModule: + _NOT_IMPLEMENTED_ = True + + def __init__(self, name, urgent=0): + self.name = name + exc_type, exc_msg = sys.exc_info()[:2] + self.info = str(exc_msg) + self.reason = f"{exc_type.__name__}: {self.info}" + self.urgent = urgent + if urgent: + self.warn() + + def __getattr__(self, var): + if not self.urgent: + self.warn() + self.urgent = 1 + missing_msg = f"{self.name} module not available ({self.reason})" + raise NotImplementedError(missing_msg) + + def __bool__(self): + return False + + def warn(self): + msg_type = "import" if self.urgent else "use" + message = f"{msg_type} {self.name}: {self.info}\n({self.reason})" + try: + import warnings + + level = 4 if self.urgent else 3 + warnings.warn(message, RuntimeWarning, level) + except ImportError: + print(message) + + +# we need to import like this, each at a time. the cleanest way to import +# our modules is with the import command (not the __import__ function) +# isort: skip_file + +# first, the "required" modules +from pygame.base import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] +from pygame.constants import * # now has __all__ pylint: disable=wildcard-import; lgtm[py/polluting-import] +from pygame.version import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] +from pygame.rect import Rect +from pygame.rwobject import encode_string, encode_file_path +import pygame.surflock +import pygame.color + +Color = pygame.color.Color +import pygame.bufferproxy + +BufferProxy = pygame.bufferproxy.BufferProxy +import pygame.math + +Vector2 = pygame.math.Vector2 +Vector3 = pygame.math.Vector3 + +__version__ = ver + +# next, the "standard" modules +# we still allow them to be missing for stripped down pygame distributions +if get_sdl_version() < (2, 0, 0): + # cdrom only available for SDL 1.2.X + try: + import pygame.cdrom + except (ImportError, OSError): + cdrom = MissingModule("cdrom", urgent=1) + +try: + import pygame.display +except (ImportError, OSError): + display = MissingModule("display", urgent=1) + +try: + import pygame.draw +except (ImportError, OSError): + draw = MissingModule("draw", urgent=1) + +try: + import pygame.event +except (ImportError, OSError): + event = MissingModule("event", urgent=1) + +try: + import pygame.image +except (ImportError, OSError): + image = MissingModule("image", urgent=1) + +try: + import pygame.joystick +except (ImportError, OSError): + joystick = MissingModule("joystick", urgent=1) + +try: + import pygame.key +except (ImportError, OSError): + key = MissingModule("key", urgent=1) + +try: + import pygame.mouse +except (ImportError, OSError): + mouse = MissingModule("mouse", urgent=1) + +try: + import pygame.cursors + from pygame.cursors import Cursor +except (ImportError, OSError): + cursors = MissingModule("cursors", urgent=1) + + def Cursor(*args): # pylint: disable=unused-argument + _attribute_undefined("pygame.Cursor") + + +try: + import pygame.sprite +except (ImportError, OSError): + sprite = MissingModule("sprite", urgent=1) + +try: + import pygame.threads +except (ImportError, OSError): + threads = MissingModule("threads", urgent=1) + +try: + import pygame.pixelcopy +except (ImportError, OSError): + pixelcopy = MissingModule("pixelcopy", urgent=1) + + +try: + from pygame.surface import Surface, SurfaceType +except (ImportError, OSError): + + def Surface(size, flags, depth, masks): # pylint: disable=unused-argument + _attribute_undefined("pygame.Surface") + + SurfaceType = Surface + +try: + import pygame.mask + from pygame.mask import Mask +except (ImportError, OSError): + mask = MissingModule("mask", urgent=0) + + def Mask(size, fill): # pylint: disable=unused-argument + _attribute_undefined("pygame.Mask") + + +try: + from pygame.pixelarray import PixelArray +except (ImportError, OSError): + + def PixelArray(surface): # pylint: disable=unused-argument + _attribute_undefined("pygame.PixelArray") + + +try: + from pygame.overlay import Overlay +except (ImportError, OSError): + + def Overlay(format, size): # pylint: disable=unused-argument + _attribute_undefined("pygame.Overlay") + + +try: + import pygame.time +except (ImportError, OSError): + time = MissingModule("time", urgent=1) + +try: + import pygame.transform +except (ImportError, OSError): + transform = MissingModule("transform", urgent=1) + +# lastly, the "optional" pygame modules +if "PYGAME_FREETYPE" in os.environ: + try: + import pygame.ftfont as font + + sys.modules["pygame.font"] = font + except (ImportError, OSError): + pass +try: + import pygame.font + import pygame.sysfont + + pygame.font.SysFont = pygame.sysfont.SysFont + pygame.font.get_fonts = pygame.sysfont.get_fonts + pygame.font.match_font = pygame.sysfont.match_font +except (ImportError, OSError): + font = MissingModule("font", urgent=0) + +# try and load pygame.mixer_music before mixer, for py2app... +try: + import pygame.mixer_music + + # del pygame.mixer_music + # print("NOTE2: failed importing pygame.mixer_music in lib/__init__.py") +except (ImportError, OSError): + pass + +try: + import pygame.mixer +except (ImportError, OSError): + mixer = MissingModule("mixer", urgent=0) + +try: + import pygame.scrap +except (ImportError, OSError): + scrap = MissingModule("scrap", urgent=0) + +try: + import pygame.surfarray +except (ImportError, OSError): + surfarray = MissingModule("surfarray", urgent=0) + +try: + import pygame.sndarray +except (ImportError, OSError): + sndarray = MissingModule("sndarray", urgent=0) + +try: + import pygame.fastevent +except (ImportError, OSError): + fastevent = MissingModule("fastevent", urgent=0) + +# there's also a couple "internal" modules not needed +# by users, but putting them here helps "dependency finder" +# programs get everything they need (like py2exe) +try: + import pygame.imageext + + del pygame.imageext +except (ImportError, OSError): + pass + +# this internal module needs to be included for dependency +# finders, but can't be deleted, as some tests need it +try: + import pygame.pkgdata + +except (ImportError, OSError): + pass + + +def packager_imports(): + """some additional imports that py2app/py2exe will want to see""" + import atexit + import numpy + import OpenGL.GL + import pygame.macosx + import pygame.colordict + + +# make Rects pickleable + +import copyreg + + +def __rect_constructor(x, y, w, h): + return Rect(x, y, w, h) + + +def __rect_reduce(r): + assert isinstance(r, Rect) + return __rect_constructor, (r.x, r.y, r.w, r.h) + + +copyreg.pickle(Rect, __rect_reduce, __rect_constructor) + + +# make Colors pickleable +def __color_constructor(r, g, b, a): + return Color(r, g, b, a) + + +def __color_reduce(c): + assert isinstance(c, Color) + return __color_constructor, (c.r, c.g, c.b, c.a) + + +copyreg.pickle(Color, __color_reduce, __color_constructor) + +# Thanks for supporting pygame. Without support now, there won't be pygame later. +if "PYGAME_HIDE_SUPPORT_PROMPT" not in os.environ: + print( + "pygame {} (SDL {}.{}.{}, Python {}.{}.{})".format( # pylint: disable=consider-using-f-string + ver, *get_sdl_version() + sys.version_info[0:3] + ) + ) + print("Hello from the pygame community. https://www.pygame.org/contribute.html") + +# cleanup namespace +del pygame, os, sys, MissingModule, copyreg, packager_imports diff --git a/.venv/Lib/site-packages/pygame/__init__.pyi b/.venv/Lib/site-packages/pygame/__init__.pyi new file mode 100644 index 00000000..2d26fd11 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/__init__.pyi @@ -0,0 +1,632 @@ +# buildconfig/stubs/gen_stubs.py +# A script to auto-generate locals.pyi, constants.pyi and __init__.pyi typestubs +# IMPORTANT NOTE: Do not edit this file by hand! + + +from typing import Tuple, NoReturn + +def Overlay(format: int, size: Tuple[int, int]) -> NoReturn: ... + +from pygame import ( + display as display, + draw as draw, + event as event, + font as font, + image as image, + key as key, + mixer as mixer, + mouse as mouse, + time as time, + cursors as cursors, + joystick as joystick, + math as math, + mask as mask, + pixelcopy as pixelcopy, + sndarray as sndarray, + sprite as sprite, + surfarray as surfarray, + transform as transform, + fastevent as fastevent, + scrap as scrap, + threads as threads, + version as version, + base as base, + bufferproxy as bufferproxy, + color as color, + colordict as colordict, + mixer_music as mixer_music, + pixelarray as pixelarray, + rect as rect, + rwobject as rwobject, + surface as surface, + surflock as surflock, + sysfont as sysfont, +) + +from .rect import Rect as Rect +from .surface import Surface as Surface, SurfaceType as SurfaceType +from .color import Color as Color +from .pixelarray import PixelArray as PixelArray +from .math import Vector2 as Vector2, Vector3 as Vector3 +from .cursors import Cursor as Cursor +from .bufferproxy import BufferProxy as BufferProxy +from .mask import Mask as Mask +from .base import ( + BufferError as BufferError, + HAVE_NEWBUF as HAVE_NEWBUF, + error as error, + get_array_interface as get_array_interface, + get_error as get_error, + get_init as get_init, + get_sdl_byteorder as get_sdl_byteorder, + get_sdl_version as get_sdl_version, + init as init, + quit as quit, + register_quit as register_quit, + set_error as set_error, +) + +from .rwobject import ( + encode_file_path as encode_file_path, + encode_string as encode_string, +) + +from .version import SDL as SDL, rev as rev, ver as ver, vernum as vernum, ver as __version__ +from .constants import ( + ACTIVEEVENT as ACTIVEEVENT, + ANYFORMAT as ANYFORMAT, + APPACTIVE as APPACTIVE, + APPINPUTFOCUS as APPINPUTFOCUS, + APPMOUSEFOCUS as APPMOUSEFOCUS, + APP_DIDENTERBACKGROUND as APP_DIDENTERBACKGROUND, + APP_DIDENTERFOREGROUND as APP_DIDENTERFOREGROUND, + APP_LOWMEMORY as APP_LOWMEMORY, + APP_TERMINATING as APP_TERMINATING, + APP_WILLENTERBACKGROUND as APP_WILLENTERBACKGROUND, + APP_WILLENTERFOREGROUND as APP_WILLENTERFOREGROUND, + ASYNCBLIT as ASYNCBLIT, + AUDIODEVICEADDED as AUDIODEVICEADDED, + AUDIODEVICEREMOVED as AUDIODEVICEREMOVED, + AUDIO_ALLOW_ANY_CHANGE as AUDIO_ALLOW_ANY_CHANGE, + AUDIO_ALLOW_CHANNELS_CHANGE as AUDIO_ALLOW_CHANNELS_CHANGE, + AUDIO_ALLOW_FORMAT_CHANGE as AUDIO_ALLOW_FORMAT_CHANGE, + AUDIO_ALLOW_FREQUENCY_CHANGE as AUDIO_ALLOW_FREQUENCY_CHANGE, + AUDIO_S16 as AUDIO_S16, + AUDIO_S16LSB as AUDIO_S16LSB, + AUDIO_S16MSB as AUDIO_S16MSB, + AUDIO_S16SYS as AUDIO_S16SYS, + AUDIO_S8 as AUDIO_S8, + AUDIO_U16 as AUDIO_U16, + AUDIO_U16LSB as AUDIO_U16LSB, + AUDIO_U16MSB as AUDIO_U16MSB, + AUDIO_U16SYS as AUDIO_U16SYS, + AUDIO_U8 as AUDIO_U8, + BIG_ENDIAN as BIG_ENDIAN, + BLENDMODE_ADD as BLENDMODE_ADD, + BLENDMODE_BLEND as BLENDMODE_BLEND, + BLENDMODE_MOD as BLENDMODE_MOD, + BLENDMODE_NONE as BLENDMODE_NONE, + BLEND_ADD as BLEND_ADD, + BLEND_ALPHA_SDL2 as BLEND_ALPHA_SDL2, + BLEND_MAX as BLEND_MAX, + BLEND_MIN as BLEND_MIN, + BLEND_MULT as BLEND_MULT, + BLEND_PREMULTIPLIED as BLEND_PREMULTIPLIED, + BLEND_RGBA_ADD as BLEND_RGBA_ADD, + BLEND_RGBA_MAX as BLEND_RGBA_MAX, + BLEND_RGBA_MIN as BLEND_RGBA_MIN, + BLEND_RGBA_MULT as BLEND_RGBA_MULT, + BLEND_RGBA_SUB as BLEND_RGBA_SUB, + BLEND_RGB_ADD as BLEND_RGB_ADD, + BLEND_RGB_MAX as BLEND_RGB_MAX, + BLEND_RGB_MIN as BLEND_RGB_MIN, + BLEND_RGB_MULT as BLEND_RGB_MULT, + BLEND_RGB_SUB as BLEND_RGB_SUB, + BLEND_SUB as BLEND_SUB, + BUTTON_LEFT as BUTTON_LEFT, + BUTTON_MIDDLE as BUTTON_MIDDLE, + BUTTON_RIGHT as BUTTON_RIGHT, + BUTTON_WHEELDOWN as BUTTON_WHEELDOWN, + BUTTON_WHEELUP as BUTTON_WHEELUP, + BUTTON_X1 as BUTTON_X1, + BUTTON_X2 as BUTTON_X2, + CLIPBOARDUPDATE as CLIPBOARDUPDATE, + CONTROLLERAXISMOTION as CONTROLLERAXISMOTION, + CONTROLLERBUTTONDOWN as CONTROLLERBUTTONDOWN, + CONTROLLERBUTTONUP as CONTROLLERBUTTONUP, + CONTROLLERDEVICEADDED as CONTROLLERDEVICEADDED, + CONTROLLERDEVICEREMAPPED as CONTROLLERDEVICEREMAPPED, + CONTROLLERDEVICEREMOVED as CONTROLLERDEVICEREMOVED, + CONTROLLERSENSORUPDATE as CONTROLLERSENSORUPDATE, + CONTROLLERTOUCHPADDOWN as CONTROLLERTOUCHPADDOWN, + CONTROLLERTOUCHPADMOTION as CONTROLLERTOUCHPADMOTION, + CONTROLLERTOUCHPADUP as CONTROLLERTOUCHPADUP, + CONTROLLER_AXIS_INVALID as CONTROLLER_AXIS_INVALID, + CONTROLLER_AXIS_LEFTX as CONTROLLER_AXIS_LEFTX, + CONTROLLER_AXIS_LEFTY as CONTROLLER_AXIS_LEFTY, + CONTROLLER_AXIS_MAX as CONTROLLER_AXIS_MAX, + CONTROLLER_AXIS_RIGHTX as CONTROLLER_AXIS_RIGHTX, + CONTROLLER_AXIS_RIGHTY as CONTROLLER_AXIS_RIGHTY, + CONTROLLER_AXIS_TRIGGERLEFT as CONTROLLER_AXIS_TRIGGERLEFT, + CONTROLLER_AXIS_TRIGGERRIGHT as CONTROLLER_AXIS_TRIGGERRIGHT, + CONTROLLER_BUTTON_A as CONTROLLER_BUTTON_A, + CONTROLLER_BUTTON_B as CONTROLLER_BUTTON_B, + CONTROLLER_BUTTON_BACK as CONTROLLER_BUTTON_BACK, + CONTROLLER_BUTTON_DPAD_DOWN as CONTROLLER_BUTTON_DPAD_DOWN, + CONTROLLER_BUTTON_DPAD_LEFT as CONTROLLER_BUTTON_DPAD_LEFT, + CONTROLLER_BUTTON_DPAD_RIGHT as CONTROLLER_BUTTON_DPAD_RIGHT, + CONTROLLER_BUTTON_DPAD_UP as CONTROLLER_BUTTON_DPAD_UP, + CONTROLLER_BUTTON_GUIDE as CONTROLLER_BUTTON_GUIDE, + CONTROLLER_BUTTON_INVALID as CONTROLLER_BUTTON_INVALID, + CONTROLLER_BUTTON_LEFTSHOULDER as CONTROLLER_BUTTON_LEFTSHOULDER, + CONTROLLER_BUTTON_LEFTSTICK as CONTROLLER_BUTTON_LEFTSTICK, + CONTROLLER_BUTTON_MAX as CONTROLLER_BUTTON_MAX, + CONTROLLER_BUTTON_RIGHTSHOULDER as CONTROLLER_BUTTON_RIGHTSHOULDER, + CONTROLLER_BUTTON_RIGHTSTICK as CONTROLLER_BUTTON_RIGHTSTICK, + CONTROLLER_BUTTON_START as CONTROLLER_BUTTON_START, + CONTROLLER_BUTTON_X as CONTROLLER_BUTTON_X, + CONTROLLER_BUTTON_Y as CONTROLLER_BUTTON_Y, + DOUBLEBUF as DOUBLEBUF, + DROPBEGIN as DROPBEGIN, + DROPCOMPLETE as DROPCOMPLETE, + DROPFILE as DROPFILE, + DROPTEXT as DROPTEXT, + FINGERDOWN as FINGERDOWN, + FINGERMOTION as FINGERMOTION, + FINGERUP as FINGERUP, + FULLSCREEN as FULLSCREEN, + GL_ACCELERATED_VISUAL as GL_ACCELERATED_VISUAL, + GL_ACCUM_ALPHA_SIZE as GL_ACCUM_ALPHA_SIZE, + GL_ACCUM_BLUE_SIZE as GL_ACCUM_BLUE_SIZE, + GL_ACCUM_GREEN_SIZE as GL_ACCUM_GREEN_SIZE, + GL_ACCUM_RED_SIZE as GL_ACCUM_RED_SIZE, + GL_ALPHA_SIZE as GL_ALPHA_SIZE, + GL_BLUE_SIZE as GL_BLUE_SIZE, + GL_BUFFER_SIZE as GL_BUFFER_SIZE, + GL_CONTEXT_DEBUG_FLAG as GL_CONTEXT_DEBUG_FLAG, + GL_CONTEXT_FLAGS as GL_CONTEXT_FLAGS, + GL_CONTEXT_FORWARD_COMPATIBLE_FLAG as GL_CONTEXT_FORWARD_COMPATIBLE_FLAG, + GL_CONTEXT_MAJOR_VERSION as GL_CONTEXT_MAJOR_VERSION, + GL_CONTEXT_MINOR_VERSION as GL_CONTEXT_MINOR_VERSION, + GL_CONTEXT_PROFILE_COMPATIBILITY as GL_CONTEXT_PROFILE_COMPATIBILITY, + GL_CONTEXT_PROFILE_CORE as GL_CONTEXT_PROFILE_CORE, + GL_CONTEXT_PROFILE_ES as GL_CONTEXT_PROFILE_ES, + GL_CONTEXT_PROFILE_MASK as GL_CONTEXT_PROFILE_MASK, + GL_CONTEXT_RELEASE_BEHAVIOR as GL_CONTEXT_RELEASE_BEHAVIOR, + GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH as GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH, + GL_CONTEXT_RELEASE_BEHAVIOR_NONE as GL_CONTEXT_RELEASE_BEHAVIOR_NONE, + GL_CONTEXT_RESET_ISOLATION_FLAG as GL_CONTEXT_RESET_ISOLATION_FLAG, + GL_CONTEXT_ROBUST_ACCESS_FLAG as GL_CONTEXT_ROBUST_ACCESS_FLAG, + GL_DEPTH_SIZE as GL_DEPTH_SIZE, + GL_DOUBLEBUFFER as GL_DOUBLEBUFFER, + GL_FRAMEBUFFER_SRGB_CAPABLE as GL_FRAMEBUFFER_SRGB_CAPABLE, + GL_GREEN_SIZE as GL_GREEN_SIZE, + GL_MULTISAMPLEBUFFERS as GL_MULTISAMPLEBUFFERS, + GL_MULTISAMPLESAMPLES as GL_MULTISAMPLESAMPLES, + GL_RED_SIZE as GL_RED_SIZE, + GL_SHARE_WITH_CURRENT_CONTEXT as GL_SHARE_WITH_CURRENT_CONTEXT, + GL_STENCIL_SIZE as GL_STENCIL_SIZE, + GL_STEREO as GL_STEREO, + GL_SWAP_CONTROL as GL_SWAP_CONTROL, + HAT_CENTERED as HAT_CENTERED, + HAT_DOWN as HAT_DOWN, + HAT_LEFT as HAT_LEFT, + HAT_LEFTDOWN as HAT_LEFTDOWN, + HAT_LEFTUP as HAT_LEFTUP, + HAT_RIGHT as HAT_RIGHT, + HAT_RIGHTDOWN as HAT_RIGHTDOWN, + HAT_RIGHTUP as HAT_RIGHTUP, + HAT_UP as HAT_UP, + HIDDEN as HIDDEN, + HWACCEL as HWACCEL, + HWPALETTE as HWPALETTE, + HWSURFACE as HWSURFACE, + JOYAXISMOTION as JOYAXISMOTION, + JOYBALLMOTION as JOYBALLMOTION, + JOYBUTTONDOWN as JOYBUTTONDOWN, + JOYBUTTONUP as JOYBUTTONUP, + JOYDEVICEADDED as JOYDEVICEADDED, + JOYDEVICEREMOVED as JOYDEVICEREMOVED, + JOYHATMOTION as JOYHATMOTION, + KEYDOWN as KEYDOWN, + KEYMAPCHANGED as KEYMAPCHANGED, + KEYUP as KEYUP, + KMOD_ALT as KMOD_ALT, + KMOD_CAPS as KMOD_CAPS, + KMOD_CTRL as KMOD_CTRL, + KMOD_GUI as KMOD_GUI, + KMOD_LALT as KMOD_LALT, + KMOD_LCTRL as KMOD_LCTRL, + KMOD_LGUI as KMOD_LGUI, + KMOD_LMETA as KMOD_LMETA, + KMOD_LSHIFT as KMOD_LSHIFT, + KMOD_META as KMOD_META, + KMOD_MODE as KMOD_MODE, + KMOD_NONE as KMOD_NONE, + KMOD_NUM as KMOD_NUM, + KMOD_RALT as KMOD_RALT, + KMOD_RCTRL as KMOD_RCTRL, + KMOD_RGUI as KMOD_RGUI, + KMOD_RMETA as KMOD_RMETA, + KMOD_RSHIFT as KMOD_RSHIFT, + KMOD_SHIFT as KMOD_SHIFT, + KSCAN_0 as KSCAN_0, + KSCAN_1 as KSCAN_1, + KSCAN_2 as KSCAN_2, + KSCAN_3 as KSCAN_3, + KSCAN_4 as KSCAN_4, + KSCAN_5 as KSCAN_5, + KSCAN_6 as KSCAN_6, + KSCAN_7 as KSCAN_7, + KSCAN_8 as KSCAN_8, + KSCAN_9 as KSCAN_9, + KSCAN_A as KSCAN_A, + KSCAN_AC_BACK as KSCAN_AC_BACK, + KSCAN_APOSTROPHE as KSCAN_APOSTROPHE, + KSCAN_B as KSCAN_B, + KSCAN_BACKSLASH as KSCAN_BACKSLASH, + KSCAN_BACKSPACE as KSCAN_BACKSPACE, + KSCAN_BREAK as KSCAN_BREAK, + KSCAN_C as KSCAN_C, + KSCAN_CAPSLOCK as KSCAN_CAPSLOCK, + KSCAN_CLEAR as KSCAN_CLEAR, + KSCAN_COMMA as KSCAN_COMMA, + KSCAN_CURRENCYSUBUNIT as KSCAN_CURRENCYSUBUNIT, + KSCAN_CURRENCYUNIT as KSCAN_CURRENCYUNIT, + KSCAN_D as KSCAN_D, + KSCAN_DELETE as KSCAN_DELETE, + KSCAN_DOWN as KSCAN_DOWN, + KSCAN_E as KSCAN_E, + KSCAN_END as KSCAN_END, + KSCAN_EQUALS as KSCAN_EQUALS, + KSCAN_ESCAPE as KSCAN_ESCAPE, + KSCAN_EURO as KSCAN_EURO, + KSCAN_F as KSCAN_F, + KSCAN_F1 as KSCAN_F1, + KSCAN_F10 as KSCAN_F10, + KSCAN_F11 as KSCAN_F11, + KSCAN_F12 as KSCAN_F12, + KSCAN_F13 as KSCAN_F13, + KSCAN_F14 as KSCAN_F14, + KSCAN_F15 as KSCAN_F15, + KSCAN_F2 as KSCAN_F2, + KSCAN_F3 as KSCAN_F3, + KSCAN_F4 as KSCAN_F4, + KSCAN_F5 as KSCAN_F5, + KSCAN_F6 as KSCAN_F6, + KSCAN_F7 as KSCAN_F7, + KSCAN_F8 as KSCAN_F8, + KSCAN_F9 as KSCAN_F9, + KSCAN_G as KSCAN_G, + KSCAN_GRAVE as KSCAN_GRAVE, + KSCAN_H as KSCAN_H, + KSCAN_HELP as KSCAN_HELP, + KSCAN_HOME as KSCAN_HOME, + KSCAN_I as KSCAN_I, + KSCAN_INSERT as KSCAN_INSERT, + KSCAN_INTERNATIONAL1 as KSCAN_INTERNATIONAL1, + KSCAN_INTERNATIONAL2 as KSCAN_INTERNATIONAL2, + KSCAN_INTERNATIONAL3 as KSCAN_INTERNATIONAL3, + KSCAN_INTERNATIONAL4 as KSCAN_INTERNATIONAL4, + KSCAN_INTERNATIONAL5 as KSCAN_INTERNATIONAL5, + KSCAN_INTERNATIONAL6 as KSCAN_INTERNATIONAL6, + KSCAN_INTERNATIONAL7 as KSCAN_INTERNATIONAL7, + KSCAN_INTERNATIONAL8 as KSCAN_INTERNATIONAL8, + KSCAN_INTERNATIONAL9 as KSCAN_INTERNATIONAL9, + KSCAN_J as KSCAN_J, + KSCAN_K as KSCAN_K, + KSCAN_KP0 as KSCAN_KP0, + KSCAN_KP1 as KSCAN_KP1, + KSCAN_KP2 as KSCAN_KP2, + KSCAN_KP3 as KSCAN_KP3, + KSCAN_KP4 as KSCAN_KP4, + KSCAN_KP5 as KSCAN_KP5, + KSCAN_KP6 as KSCAN_KP6, + KSCAN_KP7 as KSCAN_KP7, + KSCAN_KP8 as KSCAN_KP8, + KSCAN_KP9 as KSCAN_KP9, + KSCAN_KP_0 as KSCAN_KP_0, + KSCAN_KP_1 as KSCAN_KP_1, + KSCAN_KP_2 as KSCAN_KP_2, + KSCAN_KP_3 as KSCAN_KP_3, + KSCAN_KP_4 as KSCAN_KP_4, + KSCAN_KP_5 as KSCAN_KP_5, + KSCAN_KP_6 as KSCAN_KP_6, + KSCAN_KP_7 as KSCAN_KP_7, + KSCAN_KP_8 as KSCAN_KP_8, + KSCAN_KP_9 as KSCAN_KP_9, + KSCAN_KP_DIVIDE as KSCAN_KP_DIVIDE, + KSCAN_KP_ENTER as KSCAN_KP_ENTER, + KSCAN_KP_EQUALS as KSCAN_KP_EQUALS, + KSCAN_KP_MINUS as KSCAN_KP_MINUS, + KSCAN_KP_MULTIPLY as KSCAN_KP_MULTIPLY, + KSCAN_KP_PERIOD as KSCAN_KP_PERIOD, + KSCAN_KP_PLUS as KSCAN_KP_PLUS, + KSCAN_L as KSCAN_L, + KSCAN_LALT as KSCAN_LALT, + KSCAN_LANG1 as KSCAN_LANG1, + KSCAN_LANG2 as KSCAN_LANG2, + KSCAN_LANG3 as KSCAN_LANG3, + KSCAN_LANG4 as KSCAN_LANG4, + KSCAN_LANG5 as KSCAN_LANG5, + KSCAN_LANG6 as KSCAN_LANG6, + KSCAN_LANG7 as KSCAN_LANG7, + KSCAN_LANG8 as KSCAN_LANG8, + KSCAN_LANG9 as KSCAN_LANG9, + KSCAN_LCTRL as KSCAN_LCTRL, + KSCAN_LEFT as KSCAN_LEFT, + KSCAN_LEFTBRACKET as KSCAN_LEFTBRACKET, + KSCAN_LGUI as KSCAN_LGUI, + KSCAN_LMETA as KSCAN_LMETA, + KSCAN_LSHIFT as KSCAN_LSHIFT, + KSCAN_LSUPER as KSCAN_LSUPER, + KSCAN_M as KSCAN_M, + KSCAN_MENU as KSCAN_MENU, + KSCAN_MINUS as KSCAN_MINUS, + KSCAN_MODE as KSCAN_MODE, + KSCAN_N as KSCAN_N, + KSCAN_NONUSBACKSLASH as KSCAN_NONUSBACKSLASH, + KSCAN_NONUSHASH as KSCAN_NONUSHASH, + KSCAN_NUMLOCK as KSCAN_NUMLOCK, + KSCAN_NUMLOCKCLEAR as KSCAN_NUMLOCKCLEAR, + KSCAN_O as KSCAN_O, + KSCAN_P as KSCAN_P, + KSCAN_PAGEDOWN as KSCAN_PAGEDOWN, + KSCAN_PAGEUP as KSCAN_PAGEUP, + KSCAN_PAUSE as KSCAN_PAUSE, + KSCAN_PERIOD as KSCAN_PERIOD, + KSCAN_POWER as KSCAN_POWER, + KSCAN_PRINT as KSCAN_PRINT, + KSCAN_PRINTSCREEN as KSCAN_PRINTSCREEN, + KSCAN_Q as KSCAN_Q, + KSCAN_R as KSCAN_R, + KSCAN_RALT as KSCAN_RALT, + KSCAN_RCTRL as KSCAN_RCTRL, + KSCAN_RETURN as KSCAN_RETURN, + KSCAN_RGUI as KSCAN_RGUI, + KSCAN_RIGHT as KSCAN_RIGHT, + KSCAN_RIGHTBRACKET as KSCAN_RIGHTBRACKET, + KSCAN_RMETA as KSCAN_RMETA, + KSCAN_RSHIFT as KSCAN_RSHIFT, + KSCAN_RSUPER as KSCAN_RSUPER, + KSCAN_S as KSCAN_S, + KSCAN_SCROLLLOCK as KSCAN_SCROLLLOCK, + KSCAN_SCROLLOCK as KSCAN_SCROLLOCK, + KSCAN_SEMICOLON as KSCAN_SEMICOLON, + KSCAN_SLASH as KSCAN_SLASH, + KSCAN_SPACE as KSCAN_SPACE, + KSCAN_SYSREQ as KSCAN_SYSREQ, + KSCAN_T as KSCAN_T, + KSCAN_TAB as KSCAN_TAB, + KSCAN_U as KSCAN_U, + KSCAN_UNKNOWN as KSCAN_UNKNOWN, + KSCAN_UP as KSCAN_UP, + KSCAN_V as KSCAN_V, + KSCAN_W as KSCAN_W, + KSCAN_X as KSCAN_X, + KSCAN_Y as KSCAN_Y, + KSCAN_Z as KSCAN_Z, + K_0 as K_0, + K_1 as K_1, + K_2 as K_2, + K_3 as K_3, + K_4 as K_4, + K_5 as K_5, + K_6 as K_6, + K_7 as K_7, + K_8 as K_8, + K_9 as K_9, + K_AC_BACK as K_AC_BACK, + K_AMPERSAND as K_AMPERSAND, + K_ASTERISK as K_ASTERISK, + K_AT as K_AT, + K_BACKQUOTE as K_BACKQUOTE, + K_BACKSLASH as K_BACKSLASH, + K_BACKSPACE as K_BACKSPACE, + K_BREAK as K_BREAK, + K_CAPSLOCK as K_CAPSLOCK, + K_CARET as K_CARET, + K_CLEAR as K_CLEAR, + K_COLON as K_COLON, + K_COMMA as K_COMMA, + K_CURRENCYSUBUNIT as K_CURRENCYSUBUNIT, + K_CURRENCYUNIT as K_CURRENCYUNIT, + K_DELETE as K_DELETE, + K_DOLLAR as K_DOLLAR, + K_DOWN as K_DOWN, + K_END as K_END, + K_EQUALS as K_EQUALS, + K_ESCAPE as K_ESCAPE, + K_EURO as K_EURO, + K_EXCLAIM as K_EXCLAIM, + K_F1 as K_F1, + K_F10 as K_F10, + K_F11 as K_F11, + K_F12 as K_F12, + K_F13 as K_F13, + K_F14 as K_F14, + K_F15 as K_F15, + K_F2 as K_F2, + K_F3 as K_F3, + K_F4 as K_F4, + K_F5 as K_F5, + K_F6 as K_F6, + K_F7 as K_F7, + K_F8 as K_F8, + K_F9 as K_F9, + K_GREATER as K_GREATER, + K_HASH as K_HASH, + K_HELP as K_HELP, + K_HOME as K_HOME, + K_INSERT as K_INSERT, + K_KP0 as K_KP0, + K_KP1 as K_KP1, + K_KP2 as K_KP2, + K_KP3 as K_KP3, + K_KP4 as K_KP4, + K_KP5 as K_KP5, + K_KP6 as K_KP6, + K_KP7 as K_KP7, + K_KP8 as K_KP8, + K_KP9 as K_KP9, + K_KP_0 as K_KP_0, + K_KP_1 as K_KP_1, + K_KP_2 as K_KP_2, + K_KP_3 as K_KP_3, + K_KP_4 as K_KP_4, + K_KP_5 as K_KP_5, + K_KP_6 as K_KP_6, + K_KP_7 as K_KP_7, + K_KP_8 as K_KP_8, + K_KP_9 as K_KP_9, + K_KP_DIVIDE as K_KP_DIVIDE, + K_KP_ENTER as K_KP_ENTER, + K_KP_EQUALS as K_KP_EQUALS, + K_KP_MINUS as K_KP_MINUS, + K_KP_MULTIPLY as K_KP_MULTIPLY, + K_KP_PERIOD as K_KP_PERIOD, + K_KP_PLUS as K_KP_PLUS, + K_LALT as K_LALT, + K_LCTRL as K_LCTRL, + K_LEFT as K_LEFT, + K_LEFTBRACKET as K_LEFTBRACKET, + K_LEFTPAREN as K_LEFTPAREN, + K_LESS as K_LESS, + K_LGUI as K_LGUI, + K_LMETA as K_LMETA, + K_LSHIFT as K_LSHIFT, + K_LSUPER as K_LSUPER, + K_MENU as K_MENU, + K_MINUS as K_MINUS, + K_MODE as K_MODE, + K_NUMLOCK as K_NUMLOCK, + K_NUMLOCKCLEAR as K_NUMLOCKCLEAR, + K_PAGEDOWN as K_PAGEDOWN, + K_PAGEUP as K_PAGEUP, + K_PAUSE as K_PAUSE, + K_PERCENT as K_PERCENT, + K_PERIOD as K_PERIOD, + K_PLUS as K_PLUS, + K_POWER as K_POWER, + K_PRINT as K_PRINT, + K_PRINTSCREEN as K_PRINTSCREEN, + K_QUESTION as K_QUESTION, + K_QUOTE as K_QUOTE, + K_QUOTEDBL as K_QUOTEDBL, + K_RALT as K_RALT, + K_RCTRL as K_RCTRL, + K_RETURN as K_RETURN, + K_RGUI as K_RGUI, + K_RIGHT as K_RIGHT, + K_RIGHTBRACKET as K_RIGHTBRACKET, + K_RIGHTPAREN as K_RIGHTPAREN, + K_RMETA as K_RMETA, + K_RSHIFT as K_RSHIFT, + K_RSUPER as K_RSUPER, + K_SCROLLLOCK as K_SCROLLLOCK, + K_SCROLLOCK as K_SCROLLOCK, + K_SEMICOLON as K_SEMICOLON, + K_SLASH as K_SLASH, + K_SPACE as K_SPACE, + K_SYSREQ as K_SYSREQ, + K_TAB as K_TAB, + K_UNDERSCORE as K_UNDERSCORE, + K_UNKNOWN as K_UNKNOWN, + K_UP as K_UP, + K_a as K_a, + K_b as K_b, + K_c as K_c, + K_d as K_d, + K_e as K_e, + K_f as K_f, + K_g as K_g, + K_h as K_h, + K_i as K_i, + K_j as K_j, + K_k as K_k, + K_l as K_l, + K_m as K_m, + K_n as K_n, + K_o as K_o, + K_p as K_p, + K_q as K_q, + K_r as K_r, + K_s as K_s, + K_t as K_t, + K_u as K_u, + K_v as K_v, + K_w as K_w, + K_x as K_x, + K_y as K_y, + K_z as K_z, + LIL_ENDIAN as LIL_ENDIAN, + LOCALECHANGED as LOCALECHANGED, + MIDIIN as MIDIIN, + MIDIOUT as MIDIOUT, + MOUSEBUTTONDOWN as MOUSEBUTTONDOWN, + MOUSEBUTTONUP as MOUSEBUTTONUP, + MOUSEMOTION as MOUSEMOTION, + MOUSEWHEEL as MOUSEWHEEL, + MULTIGESTURE as MULTIGESTURE, + NOEVENT as NOEVENT, + NOFRAME as NOFRAME, + NUMEVENTS as NUMEVENTS, + OPENGL as OPENGL, + OPENGLBLIT as OPENGLBLIT, + PREALLOC as PREALLOC, + QUIT as QUIT, + RENDER_DEVICE_RESET as RENDER_DEVICE_RESET, + RENDER_TARGETS_RESET as RENDER_TARGETS_RESET, + RESIZABLE as RESIZABLE, + RLEACCEL as RLEACCEL, + RLEACCELOK as RLEACCELOK, + SCALED as SCALED, + SCRAP_BMP as SCRAP_BMP, + SCRAP_CLIPBOARD as SCRAP_CLIPBOARD, + SCRAP_PBM as SCRAP_PBM, + SCRAP_PPM as SCRAP_PPM, + SCRAP_SELECTION as SCRAP_SELECTION, + SCRAP_TEXT as SCRAP_TEXT, + SHOWN as SHOWN, + SRCALPHA as SRCALPHA, + SRCCOLORKEY as SRCCOLORKEY, + SWSURFACE as SWSURFACE, + SYSTEM_CURSOR_ARROW as SYSTEM_CURSOR_ARROW, + SYSTEM_CURSOR_CROSSHAIR as SYSTEM_CURSOR_CROSSHAIR, + SYSTEM_CURSOR_HAND as SYSTEM_CURSOR_HAND, + SYSTEM_CURSOR_IBEAM as SYSTEM_CURSOR_IBEAM, + SYSTEM_CURSOR_NO as SYSTEM_CURSOR_NO, + SYSTEM_CURSOR_SIZEALL as SYSTEM_CURSOR_SIZEALL, + SYSTEM_CURSOR_SIZENESW as SYSTEM_CURSOR_SIZENESW, + SYSTEM_CURSOR_SIZENS as SYSTEM_CURSOR_SIZENS, + SYSTEM_CURSOR_SIZENWSE as SYSTEM_CURSOR_SIZENWSE, + SYSTEM_CURSOR_SIZEWE as SYSTEM_CURSOR_SIZEWE, + SYSTEM_CURSOR_WAIT as SYSTEM_CURSOR_WAIT, + SYSTEM_CURSOR_WAITARROW as SYSTEM_CURSOR_WAITARROW, + SYSWMEVENT as SYSWMEVENT, + TEXTEDITING as TEXTEDITING, + TEXTINPUT as TEXTINPUT, + TIMER_RESOLUTION as TIMER_RESOLUTION, + USEREVENT as USEREVENT, + USEREVENT_DROPFILE as USEREVENT_DROPFILE, + VIDEOEXPOSE as VIDEOEXPOSE, + VIDEORESIZE as VIDEORESIZE, + WINDOWCLOSE as WINDOWCLOSE, + WINDOWDISPLAYCHANGED as WINDOWDISPLAYCHANGED, + WINDOWENTER as WINDOWENTER, + WINDOWEXPOSED as WINDOWEXPOSED, + WINDOWFOCUSGAINED as WINDOWFOCUSGAINED, + WINDOWFOCUSLOST as WINDOWFOCUSLOST, + WINDOWHIDDEN as WINDOWHIDDEN, + WINDOWHITTEST as WINDOWHITTEST, + WINDOWICCPROFCHANGED as WINDOWICCPROFCHANGED, + WINDOWLEAVE as WINDOWLEAVE, + WINDOWMAXIMIZED as WINDOWMAXIMIZED, + WINDOWMINIMIZED as WINDOWMINIMIZED, + WINDOWMOVED as WINDOWMOVED, + WINDOWRESIZED as WINDOWRESIZED, + WINDOWRESTORED as WINDOWRESTORED, + WINDOWSHOWN as WINDOWSHOWN, + WINDOWSIZECHANGED as WINDOWSIZECHANGED, + WINDOWTAKEFOCUS as WINDOWTAKEFOCUS, +) diff --git a/.venv/Lib/site-packages/pygame/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/pygame/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 00000000..3b6d09f7 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/__pycache__/_camera_opencv.cpython-311.pyc b/.venv/Lib/site-packages/pygame/__pycache__/_camera_opencv.cpython-311.pyc new file mode 100644 index 00000000..b1dc2422 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/__pycache__/_camera_opencv.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/__pycache__/_camera_vidcapture.cpython-311.pyc b/.venv/Lib/site-packages/pygame/__pycache__/_camera_vidcapture.cpython-311.pyc new file mode 100644 index 00000000..578efe5f Binary files /dev/null and b/.venv/Lib/site-packages/pygame/__pycache__/_camera_vidcapture.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/__pycache__/camera.cpython-311.pyc b/.venv/Lib/site-packages/pygame/__pycache__/camera.cpython-311.pyc new file mode 100644 index 00000000..3c87f545 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/__pycache__/camera.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/__pycache__/colordict.cpython-311.pyc b/.venv/Lib/site-packages/pygame/__pycache__/colordict.cpython-311.pyc new file mode 100644 index 00000000..2371f1cd Binary files /dev/null and b/.venv/Lib/site-packages/pygame/__pycache__/colordict.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/__pycache__/cursors.cpython-311.pyc b/.venv/Lib/site-packages/pygame/__pycache__/cursors.cpython-311.pyc new file mode 100644 index 00000000..6b8895e1 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/__pycache__/cursors.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/__pycache__/draw_py.cpython-311.pyc b/.venv/Lib/site-packages/pygame/__pycache__/draw_py.cpython-311.pyc new file mode 100644 index 00000000..62a001e1 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/__pycache__/draw_py.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/__pycache__/fastevent.cpython-311.pyc b/.venv/Lib/site-packages/pygame/__pycache__/fastevent.cpython-311.pyc new file mode 100644 index 00000000..e0382cb9 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/__pycache__/fastevent.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/__pycache__/freetype.cpython-311.pyc b/.venv/Lib/site-packages/pygame/__pycache__/freetype.cpython-311.pyc new file mode 100644 index 00000000..ad9292af Binary files /dev/null and b/.venv/Lib/site-packages/pygame/__pycache__/freetype.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/__pycache__/ftfont.cpython-311.pyc b/.venv/Lib/site-packages/pygame/__pycache__/ftfont.cpython-311.pyc new file mode 100644 index 00000000..079f9ede Binary files /dev/null and b/.venv/Lib/site-packages/pygame/__pycache__/ftfont.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/__pycache__/locals.cpython-311.pyc b/.venv/Lib/site-packages/pygame/__pycache__/locals.cpython-311.pyc new file mode 100644 index 00000000..a03fa510 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/__pycache__/locals.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/__pycache__/macosx.cpython-311.pyc b/.venv/Lib/site-packages/pygame/__pycache__/macosx.cpython-311.pyc new file mode 100644 index 00000000..535f3728 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/__pycache__/macosx.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/__pycache__/midi.cpython-311.pyc b/.venv/Lib/site-packages/pygame/__pycache__/midi.cpython-311.pyc new file mode 100644 index 00000000..4097aea6 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/__pycache__/midi.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/__pycache__/pkgdata.cpython-311.pyc b/.venv/Lib/site-packages/pygame/__pycache__/pkgdata.cpython-311.pyc new file mode 100644 index 00000000..bf2f578d Binary files /dev/null and b/.venv/Lib/site-packages/pygame/__pycache__/pkgdata.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/__pycache__/sndarray.cpython-311.pyc b/.venv/Lib/site-packages/pygame/__pycache__/sndarray.cpython-311.pyc new file mode 100644 index 00000000..bed6234a Binary files /dev/null and b/.venv/Lib/site-packages/pygame/__pycache__/sndarray.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/__pycache__/sprite.cpython-311.pyc b/.venv/Lib/site-packages/pygame/__pycache__/sprite.cpython-311.pyc new file mode 100644 index 00000000..e8e456f3 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/__pycache__/sprite.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/__pycache__/surfarray.cpython-311.pyc b/.venv/Lib/site-packages/pygame/__pycache__/surfarray.cpython-311.pyc new file mode 100644 index 00000000..f12eff9b Binary files /dev/null and b/.venv/Lib/site-packages/pygame/__pycache__/surfarray.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/__pycache__/sysfont.cpython-311.pyc b/.venv/Lib/site-packages/pygame/__pycache__/sysfont.cpython-311.pyc new file mode 100644 index 00000000..b0671afa Binary files /dev/null and b/.venv/Lib/site-packages/pygame/__pycache__/sysfont.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/__pycache__/version.cpython-311.pyc b/.venv/Lib/site-packages/pygame/__pycache__/version.cpython-311.pyc new file mode 100644 index 00000000..4cf7d727 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/__pycache__/version.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/__pyinstaller/__init__.py b/.venv/Lib/site-packages/pygame/__pyinstaller/__init__.py new file mode 100644 index 00000000..1c52aadf --- /dev/null +++ b/.venv/Lib/site-packages/pygame/__pyinstaller/__init__.py @@ -0,0 +1,5 @@ +import os + + +def get_hook_dirs(): + return [os.path.dirname(__file__)] diff --git a/.venv/Lib/site-packages/pygame/__pyinstaller/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/pygame/__pyinstaller/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 00000000..5d92fdc1 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/__pyinstaller/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/__pyinstaller/__pycache__/hook-pygame.cpython-311.pyc b/.venv/Lib/site-packages/pygame/__pyinstaller/__pycache__/hook-pygame.cpython-311.pyc new file mode 100644 index 00000000..bc28ed06 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/__pyinstaller/__pycache__/hook-pygame.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/__pyinstaller/hook-pygame.py b/.venv/Lib/site-packages/pygame/__pyinstaller/hook-pygame.py new file mode 100644 index 00000000..01477b6d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/__pyinstaller/hook-pygame.py @@ -0,0 +1,45 @@ +""" +binaries hook for pygame seems to be required for pygame 2.0 Windows. +Otherwise some essential DLLs will not be transferred to the exe. + +And also put hooks for datas, resources that pygame uses, to work +correctly with pyinstaller +""" + +import os +import platform + +from pygame import __file__ as pygame_main_file + +# Get pygame's folder +pygame_folder = os.path.dirname(os.path.abspath(pygame_main_file)) + +# datas is the variable that pyinstaller looks for while processing hooks +datas = [] + + +# A helper to append the relative path of a resource to hook variable - datas +def _append_to_datas(file_path): + res_path = os.path.join(pygame_folder, file_path) + if os.path.exists(res_path): + datas.append((res_path, "pygame")) + + +# First append the font file, then based on the OS, append pygame icon file +_append_to_datas("freesansbold.ttf") +if platform.system() == "Darwin": + _append_to_datas("pygame_icon_mac.bmp") +else: + _append_to_datas("pygame_icon.bmp") + +if platform.system() == "Windows": + from PyInstaller.utils.hooks import collect_dynamic_libs + + pre_binaries = collect_dynamic_libs("pygame") + binaries = [] + + for b in pre_binaries: + binary, location = b + # settles all the DLLs into the top level folder, which prevents duplication + # with the DLLs already being put there. + binaries.append((binary, ".")) diff --git a/.venv/Lib/site-packages/pygame/_camera.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/_camera.cp311-win_amd64.pyd new file mode 100644 index 00000000..6096db5b Binary files /dev/null and b/.venv/Lib/site-packages/pygame/_camera.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/_camera_opencv.py b/.venv/Lib/site-packages/pygame/_camera_opencv.py new file mode 100644 index 00000000..8f1bb55e --- /dev/null +++ b/.venv/Lib/site-packages/pygame/_camera_opencv.py @@ -0,0 +1,208 @@ +"""pygame.camera backend that uses OpenCV. + +Uses the cv2 module opencv for python. +See https://pypi.org/project/opencv-python/ for wheels version. + +python3 -m pip install opencv-python --user +""" +import numpy +import cv2 +import time + +import pygame + + +def list_cameras(): + """ """ + index = 0 + device_idx = [] + failed = 0 + + # Sometimes there are gaps between the device index. + # We keep trying max_gaps times. + max_gaps = 3 + + while failed < max_gaps: + vcap = cv2.VideoCapture(index) + if not vcap.read()[0]: + failed += 1 + else: + device_idx.append(index) + vcap.release() + index += 1 + return device_idx + + +def list_cameras_darwin(): + import subprocess + from xml.etree import ElementTree + + # pylint: disable=consider-using-with + flout, _ = subprocess.Popen( + "system_profiler -xml SPCameraDataType", + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ).communicate() + + last_text = None + cameras = [] + + for node in ElementTree.fromstring(flout).iterfind("./array/dict/array/dict/*"): + if last_text == "_name": + cameras.append(node.text) + last_text = node.text + + return cameras + + +class Camera: + def __init__(self, device=0, size=(640, 480), mode="RGB", api_preference=None): + """ + api_preference - cv2.CAP_DSHOW cv2.CAP_V4L2 cv2.CAP_MSMF and others + + # See https://docs.opencv.org/3.4/d4/d15/group__videoio__flags__base.html + """ + self._device_index = device + self._size = size + + self.api_preference = api_preference + if api_preference is not None: + if sys.platform == "win32": + # seems more compatible on windows? + self.api_preference = cv2.CAP_DSHOW + + if mode == "RGB": + self._fmt = cv2.COLOR_BGR2RGB + elif mode == "YUV": + self._fmt = cv2.COLOR_BGR2YUV + elif mode == "HSV": + self._fmt = cv2.COLOR_BGR2HSV + else: + raise ValueError("Not a supported mode") + + self._open = False + + # all of this could have been done in the constructor, but creating + # the VideoCapture is very time consuming, so it makes more sense in the + # actual start() method + def start(self): + if self._open: + return + + self._cam = cv2.VideoCapture(self._device_index, self.api_preference) + + if not self._cam.isOpened(): + raise ValueError("Could not open camera.") + + self._cam.set(cv2.CAP_PROP_FRAME_WIDTH, self._size[0]) + self._cam.set(cv2.CAP_PROP_FRAME_HEIGHT, self._size[1]) + + w = self._cam.get(cv2.CAP_PROP_FRAME_WIDTH) + h = self._cam.get(cv2.CAP_PROP_FRAME_HEIGHT) + self._size = (int(w), int(h)) + + self._flipx = False + self._flipy = False + self._brightness = 1 + + self._frametime = 1 / self._cam.get(cv2.CAP_PROP_FPS) + self._last_frame_time = 0 + + self._open = True + + def stop(self): + if self._open: + self._cam.release() + self._cam = None + self._open = False + + def _check_open(self): + if not self._open: + raise pygame.error("Camera must be started") + + def get_size(self): + self._check_open() + + return self._size + + def set_controls(self, hflip=None, vflip=None, brightness=None): + self._check_open() + + if hflip is not None: + self._flipx = bool(hflip) + if vflip is not None: + self._flipy = bool(vflip) + if brightness is not None: + self._cam.set(cv2.CAP_PROP_BRIGHTNESS, brightness) + + return self.get_controls() + + def get_controls(self): + self._check_open() + + return (self._flipx, self._flipy, self._cam.get(cv2.CAP_PROP_BRIGHTNESS)) + + def query_image(self): + self._check_open() + + current_time = time.time() + if current_time - self._last_frame_time > self._frametime: + return True + return False + + def get_image(self, dest_surf=None): + self._check_open() + + self._last_frame_time = time.time() + + _, image = self._cam.read() + + image = cv2.cvtColor(image, self._fmt) + + flip_code = None + if self._flipx: + if self._flipy: + flip_code = -1 + else: + flip_code = 1 + elif self._flipy: + flip_code = 0 + + if flip_code is not None: + image = cv2.flip(image, flip_code) + + image = numpy.fliplr(image) + image = numpy.rot90(image) + + surf = pygame.surfarray.make_surface(image) + + if dest_surf: + dest_surf.blit(surf, (0, 0)) + return dest_surf + + return surf + + def get_raw(self): + self._check_open() + + self._last_frame_time = time.time() + + _, image = self._cam.read() + + return image.tobytes() + + +class CameraMac(Camera): + def __init__(self, device=0, size=(640, 480), mode="RGB", api_preference=None): + if isinstance(device, int): + _dev = device + elif isinstance(device, str): + _dev = list_cameras_darwin().index(device) + else: + raise TypeError( + "OpenCV-Mac backend can take device indices or names, ints or strings, not ", + str(type(device)), + ) + + super().__init__(_dev, size, mode, api_preference) diff --git a/.venv/Lib/site-packages/pygame/_camera_vidcapture.py b/.venv/Lib/site-packages/pygame/_camera_vidcapture.py new file mode 100644 index 00000000..56df43d5 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/_camera_vidcapture.py @@ -0,0 +1,117 @@ +"""pygame.camera.Camera implementation using the videocapture module for windows. + +http://videocapture.sourceforge.net/ + +Binary windows wheels: + https://www.lfd.uci.edu/~gohlke/pythonlibs/#videocapture +""" +import pygame + + +def list_cameras(): + """Always only lists one camera. + + Functionality not supported in videocapture module. + """ + return [0] + + # this just cycles through all the cameras trying to open them + # cameras = [] + # for x in range(256): + # try: + # c = Camera(x) + # except: + # break + # cameras.append(x) + # return cameras + + +def init(): + global vidcap + try: + import vidcap as vc + except ImportError: + from VideoCapture import vidcap as vc + vidcap = vc + + +def quit(): + global vidcap + vidcap = None + + +class Camera: + # pylint: disable=unused-argument + def __init__(self, device=0, size=(640, 480), mode="RGB", show_video_window=0): + """device: VideoCapture enumerates the available video capture devices + on your system. If you have more than one device, specify + the desired one here. The device number starts from 0. + + show_video_window: 0 ... do not display a video window (the default) + 1 ... display a video window + + Mainly used for debugging, since the video window + can not be closed or moved around. + """ + self.dev = vidcap.new_Dev(device, show_video_window) + width, height = size + self.dev.setresolution(width, height) + + def display_capture_filter_properties(self): + """Displays a dialog containing the property page of the capture filter. + + For VfW drivers you may find the option to select the resolution most + likely here. + """ + self.dev.displaycapturefilterproperties() + + def display_capture_pin_properties(self): + """Displays a dialog containing the property page of the capture pin. + + For WDM drivers you may find the option to select the resolution most + likely here. + """ + self.dev.displaycapturepinproperties() + + def set_resolution(self, width, height): + """Sets the capture resolution. (without dialog)""" + self.dev.setresolution(width, height) + + def get_buffer(self): + """Returns a string containing the raw pixel data.""" + return self.dev.getbuffer() + + def start(self): + """Not implemented.""" + + def set_controls(self, **kwargs): + """Not implemented.""" + + def stop(self): + """Not implemented.""" + + def get_image(self, dest_surf=None): + """ """ + return self.get_surface(dest_surf) + + def get_surface(self, dest_surf=None): + """Returns a pygame Surface.""" + abuffer, width, height = self.get_buffer() + if not abuffer: + return None + surf = pygame.image.frombuffer(abuffer, (width, height), "BGR") + surf = pygame.transform.flip(surf, 0, 1) + # if there is a destination surface given, we blit onto that. + if dest_surf: + dest_surf.blit(surf, (0, 0)) + else: + dest_surf = surf + return dest_surf + + +if __name__ == "__main__": + import pygame.examples.camera + + pygame.camera.Camera = Camera + pygame.camera.list_cameras = list_cameras + pygame.examples.camera.main() diff --git a/.venv/Lib/site-packages/pygame/_common.pyi b/.venv/Lib/site-packages/pygame/_common.pyi new file mode 100644 index 00000000..bd139601 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/_common.pyi @@ -0,0 +1,40 @@ +from os import PathLike +from typing import IO, Callable, Sequence, Tuple, Union + +from typing_extensions import Literal as Literal +from typing_extensions import Protocol + +from pygame.color import Color +from pygame.math import Vector2 +from pygame.rect import Rect + +# For functions that take a file name +AnyPath = Union[str, bytes, PathLike[str], PathLike[bytes]] + +# Most pygame functions that take a file argument should be able to handle +# a FileArg type +FileArg = Union[AnyPath, IO[bytes], IO[str]] + +Coordinate = Union[Tuple[float, float], Sequence[float], Vector2] + +# This typehint is used when a function would return an RGBA tuble +RGBAOutput = Tuple[int, int, int, int] +ColorValue = Union[Color, int, str, Tuple[int, int, int], RGBAOutput, Sequence[int]] +from typing import Union + +def my_function(my_var: Union[int, float, complex]) -> None: + print(my_var) +_CanBeRect = Union[ + Rect, + Tuple[Union[float, int], Union[float, int], Union[float, int], Union[float, int]], + Tuple[Coordinate, Coordinate], + Sequence[Union[float, int]], + Sequence[Coordinate], +] + +class _HasRectAttribute(Protocol): + # An object that has a rect attribute that is either a rect, or a function + # that returns a rect confirms to the rect protocol + rect: Union[RectValue, Callable[[], RectValue]] + +RectValue = Union[_CanBeRect, _HasRectAttribute] diff --git a/.venv/Lib/site-packages/pygame/_freetype.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/_freetype.cp311-win_amd64.pyd new file mode 100644 index 00000000..e967aa63 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/_freetype.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/_sdl2/__init__.py b/.venv/Lib/site-packages/pygame/_sdl2/__init__.py new file mode 100644 index 00000000..b08b716f --- /dev/null +++ b/.venv/Lib/site-packages/pygame/_sdl2/__init__.py @@ -0,0 +1,3 @@ +from .sdl2 import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] +from .audio import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] +from .video import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] diff --git a/.venv/Lib/site-packages/pygame/_sdl2/__init__.pyi b/.venv/Lib/site-packages/pygame/_sdl2/__init__.pyi new file mode 100644 index 00000000..8f929c78 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/_sdl2/__init__.pyi @@ -0,0 +1,3 @@ +from pygame._sdl2.audio import * +from pygame._sdl2.sdl2 import * +from pygame._sdl2.video import * diff --git a/.venv/Lib/site-packages/pygame/_sdl2/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/pygame/_sdl2/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 00000000..7983ab53 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/_sdl2/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/_sdl2/audio.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/_sdl2/audio.cp311-win_amd64.pyd new file mode 100644 index 00000000..eed4dce7 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/_sdl2/audio.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/_sdl2/audio.pyi b/.venv/Lib/site-packages/pygame/_sdl2/audio.pyi new file mode 100644 index 00000000..332f645d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/_sdl2/audio.pyi @@ -0,0 +1,54 @@ +from typing import Callable, List + +AUDIO_U8: int +AUDIO_S8: int +AUDIO_U16LSB: int +AUDIO_S16LSB: int +AUDIO_U16MSB: int +AUDIO_S16MSB: int +AUDIO_U16: int +AUDIO_S16: int +AUDIO_S32LSB: int +AUDIO_S32MSB: int +AUDIO_S32: int +AUDIO_F32LSB: int +AUDIO_F32MSB: int +AUDIO_F32: int + +AUDIO_ALLOW_FREQUENCY_CHANGE: int +AUDIO_ALLOW_FORMAT_CHANGE: int +AUDIO_ALLOW_CHANNELS_CHANGE: int +AUDIO_ALLOW_ANY_CHANGE: int + +def get_audio_device_names(iscapture: bool = False) -> List[str]: ... + +class AudioDevice: + def __init__( + self, + devicename: str, + iscapture: bool, + frequency: int, + audioformat: int, + numchannels: int, + chunksize: int, + allowed_changes: int, + callback: Callable[[AudioDevice, memoryview], None], + ) -> None: ... + @property + def iscapture(self) -> bool: ... + @property + def deviceid(self) -> int: ... + @property + def devicename(self) -> str: ... + @property + def callback(self) -> Callable[[AudioDevice, memoryview], None]: ... + @property + def frequency(self) -> int: ... + @property + def audioformat(self) -> int: ... + @property + def numchannels(self) -> int: ... + @property + def chunksize(self) -> int: ... + def pause(self, pause_on: int) -> None: ... + def close(self) -> None: ... diff --git a/.venv/Lib/site-packages/pygame/_sdl2/controller.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/_sdl2/controller.cp311-win_amd64.pyd new file mode 100644 index 00000000..b4a39f26 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/_sdl2/controller.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/_sdl2/controller.pyi b/.venv/Lib/site-packages/pygame/_sdl2/controller.pyi new file mode 100644 index 00000000..3810fbba --- /dev/null +++ b/.venv/Lib/site-packages/pygame/_sdl2/controller.pyi @@ -0,0 +1,35 @@ +from typing import Dict, Mapping, Optional + +from pygame.joystick import JoystickType + +def init() -> None: ... +def get_init() -> bool: ... +def quit() -> None: ... +def set_eventstate(state: bool) -> None: ... +def get_eventstate() -> bool: ... +def update() -> None: ... +def get_count() -> int: ... +def is_controller(index: int) -> bool: ... +def name_forindex(index: int) -> Optional[str]: ... + +class Controller: + def __init__(self, index: int) -> None: ... + @property + def name(self) -> str: ... + @property + def id(self) -> int: ... + def init(self) -> None: ... + def get_init(self) -> bool: ... + def quit(self) -> None: ... + @staticmethod + def from_joystick(joy: JoystickType) -> Controller: ... + def attached(self) -> bool: ... + def as_joystick(self) -> JoystickType: ... + def get_axis(self, axis: int) -> int: ... + def get_button(self, button: int) -> bool: ... + def get_mapping(self) -> Dict[str, str]: ... + def set_mapping(self, mapping: Mapping[str, str]) -> int: ... + def rumble( + self, low_frequency: float, high_frequency: float, duration: int + ) -> bool: ... + def stop_rumble(self) -> None: ... diff --git a/.venv/Lib/site-packages/pygame/_sdl2/mixer.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/_sdl2/mixer.cp311-win_amd64.pyd new file mode 100644 index 00000000..a51a9b68 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/_sdl2/mixer.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/_sdl2/sdl2.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/_sdl2/sdl2.cp311-win_amd64.pyd new file mode 100644 index 00000000..78612ed5 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/_sdl2/sdl2.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/_sdl2/sdl2.pyi b/.venv/Lib/site-packages/pygame/_sdl2/sdl2.pyi new file mode 100644 index 00000000..8a2b622d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/_sdl2/sdl2.pyi @@ -0,0 +1,16 @@ +from typing import Optional + +INIT_TIMER: int +INIT_AUDIO: int +INIT_VIDEO: int +INIT_JOYSTICK: int +INIT_HAPTIC: int +INIT_GAMECONTROLLER: int +INIT_EVENTS: int +INIT_NOPARACHUTE: int +INIT_EVERYTHING: int + +class error(RuntimeError): + def __init__(self, message: Optional[str] = None) -> None: ... + +def init_subsystem(flags: int) -> None: ... diff --git a/.venv/Lib/site-packages/pygame/_sdl2/touch.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/_sdl2/touch.cp311-win_amd64.pyd new file mode 100644 index 00000000..13869e57 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/_sdl2/touch.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/_sdl2/touch.pyi b/.venv/Lib/site-packages/pygame/_sdl2/touch.pyi new file mode 100644 index 00000000..93e27457 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/_sdl2/touch.pyi @@ -0,0 +1,6 @@ +from typing import Dict, Union + +def get_num_devices() -> int: ... +def get_device(index: int) -> int: ... +def get_num_fingers(device_id: int) -> int: ... +def get_finger(touchid: int, index: int) -> Dict[str, Union[int, float]]: ... diff --git a/.venv/Lib/site-packages/pygame/_sdl2/video.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/_sdl2/video.cp311-win_amd64.pyd new file mode 100644 index 00000000..841f2fd7 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/_sdl2/video.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/_sdl2/video.pyi b/.venv/Lib/site-packages/pygame/_sdl2/video.pyi new file mode 100644 index 00000000..a2d4afec --- /dev/null +++ b/.venv/Lib/site-packages/pygame/_sdl2/video.pyi @@ -0,0 +1,159 @@ +from typing import Any, Generator, Iterable, Optional, Tuple, Union + +from pygame.rect import Rect +from pygame.surface import Surface + +from .._common import RectValue, Literal, ColorValue + +WINDOWPOS_UNDEFINED: int +WINDOWPOS_CENTERED: int + +MESSAGEBOX_ERROR: int +MESSAGEBOX_WARNING: int +MESSAGEBOX_INFORMATION: int + +class RendererDriverInfo: + name: str + flags: int + num_texture_formats: int + max_texture_width: int + max_texture_height: int + +def get_drivers() -> Generator[RendererDriverInfo, None, None]: ... +def get_grabbed_window() -> Optional[Window]: ... +def messagebox( + title: str, + message: str, + window: Optional[Window] = None, + info: bool = False, + warn: bool = False, + error: bool = False, + buttons: Tuple[str, ...] = ("OK",), + return_button: int = 0, + escape_button: int = 0, +) -> int: ... + +class Window: + DEFAULT_SIZE: Tuple[Literal[640], Literal[480]] + def __init__( + self, + title: str = "pygame", + size: Iterable[int] = (640, 480), + position: Optional[Iterable[int]] = None, + fullscreen: bool = False, + fullscreen_desktop: bool = False, + **kwargs: bool + ) -> None: ... + @classmethod + def from_display_module(cls) -> Window: ... + @classmethod + def from_window(cls, other: int) -> Window: ... + grab: bool + relative_mouse: bool + def set_windowed(self) -> None: ... + def set_fullscreen(self, desktop: bool = False) -> None: ... + title: str + def destroy(self) -> None: ... + def hide(self) -> None: ... + def show(self) -> None: ... + def focus(self, input_only: bool = False) -> None: ... + def restore(self) -> None: ... + def maximize(self) -> None: ... + def minimize(self) -> None: ... + resizable: bool + borderless: bool + def set_icon(self, surface: Surface) -> None: ... + id: int + size: Iterable[int] + position: Union[int, Iterable[int]] + opacity: float + display_index: int + def set_modal_for(self, parent: Window) -> None: ... + +class Texture: + def __init__( + self, + renderer: Renderer, + size: Iterable[int], + static: bool = False, + streaming: bool = False, + target: bool = False, + ) -> None: ... + @staticmethod + def from_surface(renderer: Renderer, surface: Surface) -> Texture: ... + renderer: Renderer + width: int + height: int + alpha: int + blend_mode: int + color: ColorValue + def get_rect(self, **kwargs: Any) -> Rect: ... + def draw( + self, + srcrect: Optional[RectValue] = None, + dstrect: Optional[RectValue] = None, + angle: float = 0.0, + origin: Optional[Iterable[int]] = None, + flip_x: bool = False, + flip_y: bool = False, + ) -> None: ... + def update(self, surface: Surface, area: Optional[RectValue] = None) -> None: ... + +class Image: + def __init__( + self, + textureOrImage: Union[Texture, Image], + srcrect: Optional[RectValue] = None, + ) -> None: ... + def get_rect(self, **kwargs: Any) -> Rect: ... + def draw( + self, srcrect: Optional[RectValue] = None, dstrect: Optional[RectValue] = None + ) -> None: ... + angle: float + origin: Optional[Iterable[float]] + flip_x: bool + flip_y: bool + color: ColorValue + alpha: float + blend_mode: int + texture: Texture + srcrect: Rect + +class Renderer: + def __init__( + self, + window: Window, + index: int = -1, + accelerated: int = -1, + vsync: bool = False, + target_texture: bool = False, + ) -> None: ... + @classmethod + def from_window(cls, window: Window) -> Renderer: ... + draw_blend_mode: int + draw_color: ColorValue + def clear(self) -> None: ... + def present(self) -> None: ... + def get_viewport(self) -> Rect: ... + def set_viewport(self, area: Optional[RectValue]) -> None: ... + logical_size: Iterable[int] + scale: Iterable[float] + target: Optional[Texture] + def blit( + self, + source: Union[Texture, Image], + dest: Optional[RectValue] = None, + area: Optional[RectValue] = None, + special_flags: int = 0, + ) -> Rect: ... + def draw_line(self, p1: Iterable[int], p2: Iterable[int]) -> None: ... + def draw_point(self, point: Iterable[int]) -> None: ... + def draw_rect(self, rect: RectValue) -> None: ... + def fill_rect(self, rect: RectValue) -> None: ... + def to_surface( + self, surface: Optional[Surface] = None, area: Optional[RectValue] = None + ) -> Surface: ... + @staticmethod + def compose_custom_blend_mode( + color_mode: Tuple[int, int, int], alpha_mode: Tuple[int, int, int] + ) -> int: ... diff --git a/.venv/Lib/site-packages/pygame/_sprite.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/_sprite.cp311-win_amd64.pyd new file mode 100644 index 00000000..b700fea9 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/_sprite.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/base.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/base.cp311-win_amd64.pyd new file mode 100644 index 00000000..830bb28a Binary files /dev/null and b/.venv/Lib/site-packages/pygame/base.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/base.pyi b/.venv/Lib/site-packages/pygame/base.pyi new file mode 100644 index 00000000..c641e36a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/base.pyi @@ -0,0 +1,19 @@ +from typing import Any, Tuple, Callable + +class error(RuntimeError): ... +class BufferError(Exception): ... + +# Always defined +HAVE_NEWBUF: int = 1 + +def init() -> Tuple[int, int]: ... +def quit() -> None: ... +def get_init() -> bool: ... +def get_error() -> str: ... +def set_error(error_msg: str) -> None: ... +def get_sdl_version(linked: bool = True) -> Tuple[int, int, int]: ... +def get_sdl_byteorder() -> int: ... +def register_quit(callable: Callable[[], Any]) -> None: ... + +# undocumented part of pygame API, kept here to make stubtest happy +def get_array_interface(arg: Any) -> dict: ... diff --git a/.venv/Lib/site-packages/pygame/bufferproxy.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/bufferproxy.cp311-win_amd64.pyd new file mode 100644 index 00000000..4f1dde81 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/bufferproxy.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/bufferproxy.pyi b/.venv/Lib/site-packages/pygame/bufferproxy.pyi new file mode 100644 index 00000000..b77eb46c --- /dev/null +++ b/.venv/Lib/site-packages/pygame/bufferproxy.pyi @@ -0,0 +1,15 @@ +from typing import Any, Dict, overload + +class BufferProxy: + parent: Any + length: int + raw: bytes + # possibly going to be deprecated/removed soon, in which case these + # typestubs must be removed too + __array_interface__: Dict[str, Any] + __array_struct__: Any + @overload + def __init__(self) -> None: ... + @overload + def __init__(self, parent: Any) -> None: ... + def write(self, buffer: bytes, offset: int = 0) -> None: ... diff --git a/.venv/Lib/site-packages/pygame/camera.py b/.venv/Lib/site-packages/pygame/camera.py new file mode 100644 index 00000000..9fc09ef7 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/camera.py @@ -0,0 +1,211 @@ +import os +import platform +import sys +import warnings +from abc import ABC, abstractmethod + +from pygame import error + +_is_init = False + + +class AbstractCamera(ABC): + # set_controls and get_controls are not a part of the AbstractCamera ABC, + # because implementations of the same can vary across different Camera + # types + @abstractmethod + def __init__(self, *args, **kwargs): + """ """ + + @abstractmethod + def start(self): + """ """ + + @abstractmethod + def stop(self): + """ """ + + @abstractmethod + def get_size(self): + """ """ + + @abstractmethod + def query_image(self): + """ """ + + @abstractmethod + def get_image(self, dest_surf=None): + """ """ + + @abstractmethod + def get_raw(self): + """ """ + + +def _pre_init_placeholder(): + if not _is_init: + raise error("pygame.camera is not initialized") + + # camera was init, and yet functions are not monkey patched. This should + # not happen + raise NotImplementedError() + + +def _pre_init_placeholder_varargs(*_, **__): + _pre_init_placeholder() + + +class _PreInitPlaceholderCamera(AbstractCamera): + __init__ = _pre_init_placeholder_varargs + start = _pre_init_placeholder_varargs + stop = _pre_init_placeholder_varargs + get_controls = _pre_init_placeholder_varargs + set_controls = _pre_init_placeholder_varargs + get_size = _pre_init_placeholder_varargs + query_image = _pre_init_placeholder_varargs + get_image = _pre_init_placeholder_varargs + get_raw = _pre_init_placeholder_varargs + + +list_cameras = _pre_init_placeholder +Camera = _PreInitPlaceholderCamera + + +def _colorspace_not_available(*args): + raise RuntimeError("pygame is not built with colorspace support") + + +try: + from pygame import _camera + + colorspace = _camera.colorspace +except ImportError: + # Should not happen in most cases + colorspace = _colorspace_not_available + + +def _setup_backend(backend): + global list_cameras, Camera + if backend == "opencv-mac": + from pygame import _camera_opencv + + list_cameras = _camera_opencv.list_cameras_darwin + Camera = _camera_opencv.CameraMac + + elif backend == "opencv": + from pygame import _camera_opencv + + list_cameras = _camera_opencv.list_cameras + Camera = _camera_opencv.Camera + + elif backend in ("_camera (msmf)", "_camera (v4l2)"): + from pygame import _camera + + list_cameras = _camera.list_cameras + Camera = _camera.Camera + + elif backend == "videocapture": + from pygame import _camera_vidcapture + + warnings.warn( + "The VideoCapture backend is not recommended and may be removed." + "For Python3 and Windows 8+, there is now a native Windows " + "backend built into pygame.", + DeprecationWarning, + stacklevel=2, + ) + + _camera_vidcapture.init() + list_cameras = _camera_vidcapture.list_cameras + Camera = _camera_vidcapture.Camera + else: + raise ValueError("unrecognized backend name") + + +def get_backends(): + possible_backends = [] + + if sys.platform == "win32" and int(platform.win32_ver()[0].split(".")[0]) >= 8: + try: + # If cv2 is installed, prefer that on windows. + import cv2 + + possible_backends.append("OpenCV") + except ImportError: + possible_backends.append("_camera (MSMF)") + + if "linux" in sys.platform: + possible_backends.append("_camera (V4L2)") + + if "darwin" in sys.platform: + possible_backends.append("OpenCV-Mac") + + if "OpenCV" not in possible_backends: + possible_backends.append("OpenCV") + + if sys.platform == "win32": + possible_backends.append("VideoCapture") + + # see if we have any user specified defaults in environments. + camera_env = os.environ.get("PYGAME_CAMERA", "").lower() + if camera_env == "opencv": # prioritize opencv + if "OpenCV" in possible_backends: + possible_backends.remove("OpenCV") + possible_backends = ["OpenCV"] + possible_backends + + if camera_env in ("vidcapture", "videocapture"): # prioritize vidcapture + if "VideoCapture" in possible_backends: + possible_backends.remove("VideoCapture") + possible_backends = ["VideoCapture"] + possible_backends + + return possible_backends + + +def init(backend=None): + global _is_init + # select the camera module to import here. + + backends = [b.lower() for b in get_backends()] + if not backends: + raise error("No camera backends are supported on your platform!") + + backend = backends[0] if backend is None else backend.lower() + if backend not in backends: + warnings.warn( + "We don't think this is a supported backend on this system, " + "but we'll try it...", + Warning, + stacklevel=2, + ) + + try: + _setup_backend(backend) + except ImportError: + emsg = f"Backend '{backend}' is not supported on your platform!" + if backend in ("opencv", "opencv-mac", "videocapture"): + dep = "vidcap" if backend == "videocapture" else "OpenCV" + emsg += ( + f" Make sure you have '{dep}' installed to be able to use this backend" + ) + + raise error(emsg) + + _is_init = True + + +def quit(): + global _is_init, Camera, list_cameras + # reset to their respective pre-init placeholders + list_cameras = _pre_init_placeholder + Camera = _PreInitPlaceholderCamera + + _is_init = False + + +if __name__ == "__main__": + # try and use this camera stuff with the pygame camera example. + import pygame.examples.camera + + # pygame.camera.Camera = Camera + # pygame.camera.list_cameras = list_cameras + pygame.examples.camera.main() diff --git a/.venv/Lib/site-packages/pygame/camera.pyi b/.venv/Lib/site-packages/pygame/camera.pyi new file mode 100644 index 00000000..eb0360ca --- /dev/null +++ b/.venv/Lib/site-packages/pygame/camera.pyi @@ -0,0 +1,49 @@ +from abc import ABC, abstractmethod +from typing import List, Optional, Sequence, Tuple, Union + +from pygame.surface import Surface + +def get_backends() -> List[str]: ... +def init(backend: Optional[str] = None) -> None: ... +def quit() -> None: ... +def list_cameras() -> List[str]: ... + +class AbstractCamera(ABC): + @abstractmethod + def __init__(self, *args, **kwargs) -> None: ... + @abstractmethod + def start(self) -> None: ... + @abstractmethod + def stop(self) -> None: ... + @abstractmethod + def get_size(self) -> Tuple[int, int]: ... + @abstractmethod + def query_image(self) -> bool: ... + @abstractmethod + def get_image(self, dest_surf: Optional[Surface] = None) -> Surface: ... + @abstractmethod + def get_raw(self) -> bytes: ... + # set_controls and get_controls are not a part of the AbstractCamera ABC, + # because implementations of the same can vary across different Camera + # types + +class Camera(AbstractCamera): + def __init__( + self, + device: Union[str, int] = 0, + size: Union[Tuple[int, int], Sequence[int]] = (640, 480), + format: str = "RGB", + ) -> None: ... + def start(self) -> None: ... + def stop(self) -> None: ... + def get_controls(self) -> Tuple[bool, bool, int]: ... + def set_controls( + self, + hflip: bool = ..., + vflip: bool = ..., + brightness: int = ..., + ) -> Tuple[bool, bool, int]: ... + def get_size(self) -> Tuple[int, int]: ... + def query_image(self) -> bool: ... + def get_image(self, surface: Optional[Surface] = None) -> Surface: ... + def get_raw(self) -> bytes: ... diff --git a/.venv/Lib/site-packages/pygame/color.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/color.cp311-win_amd64.pyd new file mode 100644 index 00000000..d6805ee0 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/color.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/color.pyi b/.venv/Lib/site-packages/pygame/color.pyi new file mode 100644 index 00000000..93ed6886 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/color.pyi @@ -0,0 +1,56 @@ +import sys +from typing import Any, Dict, Iterator, Tuple, overload + +from ._common import ColorValue + +if sys.version_info >= (3, 9): + from collections.abc import Collection +else: + from typing import Collection + +THECOLORS: Dict[str, Tuple[int, int, int, int]] + +# Color confirms to the Collection ABC, since it also confirms to +# Sized, Iterable and Container ABCs +class Color(Collection[int]): + r: int + g: int + b: int + a: int + cmy: Tuple[float, float, float] + hsva: Tuple[float, float, float, float] + hsla: Tuple[float, float, float, float] + i1i2i3: Tuple[float, float, float] + __hash__: None # type: ignore + __array_struct__: Any + @overload + def __init__(self, r: int, g: int, b: int, a: int = 255) -> None: ... + @overload + def __init__(self, rgbvalue: ColorValue) -> None: ... + @overload + def __getitem__(self, i: int) -> int: ... + @overload + def __getitem__(self, s: slice) -> Tuple[int]: ... + def __setitem__(self, key: int, value: int) -> None: ... + def __iter__(self) -> Iterator[int]: ... + def __add__(self, other: Color) -> Color: ... + def __sub__(self, other: Color) -> Color: ... + def __mul__(self, other: Color) -> Color: ... + def __floordiv__(self, other: Color) -> Color: ... + def __mod__(self, other: Color) -> Color: ... + def __int__(self) -> int: ... + def __float__(self) -> float: ... + def __len__(self) -> int: ... + def __index__(self) -> int: ... + def __invert__(self) -> Color: ... + def __contains__(self, other: int) -> bool: ... # type: ignore[override] + def normalize(self) -> Tuple[float, float, float, float]: ... + def correct_gamma(self, gamma: float) -> Color: ... + def set_length(self, length: int) -> None: ... + def lerp(self, color: ColorValue, amount: float) -> Color: ... + def premul_alpha(self) -> Color: ... + @overload + def update(self, r: int, g: int, b: int, a: int = 255) -> None: ... + @overload + def update(self, rgbvalue: ColorValue) -> None: ... + def grayscale(self) -> Color: ... diff --git a/.venv/Lib/site-packages/pygame/colordict.py b/.venv/Lib/site-packages/pygame/colordict.py new file mode 100644 index 00000000..0bd72564 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/colordict.py @@ -0,0 +1,692 @@ +# pygame - Python Game Library +# Copyright (C) 2000-2003 Pete Shinners +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Pete Shinners +# pete@shinners.org + +""" A dictionary of RGBA tuples indexed by color names. + +See https://www.pygame.org/docs/ref/color_list.html for sample swatches. +""" + +THECOLORS = { + "aliceblue": (240, 248, 255, 255), + "antiquewhite": (250, 235, 215, 255), + "antiquewhite1": (255, 239, 219, 255), + "antiquewhite2": (238, 223, 204, 255), + "antiquewhite3": (205, 192, 176, 255), + "antiquewhite4": (139, 131, 120, 255), + "aqua": (0, 255, 255, 255), + "aquamarine": (127, 255, 212, 255), + "aquamarine1": (127, 255, 212, 255), + "aquamarine2": (118, 238, 198, 255), + "aquamarine3": (102, 205, 170, 255), + "aquamarine4": (69, 139, 116, 255), + "azure": (240, 255, 255, 255), + "azure1": (240, 255, 255, 255), + "azure3": (193, 205, 205, 255), + "azure2": (224, 238, 238, 255), + "azure4": (131, 139, 139, 255), + "beige": (245, 245, 220, 255), + "bisque": (255, 228, 196, 255), + "bisque1": (255, 228, 196, 255), + "bisque2": (238, 213, 183, 255), + "bisque3": (205, 183, 158, 255), + "bisque4": (139, 125, 107, 255), + "black": (0, 0, 0, 255), + "blanchedalmond": (255, 235, 205, 255), + "blue": (0, 0, 255, 255), + "blue1": (0, 0, 255, 255), + "blue2": (0, 0, 238, 255), + "blue3": (0, 0, 205, 255), + "blue4": (0, 0, 139, 255), + "blueviolet": (138, 43, 226, 255), + "brown": (165, 42, 42, 255), + "brown1": (255, 64, 64, 255), + "brown2": (238, 59, 59, 255), + "brown3": (205, 51, 51, 255), + "brown4": (139, 35, 35, 255), + "burlywood": (222, 184, 135, 255), + "burlywood1": (255, 211, 155, 255), + "burlywood2": (238, 197, 145, 255), + "burlywood3": (205, 170, 125, 255), + "burlywood4": (139, 115, 85, 255), + "cadetblue": (95, 158, 160, 255), + "cadetblue1": (152, 245, 255, 255), + "cadetblue2": (142, 229, 238, 255), + "cadetblue3": (122, 197, 205, 255), + "cadetblue4": (83, 134, 139, 255), + "chartreuse": (127, 255, 0, 255), + "chartreuse1": (127, 255, 0, 255), + "chartreuse2": (118, 238, 0, 255), + "chartreuse3": (102, 205, 0, 255), + "chartreuse4": (69, 139, 0, 255), + "chocolate": (210, 105, 30, 255), + "chocolate1": (255, 127, 36, 255), + "chocolate2": (238, 118, 33, 255), + "chocolate3": (205, 102, 29, 255), + "chocolate4": (139, 69, 19, 255), + "coral": (255, 127, 80, 255), + "coral1": (255, 114, 86, 255), + "coral2": (238, 106, 80, 255), + "coral3": (205, 91, 69, 255), + "coral4": (139, 62, 47, 255), + "cornflowerblue": (100, 149, 237, 255), + "cornsilk": (255, 248, 220, 255), + "cornsilk1": (255, 248, 220, 255), + "cornsilk2": (238, 232, 205, 255), + "cornsilk3": (205, 200, 177, 255), + "cornsilk4": (139, 136, 120, 255), + "crimson": (220, 20, 60, 255), + "cyan": (0, 255, 255, 255), + "cyan1": (0, 255, 255, 255), + "cyan2": (0, 238, 238, 255), + "cyan3": (0, 205, 205, 255), + "cyan4": (0, 139, 139, 255), + "darkblue": (0, 0, 139, 255), + "darkcyan": (0, 139, 139, 255), + "darkgoldenrod": (184, 134, 11, 255), + "darkgoldenrod1": (255, 185, 15, 255), + "darkgoldenrod2": (238, 173, 14, 255), + "darkgoldenrod3": (205, 149, 12, 255), + "darkgoldenrod4": (139, 101, 8, 255), + "darkgray": (169, 169, 169, 255), + "darkgreen": (0, 100, 0, 255), + "darkgrey": (169, 169, 169, 255), + "darkkhaki": (189, 183, 107, 255), + "darkmagenta": (139, 0, 139, 255), + "darkolivegreen": (85, 107, 47, 255), + "darkolivegreen1": (202, 255, 112, 255), + "darkolivegreen2": (188, 238, 104, 255), + "darkolivegreen3": (162, 205, 90, 255), + "darkolivegreen4": (110, 139, 61, 255), + "darkorange": (255, 140, 0, 255), + "darkorange1": (255, 127, 0, 255), + "darkorange2": (238, 118, 0, 255), + "darkorange3": (205, 102, 0, 255), + "darkorange4": (139, 69, 0, 255), + "darkorchid": (153, 50, 204, 255), + "darkorchid1": (191, 62, 255, 255), + "darkorchid2": (178, 58, 238, 255), + "darkorchid3": (154, 50, 205, 255), + "darkorchid4": (104, 34, 139, 255), + "darkred": (139, 0, 0, 255), + "darksalmon": (233, 150, 122, 255), + "darkseagreen": (143, 188, 143, 255), + "darkseagreen1": (193, 255, 193, 255), + "darkseagreen2": (180, 238, 180, 255), + "darkseagreen3": (155, 205, 155, 255), + "darkseagreen4": (105, 139, 105, 255), + "darkslateblue": (72, 61, 139, 255), + "darkslategray": (47, 79, 79, 255), + "darkslategray1": (151, 255, 255, 255), + "darkslategray2": (141, 238, 238, 255), + "darkslategray3": (121, 205, 205, 255), + "darkslategray4": (82, 139, 139, 255), + "darkslategrey": (47, 79, 79, 255), + "darkturquoise": (0, 206, 209, 255), + "darkviolet": (148, 0, 211, 255), + "deeppink": (255, 20, 147, 255), + "deeppink1": (255, 20, 147, 255), + "deeppink2": (238, 18, 137, 255), + "deeppink3": (205, 16, 118, 255), + "deeppink4": (139, 10, 80, 255), + "deepskyblue": (0, 191, 255, 255), + "deepskyblue1": (0, 191, 255, 255), + "deepskyblue2": (0, 178, 238, 255), + "deepskyblue3": (0, 154, 205, 255), + "deepskyblue4": (0, 104, 139, 255), + "dimgray": (105, 105, 105, 255), + "dimgrey": (105, 105, 105, 255), + "dodgerblue": (30, 144, 255, 255), + "dodgerblue1": (30, 144, 255, 255), + "dodgerblue2": (28, 134, 238, 255), + "dodgerblue3": (24, 116, 205, 255), + "dodgerblue4": (16, 78, 139, 255), + "firebrick": (178, 34, 34, 255), + "firebrick1": (255, 48, 48, 255), + "firebrick2": (238, 44, 44, 255), + "firebrick3": (205, 38, 38, 255), + "firebrick4": (139, 26, 26, 255), + "floralwhite": (255, 250, 240, 255), + "forestgreen": (34, 139, 34, 255), + "fuchsia": (255, 0, 255, 255), + "gainsboro": (220, 220, 220, 255), + "ghostwhite": (248, 248, 255, 255), + "gold": (255, 215, 0, 255), + "gold1": (255, 215, 0, 255), + "gold2": (238, 201, 0, 255), + "gold3": (205, 173, 0, 255), + "gold4": (139, 117, 0, 255), + "goldenrod": (218, 165, 32, 255), + "goldenrod1": (255, 193, 37, 255), + "goldenrod2": (238, 180, 34, 255), + "goldenrod3": (205, 155, 29, 255), + "goldenrod4": (139, 105, 20, 255), + "gray": (190, 190, 190, 255), + "gray0": (0, 0, 0, 255), + "gray1": (3, 3, 3, 255), + "gray2": (5, 5, 5, 255), + "gray3": (8, 8, 8, 255), + "gray4": (10, 10, 10, 255), + "gray5": (13, 13, 13, 255), + "gray6": (15, 15, 15, 255), + "gray7": (18, 18, 18, 255), + "gray8": (20, 20, 20, 255), + "gray9": (23, 23, 23, 255), + "gray10": (26, 26, 26, 255), + "gray11": (28, 28, 28, 255), + "gray12": (31, 31, 31, 255), + "gray13": (33, 33, 33, 255), + "gray14": (36, 36, 36, 255), + "gray15": (38, 38, 38, 255), + "gray16": (41, 41, 41, 255), + "gray17": (43, 43, 43, 255), + "gray18": (46, 46, 46, 255), + "gray19": (48, 48, 48, 255), + "gray20": (51, 51, 51, 255), + "gray21": (54, 54, 54, 255), + "gray22": (56, 56, 56, 255), + "gray23": (59, 59, 59, 255), + "gray24": (61, 61, 61, 255), + "gray25": (64, 64, 64, 255), + "gray26": (66, 66, 66, 255), + "gray27": (69, 69, 69, 255), + "gray28": (71, 71, 71, 255), + "gray29": (74, 74, 74, 255), + "gray30": (77, 77, 77, 255), + "gray31": (79, 79, 79, 255), + "gray32": (82, 82, 82, 255), + "gray33": (84, 84, 84, 255), + "gray34": (87, 87, 87, 255), + "gray35": (89, 89, 89, 255), + "gray36": (92, 92, 92, 255), + "gray37": (94, 94, 94, 255), + "gray38": (97, 97, 97, 255), + "gray39": (99, 99, 99, 255), + "gray40": (102, 102, 102, 255), + "gray41": (105, 105, 105, 255), + "gray42": (107, 107, 107, 255), + "gray43": (110, 110, 110, 255), + "gray44": (112, 112, 112, 255), + "gray45": (115, 115, 115, 255), + "gray46": (117, 117, 117, 255), + "gray47": (120, 120, 120, 255), + "gray48": (122, 122, 122, 255), + "gray49": (125, 125, 125, 255), + "gray50": (127, 127, 127, 255), + "gray51": (130, 130, 130, 255), + "gray52": (133, 133, 133, 255), + "gray53": (135, 135, 135, 255), + "gray54": (138, 138, 138, 255), + "gray55": (140, 140, 140, 255), + "gray56": (143, 143, 143, 255), + "gray57": (145, 145, 145, 255), + "gray58": (148, 148, 148, 255), + "gray59": (150, 150, 150, 255), + "gray60": (153, 153, 153, 255), + "gray61": (156, 156, 156, 255), + "gray62": (158, 158, 158, 255), + "gray63": (161, 161, 161, 255), + "gray64": (163, 163, 163, 255), + "gray65": (166, 166, 166, 255), + "gray66": (168, 168, 168, 255), + "gray67": (171, 171, 171, 255), + "gray68": (173, 173, 173, 255), + "gray69": (176, 176, 176, 255), + "gray70": (179, 179, 179, 255), + "gray71": (181, 181, 181, 255), + "gray72": (184, 184, 184, 255), + "gray73": (186, 186, 186, 255), + "gray74": (189, 189, 189, 255), + "gray75": (191, 191, 191, 255), + "gray76": (194, 194, 194, 255), + "gray77": (196, 196, 196, 255), + "gray78": (199, 199, 199, 255), + "gray79": (201, 201, 201, 255), + "gray80": (204, 204, 204, 255), + "gray81": (207, 207, 207, 255), + "gray82": (209, 209, 209, 255), + "gray83": (212, 212, 212, 255), + "gray84": (214, 214, 214, 255), + "gray85": (217, 217, 217, 255), + "gray86": (219, 219, 219, 255), + "gray87": (222, 222, 222, 255), + "gray88": (224, 224, 224, 255), + "gray89": (227, 227, 227, 255), + "gray90": (229, 229, 229, 255), + "gray91": (232, 232, 232, 255), + "gray92": (235, 235, 235, 255), + "gray93": (237, 237, 237, 255), + "gray94": (240, 240, 240, 255), + "gray95": (242, 242, 242, 255), + "gray96": (245, 245, 245, 255), + "gray97": (247, 247, 247, 255), + "gray98": (250, 250, 250, 255), + "gray99": (252, 252, 252, 255), + "gray100": (255, 255, 255, 255), + "green": (0, 255, 0, 255), + "green1": (0, 255, 0, 255), + "green2": (0, 238, 0, 255), + "green3": (0, 205, 0, 255), + "green4": (0, 139, 0, 255), + "greenyellow": (173, 255, 47, 255), + "grey": (190, 190, 190, 255), + "grey0": (0, 0, 0, 255), + "grey1": (3, 3, 3, 255), + "grey2": (5, 5, 5, 255), + "grey3": (8, 8, 8, 255), + "grey4": (10, 10, 10, 255), + "grey5": (13, 13, 13, 255), + "grey6": (15, 15, 15, 255), + "grey7": (18, 18, 18, 255), + "grey8": (20, 20, 20, 255), + "grey9": (23, 23, 23, 255), + "grey10": (26, 26, 26, 255), + "grey11": (28, 28, 28, 255), + "grey12": (31, 31, 31, 255), + "grey13": (33, 33, 33, 255), + "grey14": (36, 36, 36, 255), + "grey15": (38, 38, 38, 255), + "grey16": (41, 41, 41, 255), + "grey17": (43, 43, 43, 255), + "grey18": (46, 46, 46, 255), + "grey19": (48, 48, 48, 255), + "grey20": (51, 51, 51, 255), + "grey21": (54, 54, 54, 255), + "grey22": (56, 56, 56, 255), + "grey23": (59, 59, 59, 255), + "grey24": (61, 61, 61, 255), + "grey25": (64, 64, 64, 255), + "grey26": (66, 66, 66, 255), + "grey27": (69, 69, 69, 255), + "grey28": (71, 71, 71, 255), + "grey29": (74, 74, 74, 255), + "grey30": (77, 77, 77, 255), + "grey31": (79, 79, 79, 255), + "grey32": (82, 82, 82, 255), + "grey33": (84, 84, 84, 255), + "grey34": (87, 87, 87, 255), + "grey35": (89, 89, 89, 255), + "grey36": (92, 92, 92, 255), + "grey37": (94, 94, 94, 255), + "grey38": (97, 97, 97, 255), + "grey39": (99, 99, 99, 255), + "grey40": (102, 102, 102, 255), + "grey41": (105, 105, 105, 255), + "grey42": (107, 107, 107, 255), + "grey43": (110, 110, 110, 255), + "grey44": (112, 112, 112, 255), + "grey45": (115, 115, 115, 255), + "grey46": (117, 117, 117, 255), + "grey47": (120, 120, 120, 255), + "grey48": (122, 122, 122, 255), + "grey49": (125, 125, 125, 255), + "grey50": (127, 127, 127, 255), + "grey51": (130, 130, 130, 255), + "grey52": (133, 133, 133, 255), + "grey53": (135, 135, 135, 255), + "grey54": (138, 138, 138, 255), + "grey55": (140, 140, 140, 255), + "grey56": (143, 143, 143, 255), + "grey57": (145, 145, 145, 255), + "grey58": (148, 148, 148, 255), + "grey59": (150, 150, 150, 255), + "grey60": (153, 153, 153, 255), + "grey61": (156, 156, 156, 255), + "grey62": (158, 158, 158, 255), + "grey63": (161, 161, 161, 255), + "grey64": (163, 163, 163, 255), + "grey65": (166, 166, 166, 255), + "grey66": (168, 168, 168, 255), + "grey67": (171, 171, 171, 255), + "grey68": (173, 173, 173, 255), + "grey69": (176, 176, 176, 255), + "grey70": (179, 179, 179, 255), + "grey71": (181, 181, 181, 255), + "grey72": (184, 184, 184, 255), + "grey73": (186, 186, 186, 255), + "grey74": (189, 189, 189, 255), + "grey75": (191, 191, 191, 255), + "grey76": (194, 194, 194, 255), + "grey77": (196, 196, 196, 255), + "grey78": (199, 199, 199, 255), + "grey79": (201, 201, 201, 255), + "grey80": (204, 204, 204, 255), + "grey81": (207, 207, 207, 255), + "grey82": (209, 209, 209, 255), + "grey83": (212, 212, 212, 255), + "grey84": (214, 214, 214, 255), + "grey85": (217, 217, 217, 255), + "grey86": (219, 219, 219, 255), + "grey87": (222, 222, 222, 255), + "grey88": (224, 224, 224, 255), + "grey89": (227, 227, 227, 255), + "grey90": (229, 229, 229, 255), + "grey91": (232, 232, 232, 255), + "grey92": (235, 235, 235, 255), + "grey93": (237, 237, 237, 255), + "grey94": (240, 240, 240, 255), + "grey95": (242, 242, 242, 255), + "grey96": (245, 245, 245, 255), + "grey97": (247, 247, 247, 255), + "grey98": (250, 250, 250, 255), + "grey99": (252, 252, 252, 255), + "grey100": (255, 255, 255, 255), + "honeydew": (240, 255, 240, 255), + "honeydew1": (240, 255, 240, 255), + "honeydew2": (224, 238, 224, 255), + "honeydew3": (193, 205, 193, 255), + "honeydew4": (131, 139, 131, 255), + "hotpink": (255, 105, 180, 255), + "hotpink1": (255, 110, 180, 255), + "hotpink2": (238, 106, 167, 255), + "hotpink3": (205, 96, 144, 255), + "hotpink4": (139, 58, 98, 255), + "indianred": (205, 92, 92, 255), + "indianred1": (255, 106, 106, 255), + "indianred2": (238, 99, 99, 255), + "indianred3": (205, 85, 85, 255), + "indianred4": (139, 58, 58, 255), + "indigo": (75, 0, 130, 255), + "ivory": (255, 255, 240, 255), + "ivory1": (255, 255, 240, 255), + "ivory2": (238, 238, 224, 255), + "ivory3": (205, 205, 193, 255), + "ivory4": (139, 139, 131, 255), + "khaki": (240, 230, 140, 255), + "khaki1": (255, 246, 143, 255), + "khaki2": (238, 230, 133, 255), + "khaki3": (205, 198, 115, 255), + "khaki4": (139, 134, 78, 255), + "lavender": (230, 230, 250, 255), + "lavenderblush": (255, 240, 245, 255), + "lavenderblush1": (255, 240, 245, 255), + "lavenderblush2": (238, 224, 229, 255), + "lavenderblush3": (205, 193, 197, 255), + "lavenderblush4": (139, 131, 134, 255), + "lawngreen": (124, 252, 0, 255), + "lemonchiffon": (255, 250, 205, 255), + "lemonchiffon1": (255, 250, 205, 255), + "lemonchiffon2": (238, 233, 191, 255), + "lemonchiffon3": (205, 201, 165, 255), + "lemonchiffon4": (139, 137, 112, 255), + "lightblue": (173, 216, 230, 255), + "lightblue1": (191, 239, 255, 255), + "lightblue2": (178, 223, 238, 255), + "lightblue3": (154, 192, 205, 255), + "lightblue4": (104, 131, 139, 255), + "lightcoral": (240, 128, 128, 255), + "lightcyan": (224, 255, 255, 255), + "lightcyan1": (224, 255, 255, 255), + "lightcyan2": (209, 238, 238, 255), + "lightcyan3": (180, 205, 205, 255), + "lightcyan4": (122, 139, 139, 255), + "lightgoldenrod": (238, 221, 130, 255), + "lightgoldenrod1": (255, 236, 139, 255), + "lightgoldenrod2": (238, 220, 130, 255), + "lightgoldenrod3": (205, 190, 112, 255), + "lightgoldenrod4": (139, 129, 76, 255), + "lightgoldenrodyellow": (250, 250, 210, 255), + "lightgray": (211, 211, 211, 255), + "lightgreen": (144, 238, 144, 255), + "lightgrey": (211, 211, 211, 255), + "lightpink": (255, 182, 193, 255), + "lightpink1": (255, 174, 185, 255), + "lightpink2": (238, 162, 173, 255), + "lightpink3": (205, 140, 149, 255), + "lightpink4": (139, 95, 101, 255), + "lightsalmon": (255, 160, 122, 255), + "lightsalmon1": (255, 160, 122, 255), + "lightsalmon2": (238, 149, 114, 255), + "lightsalmon3": (205, 129, 98, 255), + "lightsalmon4": (139, 87, 66, 255), + "lightseagreen": (32, 178, 170, 255), + "lightskyblue": (135, 206, 250, 255), + "lightskyblue1": (176, 226, 255, 255), + "lightskyblue2": (164, 211, 238, 255), + "lightskyblue3": (141, 182, 205, 255), + "lightskyblue4": (96, 123, 139, 255), + "lightslateblue": (132, 112, 255, 255), + "lightslategray": (119, 136, 153, 255), + "lightslategrey": (119, 136, 153, 255), + "lightsteelblue": (176, 196, 222, 255), + "lightsteelblue1": (202, 225, 255, 255), + "lightsteelblue2": (188, 210, 238, 255), + "lightsteelblue3": (162, 181, 205, 255), + "lightsteelblue4": (110, 123, 139, 255), + "lightyellow": (255, 255, 224, 255), + "lightyellow1": (255, 255, 224, 255), + "lightyellow2": (238, 238, 209, 255), + "lightyellow3": (205, 205, 180, 255), + "lightyellow4": (139, 139, 122, 255), + "linen": (250, 240, 230, 255), + "lime": (0, 255, 0, 255), + "limegreen": (50, 205, 50, 255), + "magenta": (255, 0, 255, 255), + "magenta1": (255, 0, 255, 255), + "magenta2": (238, 0, 238, 255), + "magenta3": (205, 0, 205, 255), + "magenta4": (139, 0, 139, 255), + "maroon": (176, 48, 96, 255), + "maroon1": (255, 52, 179, 255), + "maroon2": (238, 48, 167, 255), + "maroon3": (205, 41, 144, 255), + "maroon4": (139, 28, 98, 255), + "mediumaquamarine": (102, 205, 170, 255), + "mediumblue": (0, 0, 205, 255), + "mediumorchid": (186, 85, 211, 255), + "mediumorchid1": (224, 102, 255, 255), + "mediumorchid2": (209, 95, 238, 255), + "mediumorchid3": (180, 82, 205, 255), + "mediumorchid4": (122, 55, 139, 255), + "mediumpurple": (147, 112, 219, 255), + "mediumpurple1": (171, 130, 255, 255), + "mediumpurple2": (159, 121, 238, 255), + "mediumpurple3": (137, 104, 205, 255), + "mediumpurple4": (93, 71, 139, 255), + "mediumseagreen": (60, 179, 113, 255), + "mediumslateblue": (123, 104, 238, 255), + "mediumspringgreen": (0, 250, 154, 255), + "mediumturquoise": (72, 209, 204, 255), + "mediumvioletred": (199, 21, 133, 255), + "midnightblue": (25, 25, 112, 255), + "mintcream": (245, 255, 250, 255), + "mistyrose": (255, 228, 225, 255), + "mistyrose1": (255, 228, 225, 255), + "mistyrose2": (238, 213, 210, 255), + "mistyrose3": (205, 183, 181, 255), + "mistyrose4": (139, 125, 123, 255), + "moccasin": (255, 228, 181, 255), + "navajowhite": (255, 222, 173, 255), + "navajowhite1": (255, 222, 173, 255), + "navajowhite2": (238, 207, 161, 255), + "navajowhite3": (205, 179, 139, 255), + "navajowhite4": (139, 121, 94, 255), + "navy": (0, 0, 128, 255), + "navyblue": (0, 0, 128, 255), + "oldlace": (253, 245, 230, 255), + "olive": (128, 128, 0, 255), + "olivedrab": (107, 142, 35, 255), + "olivedrab1": (192, 255, 62, 255), + "olivedrab2": (179, 238, 58, 255), + "olivedrab3": (154, 205, 50, 255), + "olivedrab4": (105, 139, 34, 255), + "orange": (255, 165, 0, 255), + "orange1": (255, 165, 0, 255), + "orange2": (238, 154, 0, 255), + "orange3": (205, 133, 0, 255), + "orange4": (139, 90, 0, 255), + "orangered": (255, 69, 0, 255), + "orangered1": (255, 69, 0, 255), + "orangered2": (238, 64, 0, 255), + "orangered3": (205, 55, 0, 255), + "orangered4": (139, 37, 0, 255), + "orchid": (218, 112, 214, 255), + "orchid1": (255, 131, 250, 255), + "orchid2": (238, 122, 233, 255), + "orchid3": (205, 105, 201, 255), + "orchid4": (139, 71, 137, 255), + "palegreen": (152, 251, 152, 255), + "palegreen1": (154, 255, 154, 255), + "palegreen2": (144, 238, 144, 255), + "palegreen3": (124, 205, 124, 255), + "palegreen4": (84, 139, 84, 255), + "palegoldenrod": (238, 232, 170, 255), + "paleturquoise": (175, 238, 238, 255), + "paleturquoise1": (187, 255, 255, 255), + "paleturquoise2": (174, 238, 238, 255), + "paleturquoise3": (150, 205, 205, 255), + "paleturquoise4": (102, 139, 139, 255), + "palevioletred": (219, 112, 147, 255), + "palevioletred1": (255, 130, 171, 255), + "palevioletred2": (238, 121, 159, 255), + "palevioletred3": (205, 104, 137, 255), + "palevioletred4": (139, 71, 93, 255), + "papayawhip": (255, 239, 213, 255), + "peachpuff": (255, 218, 185, 255), + "peachpuff1": (255, 218, 185, 255), + "peachpuff2": (238, 203, 173, 255), + "peachpuff3": (205, 175, 149, 255), + "peachpuff4": (139, 119, 101, 255), + "peru": (205, 133, 63, 255), + "pink": (255, 192, 203, 255), + "pink1": (255, 181, 197, 255), + "pink2": (238, 169, 184, 255), + "pink3": (205, 145, 158, 255), + "pink4": (139, 99, 108, 255), + "plum": (221, 160, 221, 255), + "plum1": (255, 187, 255, 255), + "plum2": (238, 174, 238, 255), + "plum3": (205, 150, 205, 255), + "plum4": (139, 102, 139, 255), + "powderblue": (176, 224, 230, 255), + "purple": (160, 32, 240, 255), + "purple1": (155, 48, 255, 255), + "purple2": (145, 44, 238, 255), + "purple3": (125, 38, 205, 255), + "purple4": (85, 26, 139, 255), + "red": (255, 0, 0, 255), + "red1": (255, 0, 0, 255), + "red2": (238, 0, 0, 255), + "red3": (205, 0, 0, 255), + "red4": (139, 0, 0, 255), + "rosybrown": (188, 143, 143, 255), + "rosybrown1": (255, 193, 193, 255), + "rosybrown2": (238, 180, 180, 255), + "rosybrown3": (205, 155, 155, 255), + "rosybrown4": (139, 105, 105, 255), + "royalblue": (65, 105, 225, 255), + "royalblue1": (72, 118, 255, 255), + "royalblue2": (67, 110, 238, 255), + "royalblue3": (58, 95, 205, 255), + "royalblue4": (39, 64, 139, 255), + "salmon": (250, 128, 114, 255), + "salmon1": (255, 140, 105, 255), + "salmon2": (238, 130, 98, 255), + "salmon3": (205, 112, 84, 255), + "salmon4": (139, 76, 57, 255), + "saddlebrown": (139, 69, 19, 255), + "sandybrown": (244, 164, 96, 255), + "seagreen": (46, 139, 87, 255), + "seagreen1": (84, 255, 159, 255), + "seagreen2": (78, 238, 148, 255), + "seagreen3": (67, 205, 128, 255), + "seagreen4": (46, 139, 87, 255), + "seashell": (255, 245, 238, 255), + "seashell1": (255, 245, 238, 255), + "seashell2": (238, 229, 222, 255), + "seashell3": (205, 197, 191, 255), + "seashell4": (139, 134, 130, 255), + "sienna": (160, 82, 45, 255), + "sienna1": (255, 130, 71, 255), + "sienna2": (238, 121, 66, 255), + "sienna3": (205, 104, 57, 255), + "sienna4": (139, 71, 38, 255), + "silver": (192, 192, 192, 255), + "skyblue": (135, 206, 235, 255), + "skyblue1": (135, 206, 255, 255), + "skyblue2": (126, 192, 238, 255), + "skyblue3": (108, 166, 205, 255), + "skyblue4": (74, 112, 139, 255), + "slateblue": (106, 90, 205, 255), + "slateblue1": (131, 111, 255, 255), + "slateblue2": (122, 103, 238, 255), + "slateblue3": (105, 89, 205, 255), + "slateblue4": (71, 60, 139, 255), + "slategray": (112, 128, 144, 255), + "slategray1": (198, 226, 255, 255), + "slategray2": (185, 211, 238, 255), + "slategray3": (159, 182, 205, 255), + "slategray4": (108, 123, 139, 255), + "slategrey": (112, 128, 144, 255), + "snow": (255, 250, 250, 255), + "snow1": (255, 250, 250, 255), + "snow2": (238, 233, 233, 255), + "snow3": (205, 201, 201, 255), + "snow4": (139, 137, 137, 255), + "springgreen": (0, 255, 127, 255), + "springgreen1": (0, 255, 127, 255), + "springgreen2": (0, 238, 118, 255), + "springgreen3": (0, 205, 102, 255), + "springgreen4": (0, 139, 69, 255), + "steelblue": (70, 130, 180, 255), + "steelblue1": (99, 184, 255, 255), + "steelblue2": (92, 172, 238, 255), + "steelblue3": (79, 148, 205, 255), + "steelblue4": (54, 100, 139, 255), + "tan": (210, 180, 140, 255), + "tan1": (255, 165, 79, 255), + "tan2": (238, 154, 73, 255), + "tan3": (205, 133, 63, 255), + "tan4": (139, 90, 43, 255), + "teal": (0, 128, 128, 255), + "thistle": (216, 191, 216, 255), + "thistle1": (255, 225, 255, 255), + "thistle2": (238, 210, 238, 255), + "thistle3": (205, 181, 205, 255), + "thistle4": (139, 123, 139, 255), + "tomato": (255, 99, 71, 255), + "tomato1": (255, 99, 71, 255), + "tomato2": (238, 92, 66, 255), + "tomato3": (205, 79, 57, 255), + "tomato4": (139, 54, 38, 255), + "turquoise": (64, 224, 208, 255), + "turquoise1": (0, 245, 255, 255), + "turquoise2": (0, 229, 238, 255), + "turquoise3": (0, 197, 205, 255), + "turquoise4": (0, 134, 139, 255), + "violet": (238, 130, 238, 255), + "violetred": (208, 32, 144, 255), + "violetred1": (255, 62, 150, 255), + "violetred2": (238, 58, 140, 255), + "violetred3": (205, 50, 120, 255), + "violetred4": (139, 34, 82, 255), + "wheat": (245, 222, 179, 255), + "wheat1": (255, 231, 186, 255), + "wheat2": (238, 216, 174, 255), + "wheat3": (205, 186, 150, 255), + "wheat4": (139, 126, 102, 255), + "white": (255, 255, 255, 255), + "whitesmoke": (245, 245, 245, 255), + "yellow": (255, 255, 0, 255), + "yellow1": (255, 255, 0, 255), + "yellow2": (238, 238, 0, 255), + "yellow3": (205, 205, 0, 255), + "yellow4": (139, 139, 0, 255), + "yellowgreen": (154, 205, 50, 255), +} diff --git a/.venv/Lib/site-packages/pygame/constants.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/constants.cp311-win_amd64.pyd new file mode 100644 index 00000000..24fcf4b5 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/constants.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/constants.pyi b/.venv/Lib/site-packages/pygame/constants.pyi new file mode 100644 index 00000000..07b2d465 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/constants.pyi @@ -0,0 +1,560 @@ +# buildconfig/stubs/gen_stubs.py +# A script to auto-generate locals.pyi, constants.pyi and __init__.pyi typestubs +# IMPORTANT NOTE: Do not edit this file by hand! + +ACTIVEEVENT: int +ANYFORMAT: int +APPACTIVE: int +APPINPUTFOCUS: int +APPMOUSEFOCUS: int +APP_DIDENTERBACKGROUND: int +APP_DIDENTERFOREGROUND: int +APP_LOWMEMORY: int +APP_TERMINATING: int +APP_WILLENTERBACKGROUND: int +APP_WILLENTERFOREGROUND: int +ASYNCBLIT: int +AUDIODEVICEADDED: int +AUDIODEVICEREMOVED: int +AUDIO_ALLOW_ANY_CHANGE: int +AUDIO_ALLOW_CHANNELS_CHANGE: int +AUDIO_ALLOW_FORMAT_CHANGE: int +AUDIO_ALLOW_FREQUENCY_CHANGE: int +AUDIO_S16: int +AUDIO_S16LSB: int +AUDIO_S16MSB: int +AUDIO_S16SYS: int +AUDIO_S8: int +AUDIO_U16: int +AUDIO_U16LSB: int +AUDIO_U16MSB: int +AUDIO_U16SYS: int +AUDIO_U8: int +BIG_ENDIAN: int +BLENDMODE_ADD: int +BLENDMODE_BLEND: int +BLENDMODE_MOD: int +BLENDMODE_NONE: int +BLEND_ADD: int +BLEND_ALPHA_SDL2: int +BLEND_MAX: int +BLEND_MIN: int +BLEND_MULT: int +BLEND_PREMULTIPLIED: int +BLEND_RGBA_ADD: int +BLEND_RGBA_MAX: int +BLEND_RGBA_MIN: int +BLEND_RGBA_MULT: int +BLEND_RGBA_SUB: int +BLEND_RGB_ADD: int +BLEND_RGB_MAX: int +BLEND_RGB_MIN: int +BLEND_RGB_MULT: int +BLEND_RGB_SUB: int +BLEND_SUB: int +BUTTON_LEFT: int +BUTTON_MIDDLE: int +BUTTON_RIGHT: int +BUTTON_WHEELDOWN: int +BUTTON_WHEELUP: int +BUTTON_X1: int +BUTTON_X2: int +CLIPBOARDUPDATE: int +CONTROLLERAXISMOTION: int +CONTROLLERBUTTONDOWN: int +CONTROLLERBUTTONUP: int +CONTROLLERDEVICEADDED: int +CONTROLLERDEVICEREMAPPED: int +CONTROLLERDEVICEREMOVED: int +CONTROLLERSENSORUPDATE: int +CONTROLLERTOUCHPADDOWN: int +CONTROLLERTOUCHPADMOTION: int +CONTROLLERTOUCHPADUP: int +CONTROLLER_AXIS_INVALID: int +CONTROLLER_AXIS_LEFTX: int +CONTROLLER_AXIS_LEFTY: int +CONTROLLER_AXIS_MAX: int +CONTROLLER_AXIS_RIGHTX: int +CONTROLLER_AXIS_RIGHTY: int +CONTROLLER_AXIS_TRIGGERLEFT: int +CONTROLLER_AXIS_TRIGGERRIGHT: int +CONTROLLER_BUTTON_A: int +CONTROLLER_BUTTON_B: int +CONTROLLER_BUTTON_BACK: int +CONTROLLER_BUTTON_DPAD_DOWN: int +CONTROLLER_BUTTON_DPAD_LEFT: int +CONTROLLER_BUTTON_DPAD_RIGHT: int +CONTROLLER_BUTTON_DPAD_UP: int +CONTROLLER_BUTTON_GUIDE: int +CONTROLLER_BUTTON_INVALID: int +CONTROLLER_BUTTON_LEFTSHOULDER: int +CONTROLLER_BUTTON_LEFTSTICK: int +CONTROLLER_BUTTON_MAX: int +CONTROLLER_BUTTON_RIGHTSHOULDER: int +CONTROLLER_BUTTON_RIGHTSTICK: int +CONTROLLER_BUTTON_START: int +CONTROLLER_BUTTON_X: int +CONTROLLER_BUTTON_Y: int +DOUBLEBUF: int +DROPBEGIN: int +DROPCOMPLETE: int +DROPFILE: int +DROPTEXT: int +FINGERDOWN: int +FINGERMOTION: int +FINGERUP: int +FULLSCREEN: int +GL_ACCELERATED_VISUAL: int +GL_ACCUM_ALPHA_SIZE: int +GL_ACCUM_BLUE_SIZE: int +GL_ACCUM_GREEN_SIZE: int +GL_ACCUM_RED_SIZE: int +GL_ALPHA_SIZE: int +GL_BLUE_SIZE: int +GL_BUFFER_SIZE: int +GL_CONTEXT_DEBUG_FLAG: int +GL_CONTEXT_FLAGS: int +GL_CONTEXT_FORWARD_COMPATIBLE_FLAG: int +GL_CONTEXT_MAJOR_VERSION: int +GL_CONTEXT_MINOR_VERSION: int +GL_CONTEXT_PROFILE_COMPATIBILITY: int +GL_CONTEXT_PROFILE_CORE: int +GL_CONTEXT_PROFILE_ES: int +GL_CONTEXT_PROFILE_MASK: int +GL_CONTEXT_RELEASE_BEHAVIOR: int +GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH: int +GL_CONTEXT_RELEASE_BEHAVIOR_NONE: int +GL_CONTEXT_RESET_ISOLATION_FLAG: int +GL_CONTEXT_ROBUST_ACCESS_FLAG: int +GL_DEPTH_SIZE: int +GL_DOUBLEBUFFER: int +GL_FRAMEBUFFER_SRGB_CAPABLE: int +GL_GREEN_SIZE: int +GL_MULTISAMPLEBUFFERS: int +GL_MULTISAMPLESAMPLES: int +GL_RED_SIZE: int +GL_SHARE_WITH_CURRENT_CONTEXT: int +GL_STENCIL_SIZE: int +GL_STEREO: int +GL_SWAP_CONTROL: int +HAT_CENTERED: int +HAT_DOWN: int +HAT_LEFT: int +HAT_LEFTDOWN: int +HAT_LEFTUP: int +HAT_RIGHT: int +HAT_RIGHTDOWN: int +HAT_RIGHTUP: int +HAT_UP: int +HIDDEN: int +HWACCEL: int +HWPALETTE: int +HWSURFACE: int +JOYAXISMOTION: int +JOYBALLMOTION: int +JOYBUTTONDOWN: int +JOYBUTTONUP: int +JOYDEVICEADDED: int +JOYDEVICEREMOVED: int +JOYHATMOTION: int +KEYDOWN: int +KEYMAPCHANGED: int +KEYUP: int +KMOD_ALT: int +KMOD_CAPS: int +KMOD_CTRL: int +KMOD_GUI: int +KMOD_LALT: int +KMOD_LCTRL: int +KMOD_LGUI: int +KMOD_LMETA: int +KMOD_LSHIFT: int +KMOD_META: int +KMOD_MODE: int +KMOD_NONE: int +KMOD_NUM: int +KMOD_RALT: int +KMOD_RCTRL: int +KMOD_RGUI: int +KMOD_RMETA: int +KMOD_RSHIFT: int +KMOD_SHIFT: int +KSCAN_0: int +KSCAN_1: int +KSCAN_2: int +KSCAN_3: int +KSCAN_4: int +KSCAN_5: int +KSCAN_6: int +KSCAN_7: int +KSCAN_8: int +KSCAN_9: int +KSCAN_A: int +KSCAN_AC_BACK: int +KSCAN_APOSTROPHE: int +KSCAN_B: int +KSCAN_BACKSLASH: int +KSCAN_BACKSPACE: int +KSCAN_BREAK: int +KSCAN_C: int +KSCAN_CAPSLOCK: int +KSCAN_CLEAR: int +KSCAN_COMMA: int +KSCAN_CURRENCYSUBUNIT: int +KSCAN_CURRENCYUNIT: int +KSCAN_D: int +KSCAN_DELETE: int +KSCAN_DOWN: int +KSCAN_E: int +KSCAN_END: int +KSCAN_EQUALS: int +KSCAN_ESCAPE: int +KSCAN_EURO: int +KSCAN_F: int +KSCAN_F1: int +KSCAN_F10: int +KSCAN_F11: int +KSCAN_F12: int +KSCAN_F13: int +KSCAN_F14: int +KSCAN_F15: int +KSCAN_F2: int +KSCAN_F3: int +KSCAN_F4: int +KSCAN_F5: int +KSCAN_F6: int +KSCAN_F7: int +KSCAN_F8: int +KSCAN_F9: int +KSCAN_G: int +KSCAN_GRAVE: int +KSCAN_H: int +KSCAN_HELP: int +KSCAN_HOME: int +KSCAN_I: int +KSCAN_INSERT: int +KSCAN_INTERNATIONAL1: int +KSCAN_INTERNATIONAL2: int +KSCAN_INTERNATIONAL3: int +KSCAN_INTERNATIONAL4: int +KSCAN_INTERNATIONAL5: int +KSCAN_INTERNATIONAL6: int +KSCAN_INTERNATIONAL7: int +KSCAN_INTERNATIONAL8: int +KSCAN_INTERNATIONAL9: int +KSCAN_J: int +KSCAN_K: int +KSCAN_KP0: int +KSCAN_KP1: int +KSCAN_KP2: int +KSCAN_KP3: int +KSCAN_KP4: int +KSCAN_KP5: int +KSCAN_KP6: int +KSCAN_KP7: int +KSCAN_KP8: int +KSCAN_KP9: int +KSCAN_KP_0: int +KSCAN_KP_1: int +KSCAN_KP_2: int +KSCAN_KP_3: int +KSCAN_KP_4: int +KSCAN_KP_5: int +KSCAN_KP_6: int +KSCAN_KP_7: int +KSCAN_KP_8: int +KSCAN_KP_9: int +KSCAN_KP_DIVIDE: int +KSCAN_KP_ENTER: int +KSCAN_KP_EQUALS: int +KSCAN_KP_MINUS: int +KSCAN_KP_MULTIPLY: int +KSCAN_KP_PERIOD: int +KSCAN_KP_PLUS: int +KSCAN_L: int +KSCAN_LALT: int +KSCAN_LANG1: int +KSCAN_LANG2: int +KSCAN_LANG3: int +KSCAN_LANG4: int +KSCAN_LANG5: int +KSCAN_LANG6: int +KSCAN_LANG7: int +KSCAN_LANG8: int +KSCAN_LANG9: int +KSCAN_LCTRL: int +KSCAN_LEFT: int +KSCAN_LEFTBRACKET: int +KSCAN_LGUI: int +KSCAN_LMETA: int +KSCAN_LSHIFT: int +KSCAN_LSUPER: int +KSCAN_M: int +KSCAN_MENU: int +KSCAN_MINUS: int +KSCAN_MODE: int +KSCAN_N: int +KSCAN_NONUSBACKSLASH: int +KSCAN_NONUSHASH: int +KSCAN_NUMLOCK: int +KSCAN_NUMLOCKCLEAR: int +KSCAN_O: int +KSCAN_P: int +KSCAN_PAGEDOWN: int +KSCAN_PAGEUP: int +KSCAN_PAUSE: int +KSCAN_PERIOD: int +KSCAN_POWER: int +KSCAN_PRINT: int +KSCAN_PRINTSCREEN: int +KSCAN_Q: int +KSCAN_R: int +KSCAN_RALT: int +KSCAN_RCTRL: int +KSCAN_RETURN: int +KSCAN_RGUI: int +KSCAN_RIGHT: int +KSCAN_RIGHTBRACKET: int +KSCAN_RMETA: int +KSCAN_RSHIFT: int +KSCAN_RSUPER: int +KSCAN_S: int +KSCAN_SCROLLLOCK: int +KSCAN_SCROLLOCK: int +KSCAN_SEMICOLON: int +KSCAN_SLASH: int +KSCAN_SPACE: int +KSCAN_SYSREQ: int +KSCAN_T: int +KSCAN_TAB: int +KSCAN_U: int +KSCAN_UNKNOWN: int +KSCAN_UP: int +KSCAN_V: int +KSCAN_W: int +KSCAN_X: int +KSCAN_Y: int +KSCAN_Z: int +K_0: int +K_1: int +K_2: int +K_3: int +K_4: int +K_5: int +K_6: int +K_7: int +K_8: int +K_9: int +K_AC_BACK: int +K_AMPERSAND: int +K_ASTERISK: int +K_AT: int +K_BACKQUOTE: int +K_BACKSLASH: int +K_BACKSPACE: int +K_BREAK: int +K_CAPSLOCK: int +K_CARET: int +K_CLEAR: int +K_COLON: int +K_COMMA: int +K_CURRENCYSUBUNIT: int +K_CURRENCYUNIT: int +K_DELETE: int +K_DOLLAR: int +K_DOWN: int +K_END: int +K_EQUALS: int +K_ESCAPE: int +K_EURO: int +K_EXCLAIM: int +K_F1: int +K_F10: int +K_F11: int +K_F12: int +K_F13: int +K_F14: int +K_F15: int +K_F2: int +K_F3: int +K_F4: int +K_F5: int +K_F6: int +K_F7: int +K_F8: int +K_F9: int +K_GREATER: int +K_HASH: int +K_HELP: int +K_HOME: int +K_INSERT: int +K_KP0: int +K_KP1: int +K_KP2: int +K_KP3: int +K_KP4: int +K_KP5: int +K_KP6: int +K_KP7: int +K_KP8: int +K_KP9: int +K_KP_0: int +K_KP_1: int +K_KP_2: int +K_KP_3: int +K_KP_4: int +K_KP_5: int +K_KP_6: int +K_KP_7: int +K_KP_8: int +K_KP_9: int +K_KP_DIVIDE: int +K_KP_ENTER: int +K_KP_EQUALS: int +K_KP_MINUS: int +K_KP_MULTIPLY: int +K_KP_PERIOD: int +K_KP_PLUS: int +K_LALT: int +K_LCTRL: int +K_LEFT: int +K_LEFTBRACKET: int +K_LEFTPAREN: int +K_LESS: int +K_LGUI: int +K_LMETA: int +K_LSHIFT: int +K_LSUPER: int +K_MENU: int +K_MINUS: int +K_MODE: int +K_NUMLOCK: int +K_NUMLOCKCLEAR: int +K_PAGEDOWN: int +K_PAGEUP: int +K_PAUSE: int +K_PERCENT: int +K_PERIOD: int +K_PLUS: int +K_POWER: int +K_PRINT: int +K_PRINTSCREEN: int +K_QUESTION: int +K_QUOTE: int +K_QUOTEDBL: int +K_RALT: int +K_RCTRL: int +K_RETURN: int +K_RGUI: int +K_RIGHT: int +K_RIGHTBRACKET: int +K_RIGHTPAREN: int +K_RMETA: int +K_RSHIFT: int +K_RSUPER: int +K_SCROLLLOCK: int +K_SCROLLOCK: int +K_SEMICOLON: int +K_SLASH: int +K_SPACE: int +K_SYSREQ: int +K_TAB: int +K_UNDERSCORE: int +K_UNKNOWN: int +K_UP: int +K_a: int +K_b: int +K_c: int +K_d: int +K_e: int +K_f: int +K_g: int +K_h: int +K_i: int +K_j: int +K_k: int +K_l: int +K_m: int +K_n: int +K_o: int +K_p: int +K_q: int +K_r: int +K_s: int +K_t: int +K_u: int +K_v: int +K_w: int +K_x: int +K_y: int +K_z: int +LIL_ENDIAN: int +LOCALECHANGED: int +MIDIIN: int +MIDIOUT: int +MOUSEBUTTONDOWN: int +MOUSEBUTTONUP: int +MOUSEMOTION: int +MOUSEWHEEL: int +MULTIGESTURE: int +NOEVENT: int +NOFRAME: int +NUMEVENTS: int +OPENGL: int +OPENGLBLIT: int +PREALLOC: int +QUIT: int +RENDER_DEVICE_RESET: int +RENDER_TARGETS_RESET: int +RESIZABLE: int +RLEACCEL: int +RLEACCELOK: int +SCALED: int +SCRAP_BMP: str +SCRAP_CLIPBOARD: int +SCRAP_PBM: str +SCRAP_PPM: str +SCRAP_SELECTION: int +SCRAP_TEXT: str +SHOWN: int +SRCALPHA: int +SRCCOLORKEY: int +SWSURFACE: int +SYSTEM_CURSOR_ARROW: int +SYSTEM_CURSOR_CROSSHAIR: int +SYSTEM_CURSOR_HAND: int +SYSTEM_CURSOR_IBEAM: int +SYSTEM_CURSOR_NO: int +SYSTEM_CURSOR_SIZEALL: int +SYSTEM_CURSOR_SIZENESW: int +SYSTEM_CURSOR_SIZENS: int +SYSTEM_CURSOR_SIZENWSE: int +SYSTEM_CURSOR_SIZEWE: int +SYSTEM_CURSOR_WAIT: int +SYSTEM_CURSOR_WAITARROW: int +SYSWMEVENT: int +TEXTEDITING: int +TEXTINPUT: int +TIMER_RESOLUTION: int +USEREVENT: int +USEREVENT_DROPFILE: int +VIDEOEXPOSE: int +VIDEORESIZE: int +WINDOWCLOSE: int +WINDOWDISPLAYCHANGED: int +WINDOWENTER: int +WINDOWEXPOSED: int +WINDOWFOCUSGAINED: int +WINDOWFOCUSLOST: int +WINDOWHIDDEN: int +WINDOWHITTEST: int +WINDOWICCPROFCHANGED: int +WINDOWLEAVE: int +WINDOWMAXIMIZED: int +WINDOWMINIMIZED: int +WINDOWMOVED: int +WINDOWRESIZED: int +WINDOWRESTORED: int +WINDOWSHOWN: int +WINDOWSIZECHANGED: int +WINDOWTAKEFOCUS: int diff --git a/.venv/Lib/site-packages/pygame/cursors.py b/.venv/Lib/site-packages/pygame/cursors.py new file mode 100644 index 00000000..0ba3ac42 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/cursors.py @@ -0,0 +1,844 @@ +# pygame - Python Game Library +# Copyright (C) 2000-2003 Pete Shinners +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Pete Shinners +# pete@shinners.org + +"""Set of cursor resources available for use. These cursors come +in a sequence of values that are needed as the arguments for +pygame.mouse.set_cursor(). To dereference the sequence in place +and create the cursor in one step, call like this: + pygame.mouse.set_cursor(*pygame.cursors.arrow). + +Here is a list of available cursors: + arrow, diamond, ball, broken_x, tri_left, tri_right + +There is also a sample string cursor named 'thickarrow_strings'. +The compile() function can convert these string cursors into cursor byte data that can be used to +create Cursor objects. + +Alternately, you can also create Cursor objects using surfaces or cursors constants, +such as pygame.SYSTEM_CURSOR_ARROW. +""" + +import pygame + +_cursor_id_table = { + pygame.SYSTEM_CURSOR_ARROW: "SYSTEM_CURSOR_ARROW", + pygame.SYSTEM_CURSOR_IBEAM: "SYSTEM_CURSOR_IBEAM", + pygame.SYSTEM_CURSOR_WAIT: "SYSTEM_CURSOR_WAIT", + pygame.SYSTEM_CURSOR_CROSSHAIR: "SYSTEM_CURSOR_CROSSHAIR", + pygame.SYSTEM_CURSOR_WAITARROW: "SYSTEM_CURSOR_WAITARROW", + pygame.SYSTEM_CURSOR_SIZENWSE: "SYSTEM_CURSOR_SIZENWSE", + pygame.SYSTEM_CURSOR_SIZENESW: "SYSTEM_CURSOR_SIZENESW", + pygame.SYSTEM_CURSOR_SIZEWE: "SYSTEM_CURSOR_SIZEWE", + pygame.SYSTEM_CURSOR_SIZENS: "SYSTEM_CURSOR_SIZENS", + pygame.SYSTEM_CURSOR_SIZEALL: "SYSTEM_CURSOR_SIZEALL", + pygame.SYSTEM_CURSOR_NO: "SYSTEM_CURSOR_NO", + pygame.SYSTEM_CURSOR_HAND: "SYSTEM_CURSOR_HAND", +} + + +class Cursor: + def __init__(self, *args): + """Cursor(size, hotspot, xormasks, andmasks) -> Cursor + Cursor(hotspot, Surface) -> Cursor + Cursor(constant) -> Cursor + Cursor(Cursor) -> copies the Cursor object passed as an argument + Cursor() -> Cursor + + pygame object for representing cursors + + You can initialize a cursor from a system cursor or use the + constructor on an existing Cursor object, which will copy it. + Providing a Surface instance will render the cursor displayed + as that Surface when used. + + These Surfaces may use other colors than black and white.""" + if len(args) == 0: + self.type = "system" + self.data = (pygame.SYSTEM_CURSOR_ARROW,) + elif len(args) == 1 and args[0] in _cursor_id_table: + self.type = "system" + self.data = (args[0],) + elif len(args) == 1 and isinstance(args[0], Cursor): + self.type = args[0].type + self.data = args[0].data + elif ( + len(args) == 2 and len(args[0]) == 2 and isinstance(args[1], pygame.Surface) + ): + self.type = "color" + self.data = tuple(args) + elif len(args) == 4 and len(args[0]) == 2 and len(args[1]) == 2: + self.type = "bitmap" + # pylint: disable=consider-using-generator + # See https://github.com/pygame/pygame/pull/2509 for analysis + self.data = tuple(tuple(arg) for arg in args) + else: + raise TypeError("Arguments must match a cursor specification") + + def __len__(self): + return len(self.data) + + def __iter__(self): + return iter(self.data) + + def __getitem__(self, index): + return self.data[index] + + def __eq__(self, other): + return isinstance(other, Cursor) and self.data == other.data + + def __ne__(self, other): + return not self.__eq__(other) + + def __copy__(self): + """Clone the current Cursor object. + You can do the same thing by doing Cursor(Cursor).""" + return self.__class__(self) + + copy = __copy__ + + def __hash__(self): + return hash(tuple([self.type] + list(self.data))) + + def __repr__(self): + if self.type == "system": + id_string = _cursor_id_table.get(self.data[0], "constant lookup error") + return f"" + if self.type == "bitmap": + size = f"size: {self.data[0]}" + hotspot = f"hotspot: {self.data[1]}" + return f"" + if self.type == "color": + hotspot = f"hotspot: {self.data[0]}" + surf = repr(self.data[1]) + return f"" + raise TypeError("Invalid Cursor") + + +# Python side of the set_cursor function: C side in mouse.c +def set_cursor(*args): + """set_cursor(pygame.cursors.Cursor OR args for a pygame.cursors.Cursor) -> None + set the mouse cursor to a new cursor""" + cursor = Cursor(*args) + pygame.mouse._set_cursor(**{cursor.type: cursor.data}) + + +pygame.mouse.set_cursor = set_cursor +del set_cursor # cleanup namespace + + +# Python side of the get_cursor function: C side in mouse.c +def get_cursor(): + """get_cursor() -> pygame.cursors.Cursor + get the current mouse cursor""" + return Cursor(*pygame.mouse._get_cursor()) + + +pygame.mouse.get_cursor = get_cursor +del get_cursor # cleanup namespace + +arrow = Cursor( + (16, 16), + (0, 0), + ( + 0x00, + 0x00, + 0x40, + 0x00, + 0x60, + 0x00, + 0x70, + 0x00, + 0x78, + 0x00, + 0x7C, + 0x00, + 0x7E, + 0x00, + 0x7F, + 0x00, + 0x7F, + 0x80, + 0x7C, + 0x00, + 0x6C, + 0x00, + 0x46, + 0x00, + 0x06, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x00, + 0x00, + ), + ( + 0x40, + 0x00, + 0xE0, + 0x00, + 0xF0, + 0x00, + 0xF8, + 0x00, + 0xFC, + 0x00, + 0xFE, + 0x00, + 0xFF, + 0x00, + 0xFF, + 0x80, + 0xFF, + 0xC0, + 0xFF, + 0x80, + 0xFE, + 0x00, + 0xEF, + 0x00, + 0x4F, + 0x00, + 0x07, + 0x80, + 0x07, + 0x80, + 0x03, + 0x00, + ), +) + +diamond = Cursor( + (16, 16), + (7, 7), + ( + 0, + 0, + 1, + 0, + 3, + 128, + 7, + 192, + 14, + 224, + 28, + 112, + 56, + 56, + 112, + 28, + 56, + 56, + 28, + 112, + 14, + 224, + 7, + 192, + 3, + 128, + 1, + 0, + 0, + 0, + 0, + 0, + ), + ( + 1, + 0, + 3, + 128, + 7, + 192, + 15, + 224, + 31, + 240, + 62, + 248, + 124, + 124, + 248, + 62, + 124, + 124, + 62, + 248, + 31, + 240, + 15, + 224, + 7, + 192, + 3, + 128, + 1, + 0, + 0, + 0, + ), +) + +ball = Cursor( + (16, 16), + (7, 7), + ( + 0, + 0, + 3, + 192, + 15, + 240, + 24, + 248, + 51, + 252, + 55, + 252, + 127, + 254, + 127, + 254, + 127, + 254, + 127, + 254, + 63, + 252, + 63, + 252, + 31, + 248, + 15, + 240, + 3, + 192, + 0, + 0, + ), + ( + 3, + 192, + 15, + 240, + 31, + 248, + 63, + 252, + 127, + 254, + 127, + 254, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 127, + 254, + 127, + 254, + 63, + 252, + 31, + 248, + 15, + 240, + 3, + 192, + ), +) + +broken_x = Cursor( + (16, 16), + (7, 7), + ( + 0, + 0, + 96, + 6, + 112, + 14, + 56, + 28, + 28, + 56, + 12, + 48, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12, + 48, + 28, + 56, + 56, + 28, + 112, + 14, + 96, + 6, + 0, + 0, + ), + ( + 224, + 7, + 240, + 15, + 248, + 31, + 124, + 62, + 62, + 124, + 30, + 120, + 14, + 112, + 0, + 0, + 0, + 0, + 14, + 112, + 30, + 120, + 62, + 124, + 124, + 62, + 248, + 31, + 240, + 15, + 224, + 7, + ), +) + +tri_left = Cursor( + (16, 16), + (1, 1), + ( + 0, + 0, + 96, + 0, + 120, + 0, + 62, + 0, + 63, + 128, + 31, + 224, + 31, + 248, + 15, + 254, + 15, + 254, + 7, + 128, + 7, + 128, + 3, + 128, + 3, + 128, + 1, + 128, + 1, + 128, + 0, + 0, + ), + ( + 224, + 0, + 248, + 0, + 254, + 0, + 127, + 128, + 127, + 224, + 63, + 248, + 63, + 254, + 31, + 255, + 31, + 255, + 15, + 254, + 15, + 192, + 7, + 192, + 7, + 192, + 3, + 192, + 3, + 192, + 1, + 128, + ), +) + +tri_right = Cursor( + (16, 16), + (14, 1), + ( + 0, + 0, + 0, + 6, + 0, + 30, + 0, + 124, + 1, + 252, + 7, + 248, + 31, + 248, + 127, + 240, + 127, + 240, + 1, + 224, + 1, + 224, + 1, + 192, + 1, + 192, + 1, + 128, + 1, + 128, + 0, + 0, + ), + ( + 0, + 7, + 0, + 31, + 0, + 127, + 1, + 254, + 7, + 254, + 31, + 252, + 127, + 252, + 255, + 248, + 255, + 248, + 127, + 240, + 3, + 240, + 3, + 224, + 3, + 224, + 3, + 192, + 3, + 192, + 1, + 128, + ), +) + + +# Here is an example string resource cursor. To use this: +# curs, mask = pygame.cursors.compile_cursor(pygame.cursors.thickarrow_strings, 'X', '.') +# pygame.mouse.set_cursor((24, 24), (0, 0), curs, mask) +# Be warned, though, that cursors created from compiled strings do not support colors. + +# sized 24x24 +thickarrow_strings = ( + "XX ", + "XXX ", + "XXXX ", + "XX.XX ", + "XX..XX ", + "XX...XX ", + "XX....XX ", + "XX.....XX ", + "XX......XX ", + "XX.......XX ", + "XX........XX ", + "XX........XXX ", + "XX......XXXXX ", + "XX.XXX..XX ", + "XXXX XX..XX ", + "XX XX..XX ", + " XX..XX ", + " XX..XX ", + " XX..XX ", + " XXXX ", + " XX ", + " ", + " ", + " ", +) + +# sized 24x16 +sizer_x_strings = ( + " X X ", + " XX XX ", + " X.X X.X ", + " X..X X..X ", + " X...XXXXXXXX...X ", + "X................X ", + " X...XXXXXXXX...X ", + " X..X X..X ", + " X.X X.X ", + " XX XX ", + " X X ", + " ", + " ", + " ", + " ", + " ", +) + +# sized 16x24 +sizer_y_strings = ( + " X ", + " X.X ", + " X...X ", + " X.....X ", + " X.......X ", + "XXXXX.XXXXX ", + " X.X ", + " X.X ", + " X.X ", + " X.X ", + " X.X ", + " X.X ", + " X.X ", + "XXXXX.XXXXX ", + " X.......X ", + " X.....X ", + " X...X ", + " X.X ", + " X ", + " ", + " ", + " ", + " ", + " ", +) + +# sized 24x16 +sizer_xy_strings = ( + "XXXXXXXX ", + "X.....X ", + "X....X ", + "X...X ", + "X..X.X ", + "X.X X.X ", + "XX X.X X ", + "X X.X XX ", + " X.XX.X ", + " X...X ", + " X...X ", + " X....X ", + " X.....X ", + " XXXXXXXX ", + " ", + " ", +) + +# sized 8x16 +textmarker_strings = ( + "ooo ooo ", + " o ", + " o ", + " o ", + " o ", + " o ", + " o ", + " o ", + " o ", + " o ", + " o ", + "ooo ooo ", + " ", + " ", + " ", + " ", +) + + +def compile(strings, black="X", white=".", xor="o"): + """pygame.cursors.compile(strings, black, white, xor) -> data, mask + compile cursor strings into cursor data + + This takes a set of strings with equal length and computes + the binary data for that cursor. The string widths must be + divisible by 8. + + The black and white arguments are single letter strings that + tells which characters will represent black pixels, and which + characters represent white pixels. All other characters are + considered clear. + + Some systems allow you to set a special toggle color for the + system color, this is also called the xor color. If the system + does not support xor cursors, that color will simply be black. + + This returns a tuple containing the cursor data and cursor mask + data. Both these arguments are used when setting a cursor with + pygame.mouse.set_cursor(). + """ + # first check for consistent lengths + size = len(strings[0]), len(strings) + if size[0] % 8 or size[1] % 8: + raise ValueError(f"cursor string sizes must be divisible by 8 {size}") + + for s in strings[1:]: + if len(s) != size[0]: + raise ValueError("Cursor strings are inconsistent lengths") + + # create the data arrays. + # this could stand a little optimizing + maskdata = [] + filldata = [] + maskitem = fillitem = 0 + step = 8 + for s in strings: + for c in s: + maskitem = maskitem << 1 + fillitem = fillitem << 1 + step = step - 1 + if c == black: + maskitem = maskitem | 1 + fillitem = fillitem | 1 + elif c == white: + maskitem = maskitem | 1 + elif c == xor: + fillitem = fillitem | 1 + + if not step: + maskdata.append(maskitem) + filldata.append(fillitem) + maskitem = fillitem = 0 + step = 8 + + return tuple(filldata), tuple(maskdata) + + +def load_xbm(curs, mask): + """pygame.cursors.load_xbm(cursorfile, maskfile) -> cursor_args + reads a pair of XBM files into set_cursor arguments + + Arguments can either be filenames or filelike objects + with the readlines method. Not largely tested, but + should work with typical XBM files. + """ + + def bitswap(num): + val = 0 + for x in range(8): + b = num & (1 << x) != 0 + val = val << 1 | b + return val + + if hasattr(curs, "readlines"): + curs = curs.readlines() + else: + with open(curs, encoding="ascii") as cursor_f: + curs = cursor_f.readlines() + + if hasattr(mask, "readlines"): + mask = mask.readlines() + else: + with open(mask, encoding="ascii") as mask_f: + mask = mask_f.readlines() + + # avoid comments + for i, line in enumerate(curs): + if line.startswith("#define"): + curs = curs[i:] + break + + for i, line in enumerate(mask): + if line.startswith("#define"): + mask = mask[i:] + break + + # load width,height + width = int(curs[0].split()[-1]) + height = int(curs[1].split()[-1]) + # load hotspot position + if curs[2].startswith("#define"): + hotx = int(curs[2].split()[-1]) + hoty = int(curs[3].split()[-1]) + else: + hotx = hoty = 0 + + info = width, height, hotx, hoty + + possible_starts = ("static char", "static unsigned char") + for i, line in enumerate(curs): + if line.startswith(possible_starts): + break + data = " ".join(curs[i + 1 :]).replace("};", "").replace(",", " ") + cursdata = [] + for x in data.split(): + cursdata.append(bitswap(int(x, 16))) + cursdata = tuple(cursdata) + for i, line in enumerate(mask): + if line.startswith(possible_starts): + break + data = " ".join(mask[i + 1 :]).replace("};", "").replace(",", " ") + maskdata = [] + for x in data.split(): + maskdata.append(bitswap(int(x, 16))) + + maskdata = tuple(maskdata) + return info[:2], info[2:], cursdata, maskdata diff --git a/.venv/Lib/site-packages/pygame/cursors.pyi b/.venv/Lib/site-packages/pygame/cursors.pyi new file mode 100644 index 00000000..7edc62b5 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/cursors.pyi @@ -0,0 +1,91 @@ +from typing import Any, Iterator, Sequence, Tuple, Union, overload + +from pygame.surface import Surface + +from ._common import FileArg, Literal + +_Small_string = Tuple[ + str, str, str, str, str, str, str, str, str, str, str, str, str, str, str, str +] +_Big_string = Tuple[ + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, + str, +] + +arrow: Cursor +diamond: Cursor +broken_x: Cursor +tri_left: Cursor +tri_right: Cursor +ball: Cursor +thickarrow_strings: _Big_string +sizer_x_strings: _Small_string +sizer_y_strings: _Big_string +sizer_xy_strings: _Small_string +textmarker_strings: _Small_string + +def compile( + strings: Sequence[str], + black: str = "X", + white: str = ".", + xor: str = "o", +) -> Tuple[Tuple[int, ...], Tuple[int, ...]]: ... +def load_xbm( + curs: FileArg, mask: FileArg +) -> Tuple[Tuple[int, int], Tuple[int, int], Tuple[int, ...], Tuple[int, ...]]: ... + +class Cursor: + @overload + def __init__(self, constant: int = ...) -> None: ... + @overload + def __init__(self, cursor: Cursor) -> None: ... + @overload + def __init__( + self, + size: Union[Tuple[int, int], Sequence[int]], + hotspot: Union[Tuple[int, int], Sequence[int]], + xormasks: Sequence[int], + andmasks: Sequence[int], + ) -> None: ... + @overload + def __init__( + self, + hotspot: Union[Tuple[int, int], Sequence[int]], + surface: Surface, + ) -> None: ... + def __iter__(self) -> Iterator[Any]: ... + def __len__(self) -> int: ... + def __copy__(self) -> Cursor: ... + def __hash__(self) -> int: ... + def __getitem__( + self, index: int + ) -> Union[int, Tuple[int, int], Sequence[int], Surface]: ... + copy = __copy__ + type: Literal["system", "color", "bitmap"] + data: Union[ + Tuple[int], + Tuple[Tuple[int, int], Tuple[int, int], Tuple[int, ...], Tuple[int, ...]], + Tuple[Union[Tuple[int, int], Sequence[int]], Surface], + ] diff --git a/.venv/Lib/site-packages/pygame/display.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/display.cp311-win_amd64.pyd new file mode 100644 index 00000000..091ac86f Binary files /dev/null and b/.venv/Lib/site-packages/pygame/display.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/display.pyi b/.venv/Lib/site-packages/pygame/display.pyi new file mode 100644 index 00000000..443a1f93 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/display.pyi @@ -0,0 +1,77 @@ +from typing import Union, Tuple, List, Optional, Dict, Sequence, overload + +from pygame.surface import Surface +from pygame.constants import FULLSCREEN +from ._common import Coordinate, RectValue, ColorValue, RGBAOutput + +class _VidInfo: + hw: int + wm: int + video_mem: int + bitsize: int + bytesize: int + masks: RGBAOutput + shifts: RGBAOutput + losses: RGBAOutput + blit_hw: int + blit_hw_CC: int + blit_hw_A: int + blit_sw: int + blit_sw_CC: int + blit_sw_A: int + current_h: int + current_w: int + +def init() -> None: ... +def quit() -> None: ... +def get_init() -> bool: ... +def set_mode( + size: Coordinate = (0, 0), + flags: int = 0, + depth: int = 0, + display: int = 0, + vsync: int = 0, +) -> Surface: ... +def get_surface() -> Surface: ... +def flip() -> None: ... +@overload +def update( + rectangle: Optional[Union[RectValue, Sequence[Optional[RectValue]]]] = None +) -> None: ... +@overload +def update(x: int, y: int, w: int, h: int) -> None: ... +@overload +def update(xy: Coordinate, wh: Coordinate) -> None: ... +def get_driver() -> str: ... +def Info() -> _VidInfo: ... +def get_wm_info() -> Dict[str, int]: ... +def list_modes( + depth: int = 0, + flags: int = FULLSCREEN, + display: int = 0, +) -> List[Tuple[int, int]]: ... +def mode_ok( + size: Union[Sequence[int], Tuple[int, int]], + flags: int = 0, + depth: int = 0, + display: int = 0, +) -> int: ... +def gl_get_attribute(flag: int) -> int: ... +def gl_set_attribute(flag: int, value: int) -> None: ... +def get_active() -> bool: ... +def iconify() -> bool: ... +def toggle_fullscreen() -> int: ... +def set_gamma(red: float, green: float = ..., blue: float = ...) -> int: ... +def set_gamma_ramp( + red: Sequence[int], green: Sequence[int], blue: Sequence[int] +) -> int: ... +def set_icon(surface: Surface) -> None: ... +def set_caption(title: str, icontitle: Optional[str] = None) -> None: ... +def get_caption() -> Tuple[str, str]: ... +def set_palette(palette: Sequence[ColorValue]) -> None: ... +def get_num_displays() -> int: ... +def get_window_size() -> Tuple[int, int]: ... +def get_allow_screensaver() -> bool: ... +def set_allow_screensaver(value: bool = True) -> None: ... +def get_desktop_sizes() -> List[Tuple[int, int]]: ... +def is_fullscreen() -> bool: ... diff --git a/.venv/Lib/site-packages/pygame/docs/__main__.py b/.venv/Lib/site-packages/pygame/docs/__main__.py new file mode 100644 index 00000000..249b65ee --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/__main__.py @@ -0,0 +1,37 @@ +# python -m pygame.docs + +import os +import webbrowser +from urllib.parse import quote, urlunparse + + +def _iterpath(path): + path, last = os.path.split(path) + if last: + yield from _iterpath(path) + yield last + + +# for test suite to confirm pygame built with local docs +def has_local_docs(): + pkg_dir = os.path.dirname(os.path.abspath(__file__)) + main_page = os.path.join(pkg_dir, "generated", "index.html") + return os.path.exists(main_page) + + +def open_docs(): + pkg_dir = os.path.dirname(os.path.abspath(__file__)) + main_page = os.path.join(pkg_dir, "generated", "index.html") + if os.path.exists(main_page): + url_path = quote("/".join(_iterpath(main_page))) + drive, rest = os.path.splitdrive(__file__) + if drive: + url_path = f"{drive}/{url_path}" + url = urlunparse(("file", "", url_path, "", "", "")) + else: + url = "https://www.pygame.org/docs/" + webbrowser.open(url) + + +if __name__ == "__main__": + open_docs() diff --git a/.venv/Lib/site-packages/pygame/docs/__pycache__/__main__.cpython-311.pyc b/.venv/Lib/site-packages/pygame/docs/__pycache__/__main__.cpython-311.pyc new file mode 100644 index 00000000..af3bdb3e Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/__pycache__/__main__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/LGPL.txt b/.venv/Lib/site-packages/pygame/docs/generated/LGPL.txt new file mode 100644 index 00000000..b1e3f5a2 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/LGPL.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput1.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput1.gif new file mode 100644 index 00000000..8c7f7ea6 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput1.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput11.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput11.gif new file mode 100644 index 00000000..8c7f7ea6 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput11.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput2.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput2.gif new file mode 100644 index 00000000..2094e3aa Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput2.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput21.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput21.gif new file mode 100644 index 00000000..2094e3aa Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput21.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput3.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput3.gif new file mode 100644 index 00000000..3a0d748a Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput3.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput31.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput31.gif new file mode 100644 index 00000000..3a0d748a Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput31.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput4.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput4.gif new file mode 100644 index 00000000..415644e6 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput4.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput41.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput41.gif new file mode 100644 index 00000000..415644e6 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput41.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput5.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput5.gif new file mode 100644 index 00000000..5796b4e1 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput5.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput51.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput51.gif new file mode 100644 index 00000000..5796b4e1 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedInputOutput51.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputAlpha1.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputAlpha1.gif new file mode 100644 index 00000000..c0d41244 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputAlpha1.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputAlpha11.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputAlpha11.gif new file mode 100644 index 00000000..c0d41244 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputAlpha11.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputAlpha2.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputAlpha2.gif new file mode 100644 index 00000000..00b443da Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputAlpha2.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputAlpha21.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputAlpha21.gif new file mode 100644 index 00000000..00b443da Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputAlpha21.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputAlpha3.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputAlpha3.gif new file mode 100644 index 00000000..be1204dd Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputAlpha3.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputAlpha31.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputAlpha31.gif new file mode 100644 index 00000000..be1204dd Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputAlpha31.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess1.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess1.gif new file mode 100644 index 00000000..20edf216 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess1.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess11.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess11.gif new file mode 100644 index 00000000..20edf216 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess11.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess2.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess2.gif new file mode 100644 index 00000000..5445c0e2 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess2.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess21.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess21.gif new file mode 100644 index 00000000..5445c0e2 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess21.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess3.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess3.gif new file mode 100644 index 00000000..0da81f17 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess3.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess31.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess31.gif new file mode 100644 index 00000000..0da81f17 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess31.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess4.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess4.gif new file mode 100644 index 00000000..18506fae Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess4.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess41.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess41.gif new file mode 100644 index 00000000..18506fae Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess41.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess5.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess5.gif new file mode 100644 index 00000000..def0e06b Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess5.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess51.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess51.gif new file mode 100644 index 00000000..def0e06b Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess51.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess6.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess6.gif new file mode 100644 index 00000000..127cc1e8 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess6.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess61.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess61.gif new file mode 100644 index 00000000..127cc1e8 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/AdvancedOutputProcess61.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-INPUT-resultscreen.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-INPUT-resultscreen.png new file mode 100644 index 00000000..9280cbe8 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-INPUT-resultscreen.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-INPUT-resultscreen1.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-INPUT-resultscreen1.png new file mode 100644 index 00000000..9280cbe8 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-INPUT-resultscreen1.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-INPUT-sourcecode.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-INPUT-sourcecode.png new file mode 100644 index 00000000..d378854b Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-INPUT-sourcecode.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-INPUT-sourcecode1.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-INPUT-sourcecode1.png new file mode 100644 index 00000000..d378854b Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-INPUT-sourcecode1.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-PROCESS-resultscreen.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-PROCESS-resultscreen.png new file mode 100644 index 00000000..f4e8322c Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-PROCESS-resultscreen.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-PROCESS-resultscreen1.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-PROCESS-resultscreen1.png new file mode 100644 index 00000000..f4e8322c Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-PROCESS-resultscreen1.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-PROCESS-sourcecode.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-PROCESS-sourcecode.png new file mode 100644 index 00000000..b20873a1 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-PROCESS-sourcecode.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-PROCESS-sourcecode1.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-PROCESS-sourcecode1.png new file mode 100644 index 00000000..b20873a1 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-PROCESS-sourcecode1.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-ouput-result-screen.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-ouput-result-screen.png new file mode 100644 index 00000000..d8628e11 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-ouput-result-screen.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-ouput-result-screen1.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-ouput-result-screen1.png new file mode 100644 index 00000000..d8628e11 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/Bagic-ouput-result-screen1.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/Basic-ouput-sourcecode.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/Basic-ouput-sourcecode.png new file mode 100644 index 00000000..659c981b Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/Basic-ouput-sourcecode.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/Basic-ouput-sourcecode1.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/Basic-ouput-sourcecode1.png new file mode 100644 index 00000000..659c981b Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/Basic-ouput-sourcecode1.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/angle_to.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/angle_to.png new file mode 100644 index 00000000..2cf3b2a5 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/angle_to.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_average.jpg b/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_average.jpg new file mode 100644 index 00000000..043e485b Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_average.jpg differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_background.jpg b/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_background.jpg new file mode 100644 index 00000000..cfcbd02c Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_background.jpg differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_green.jpg b/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_green.jpg new file mode 100644 index 00000000..24c4b09b Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_green.jpg differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_hsv.jpg b/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_hsv.jpg new file mode 100644 index 00000000..afe95597 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_hsv.jpg differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_mask.jpg b/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_mask.jpg new file mode 100644 index 00000000..cd8ed1de Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_mask.jpg differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_rgb.jpg b/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_rgb.jpg new file mode 100644 index 00000000..3401421b Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_rgb.jpg differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_thresh.jpg b/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_thresh.jpg new file mode 100644 index 00000000..cb2426ff Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_thresh.jpg differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_thresholded.jpg b/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_thresholded.jpg new file mode 100644 index 00000000..c4ee891a Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_thresholded.jpg differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_yuv.jpg b/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_yuv.jpg new file mode 100644 index 00000000..1cda596f Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/camera_yuv.jpg differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/chimpshot.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/chimpshot.gif new file mode 100644 index 00000000..c27191d1 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/chimpshot.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/draw_module_example.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/draw_module_example.png new file mode 100644 index 00000000..42d6ed18 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/draw_module_example.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/intro_ball.gif b/.venv/Lib/site-packages/pygame/docs/generated/_images/intro_ball.gif new file mode 100644 index 00000000..bbc4a95f Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/intro_ball.gif differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/intro_blade.jpg b/.venv/Lib/site-packages/pygame/docs/generated/_images/intro_blade.jpg new file mode 100644 index 00000000..2ed490cb Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/intro_blade.jpg differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/intro_freedom.jpg b/.venv/Lib/site-packages/pygame/docs/generated/_images/intro_freedom.jpg new file mode 100644 index 00000000..9ed473d5 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/intro_freedom.jpg differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/introduction-Battleship.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/introduction-Battleship.png new file mode 100644 index 00000000..35674f69 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/introduction-Battleship.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/introduction-Battleship1.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/introduction-Battleship1.png new file mode 100644 index 00000000..35674f69 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/introduction-Battleship1.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/introduction-PuyoPuyo.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/introduction-PuyoPuyo.png new file mode 100644 index 00000000..dc104ead Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/introduction-PuyoPuyo.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/introduction-PuyoPuyo1.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/introduction-PuyoPuyo1.png new file mode 100644 index 00000000..dc104ead Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/introduction-PuyoPuyo1.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/introduction-TPS.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/introduction-TPS.png new file mode 100644 index 00000000..9c8cb0c1 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/introduction-TPS.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/introduction-TPS1.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/introduction-TPS1.png new file mode 100644 index 00000000..9c8cb0c1 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/introduction-TPS1.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/joystick_calls.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/joystick_calls.png new file mode 100644 index 00000000..c713bfe0 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/joystick_calls.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/pygame_lofi.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/pygame_lofi.png new file mode 100644 index 00000000..b9e9f569 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/pygame_lofi.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/pygame_logo.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/pygame_logo.png new file mode 100644 index 00000000..0de28c4c Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/pygame_logo.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/pygame_powered.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/pygame_powered.png new file mode 100644 index 00000000..4d5bf4bb Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/pygame_powered.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/pygame_powered_lowres.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/pygame_powered_lowres.png new file mode 100644 index 00000000..642ae7a6 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/pygame_powered_lowres.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/pygame_tiny.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/pygame_tiny.png new file mode 100644 index 00000000..5ee063a7 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/pygame_tiny.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_allblack.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_allblack.png new file mode 100644 index 00000000..80cbc35e Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_allblack.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_flipped.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_flipped.png new file mode 100644 index 00000000..742f4f3a Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_flipped.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_redimg.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_redimg.png new file mode 100644 index 00000000..58e9c3f6 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_redimg.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_rgbarray.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_rgbarray.png new file mode 100644 index 00000000..8513ef75 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_rgbarray.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_scaledown.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_scaledown.png new file mode 100644 index 00000000..0be3541d Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_scaledown.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_scaleup.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_scaleup.png new file mode 100644 index 00000000..ff1f15e7 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_scaleup.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_soften.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_soften.png new file mode 100644 index 00000000..c1d4ce48 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_soften.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_striped.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_striped.png new file mode 100644 index 00000000..2ca4a79d Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_striped.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_xfade.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_xfade.png new file mode 100644 index 00000000..8b907068 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/surfarray_xfade.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/tom_basic.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/tom_basic.png new file mode 100644 index 00000000..849adb5c Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/tom_basic.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/tom_event-flowchart.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/tom_event-flowchart.png new file mode 100644 index 00000000..6a33613c Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/tom_event-flowchart.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/tom_formulae.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/tom_formulae.png new file mode 100644 index 00000000..f04b482a Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/tom_formulae.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_images/tom_radians.png b/.venv/Lib/site-packages/pygame/docs/generated/_images/tom_radians.png new file mode 100644 index 00000000..4569df1c Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_images/tom_radians.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/c_api.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/c_api.rst.txt new file mode 100644 index 00000000..734289f0 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/c_api.rst.txt @@ -0,0 +1,25 @@ +pygame C API +============ + +.. toctree:: + :maxdepth: 1 + :glob: + + c_api/slots.rst + c_api/base.rst + c_api/bufferproxy.rst + c_api/color.rst + c_api/display.rst + c_api/event.rst + c_api/freetype.rst + c_api/mixer.rst + c_api/rect.rst + c_api/rwobject.rst + c_api/surface.rst + c_api/surflock.rst + c_api/version.rst + + +src_c/include/ contains header files for applications +that use the pygame C API, while src_c/ contains +headers used by pygame internally. diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/filepaths.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/filepaths.rst.txt new file mode 100644 index 00000000..e17f6875 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/filepaths.rst.txt @@ -0,0 +1,17 @@ +File Path Function Arguments +============================ + +A pygame function or method which takes a file path argument will accept +either a Unicode or a byte (8-bit or ASCII character) string. +Unicode strings are translated to Python's default filesystem encoding, +as returned by sys.getfilesystemencoding(). A Unicode code point +above U+FFFF (``\uFFFF``) can be coded directly with a 32-bit escape sequences +(``\Uxxxxxxxx``), even for Python interpreters built with an UCS-2 +(16-bit character) Unicode type. Byte strings are passed +to the operating system unchanged. + +Null characters (``\x00``) are not permitted in the path, raising an exception. +An exception is also raised if an Unicode file path cannot be encoded. +How UTF-16 surrogate codes are handled is Python-interpreter-dependent. +Use UTF-32 code points and 32-bit escape sequences instead. +The exception types are function-dependent. diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/index.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/index.rst.txt new file mode 100644 index 00000000..8d34d7cc --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/index.rst.txt @@ -0,0 +1,213 @@ +Pygame Front Page +================= + +.. toctree:: + :maxdepth: 2 + :glob: + :hidden: + + ref/* + tut/* + tut/en/**/* + tut/ko/**/* + c_api + filepaths + logos + +Quick start +----------- + +Welcome to pygame! Once you've got pygame installed (:code:`pip install pygame` or +:code:`pip3 install pygame` for most people), the next question is how to get a game +loop running. Pygame, unlike some other libraries, gives you full control of program +execution. That freedom means it is easy to mess up in your initial steps. + +Here is a good example of a basic setup (opens the window, updates the screen, and handles events)-- + +.. literalinclude:: ref/code_examples/base_script.py + +Here is a slightly more fleshed out example, which shows you how to move something +(a circle in this case) around on screen-- + +.. literalinclude:: ref/code_examples/base_script_example.py + +For more in depth reference, check out the :ref:`tutorials-reference-label` +section below, check out a video tutorial (`I'm a fan of this one +`_), or reference the API +documentation by module. + +Documents +--------- + +`Readme`_ + Basic information about pygame: what it is, who is involved, and where to find it. + +`Install`_ + Steps needed to compile pygame on several platforms. + Also help on finding and installing prebuilt binaries for your system. + +:doc:`filepaths` + How pygame handles file system paths. + +:doc:`Pygame Logos ` + The logos of Pygame in different resolutions. + + +`LGPL License`_ + This is the license pygame is distributed under. + It provides for pygame to be distributed with open source and commercial software. + Generally, if pygame is not changed, it can be used with any type of program. + +.. _tutorials-reference-label: + +Tutorials +--------- + +:doc:`Introduction to Pygame ` + An introduction to the basics of pygame. + This is written for users of Python and appeared in volume two of the Py magazine. + +:doc:`Import and Initialize ` + The beginning steps on importing and initializing pygame. + The pygame package is made of several modules. + Some modules are not included on all platforms. + +:doc:`How do I move an Image? ` + A basic tutorial that covers the concepts behind 2D computer animation. + Information about drawing and clearing objects to make them appear animated. + +:doc:`Chimp Tutorial, Line by Line ` + The pygame examples include a simple program with an interactive fist and a chimpanzee. + This was inspired by the annoying flash banner of the early 2000s. + This tutorial examines every line of code used in the example. + +:doc:`Sprite Module Introduction ` + Pygame includes a higher level sprite module to help organize games. + The sprite module includes several classes that help manage details found in almost all games types. + The Sprite classes are a bit more advanced than the regular pygame modules, + and need more understanding to be properly used. + +:doc:`Surfarray Introduction ` + Pygame used the NumPy python module to allow efficient per pixel effects on images. + Using the surface arrays is an advanced feature that allows custom effects and filters. + This also examines some of the simple effects from the pygame example, arraydemo.py. + +:doc:`Camera Module Introduction ` + Pygame, as of 1.9, has a camera module that allows you to capture images, + watch live streams, and do some basic computer vision. + This tutorial covers those use cases. + +:doc:`Newbie Guide ` + A list of thirteen helpful tips for people to get comfortable using pygame. + +:doc:`Making Games Tutorial ` + A large tutorial that covers the bigger topics needed to create an entire game. + +:doc:`Display Modes ` + Getting a display surface for the screen. + +:doc:`한국어 튜토리얼 (Korean Tutorial) ` + 빨간블록 검은블록 + + +Reference +--------- + +:ref:`genindex` + A list of all functions, classes, and methods in the pygame package. + +:doc:`ref/bufferproxy` + An array protocol view of surface pixels + +:doc:`ref/color` + Color representation. + +:doc:`ref/cursors` + Loading and compiling cursor images. + +:doc:`ref/display` + Configure the display surface. + +:doc:`ref/draw` + Drawing simple shapes like lines and ellipses to surfaces. + +:doc:`ref/event` + Manage the incoming events from various input devices and the windowing platform. + +:doc:`ref/examples` + Various programs demonstrating the use of individual pygame modules. + +:doc:`ref/font` + Loading and rendering TrueType fonts. + +:doc:`ref/freetype` + Enhanced pygame module for loading and rendering font faces. + +:doc:`ref/gfxdraw` + Anti-aliasing draw functions. + +:doc:`ref/image` + Loading, saving, and transferring of surfaces. + +:doc:`ref/joystick` + Manage the joystick devices. + +:doc:`ref/key` + Manage the keyboard device. + +:doc:`ref/locals` + Pygame constants. + +:doc:`ref/mixer` + Load and play sounds + +:doc:`ref/mouse` + Manage the mouse device and display. + +:doc:`ref/music` + Play streaming music tracks. + +:doc:`ref/pygame` + Top level functions to manage pygame. + +:doc:`ref/pixelarray` + Manipulate image pixel data. + +:doc:`ref/rect` + Flexible container for a rectangle. + +:doc:`ref/scrap` + Native clipboard access. + +:doc:`ref/sndarray` + Manipulate sound sample data. + +:doc:`ref/sprite` + Higher level objects to represent game images. + +:doc:`ref/surface` + Objects for images and the screen. + +:doc:`ref/surfarray` + Manipulate image pixel data. + +:doc:`ref/tests` + Test pygame. + +:doc:`ref/time` + Manage timing and framerate. + +:doc:`ref/transform` + Resize and move images. + +:doc:`pygame C API ` + The C api shared amongst pygame extension modules. + +:ref:`search` + Search pygame documents by keyword. + +.. _Readme: ../wiki/about + +.. _Install: ../wiki/GettingStarted#Pygame%20Installation + +.. _LGPL License: LGPL.txt diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/logos.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/logos.rst.txt new file mode 100644 index 00000000..a7ee4931 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/logos.rst.txt @@ -0,0 +1,47 @@ +************************************************* + Pygame Logos Page +************************************************* + +Pygame Logos +============ + +These logos are available for use in your own game projects. +Please put them up wherever you see fit. The logo was created +by TheCorruptor on July 29, 2001 and upscaled by Mega_JC on +August 29, 2021. + +.. container:: fullwidth + + .. image:: _static/pygame_logo.png + + | `pygame_logo.svg <_static/pygame_logo.svg>`_ + | `pygame_logo.png <_static/pygame_logo.png>`_ - 1561 x 438 + + .. image:: _static/pygame_lofi.png + + | `pygame_lofi.svg <_static/pygame_lofi.svg>`_ + | `pygame_lofi.png <_static/pygame_lofi.png>`_ - 1561 x 438 + + .. image:: _static/pygame_powered.png + + | `pygame_powered.svg <_static/pygame_powered.svg>`_ + | `pygame_powered.png <_static/pygame_powered.png>`_ - 1617 x 640 + + .. image:: _static/pygame_tiny.png + + | `pygame_tiny.png <_static/pygame_tiny.png>`_ - 214 x 60 + + .. image:: _static/pygame_powered_lowres.png + + | `pygame_powered_lowres.png <_static/pygame_powered_lowres.png>`_ - 101 x 40 + + +There is a higher resolution layered photoshop image +available `here `_. *(1.3 MB)* + +Legacy logos +------------ + +.. container:: fullwidth + + `legacy_logos.zip <_static/legacy_logos.zip>`_ - 50.1 KB \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/bufferproxy.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/bufferproxy.rst.txt new file mode 100644 index 00000000..bb4e6935 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/bufferproxy.rst.txt @@ -0,0 +1,113 @@ +.. include:: common.txt + +.. default-domain:: py + +:class:`pygame.BufferProxy` +=========================== + +.. currentmodule:: pygame + +.. class:: BufferProxy + + | :sl:`pygame object to export a surface buffer through an array protocol` + | :sg:`BufferProxy() -> BufferProxy` + + :class:`BufferProxy` is a pygame support type, designed as the return value + of the :meth:`Surface.get_buffer` and :meth:`Surface.get_view` methods. + For all Python versions a :class:`BufferProxy` object exports a C struct + and Python level array interface on behalf of its parent object's buffer. + A new buffer interface is also exported. + In pygame, :class:`BufferProxy` is key to implementing the + :mod:`pygame.surfarray` module. + + :class:`BufferProxy` instances can be created directly from Python code, + either for a parent that exports an interface, or from a Python ``dict`` + describing an object's buffer layout. The dict entries are based on the + Python level array interface mapping. The following keys are recognized: + + ``"shape"`` : tuple + The length of each array dimension as a tuple of integers. The + length of the tuple is the number of dimensions in the array. + + ``"typestr"`` : string + The array element type as a length 3 string. The first character + gives byteorder, '<' for little-endian, '>' for big-endian, and + '\|' for not applicable. The second character is the element type, + 'i' for signed integer, 'u' for unsigned integer, 'f' for floating + point, and 'V' for an chunk of bytes. The third character gives the + bytesize of the element, from '1' to '9' bytes. So, for example, + " Surface` + | :sg:`parent -> ` + + The :class:`Surface` which returned the :class:`BufferProxy` object or + the object passed to a :class:`BufferProxy` call. + + .. attribute:: length + + | :sl:`The size, in bytes, of the exported buffer.` + | :sg:`length -> int` + + The number of valid bytes of data exported. For discontinuous data, + that is data which is not a single block of memory, the bytes within + the gaps are excluded from the count. This property is equivalent to + the ``Py_buffer`` C struct ``len`` field. + + .. attribute:: raw + + | :sl:`A copy of the exported buffer as a single block of bytes.` + | :sg:`raw -> bytes` + + The buffer data as a ``str``/``bytes`` object. + Any gaps in the exported data are removed. + + .. method:: write + + | :sl:`Write raw bytes to object buffer.` + | :sg:`write(buffer, offset=0)` + + Overwrite bytes in the parent object's data. The data must be C or F + contiguous, otherwise a ValueError is raised. Argument `buffer` is a + ``str``/``bytes`` object. An optional offset gives a + start position, in bytes, within the buffer where overwriting begins. + If the offset is negative or greater that or equal to the buffer proxy's + :attr:`length` value, an ``IndexException`` is raised. + If ``len(buffer) > proxy.length + offset``, a ``ValueError`` is raised. diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/camera.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/camera.rst.txt new file mode 100644 index 00000000..a4123921 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/camera.rst.txt @@ -0,0 +1,250 @@ +.. include:: common.txt + +:mod:`pygame.camera` +==================== + +.. module:: pygame.camera + :synopsis: pygame module for camera use + +| :sl:`pygame module for camera use` + +.. note:: + Use import pygame.camera before using this module. + +Pygame currently supports Linux (V4L2) and Windows (MSMF) cameras natively, +with wider platform support available via an integrated OpenCV backend. + +.. versionadded:: 2.0.2 Windows native camera support +.. versionadded:: 2.0.3 New OpenCV backends + +EXPERIMENTAL!: This API may change or disappear in later pygame releases. If +you use this, your code will very likely break with the next pygame release. + +The Bayer to ``RGB`` function is based on: + +:: + + Sonix SN9C101 based webcam basic I/F routines + Copyright (C) 2004 Takafumi Mizuno + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + +New in pygame 1.9.0. + +.. function:: init + + | :sl:`Module init` + | :sg:`init(backend = None) -> None` + + This function starts up the camera module, choosing the best webcam backend + it can find for your system. This is not guaranteed to succeed, and may even + attempt to import third party modules, like `OpenCV`. If you want to + override its backend choice, you can call pass the name of the backend you + want into this function. More about backends in + :func:`get_backends()`. + + .. versionchanged:: 2.0.3 Option to explicitly select backend + + .. ## pygame.camera.init ## + +.. function:: get_backends + + | :sl:`Get the backends supported on this system` + | :sg:`get_backends() -> [str]` + + This function returns every backend it thinks has a possibility of working + on your system, in order of priority. + + pygame.camera Backends: + :: + + Backend OS Description + --------------------------------------------------------------------------------- + _camera (MSMF) Windows Builtin, works on Windows 8+ Python3 + _camera (V4L2) Linux Builtin + OpenCV Any Uses `opencv-python` module, can't enumerate cameras + OpenCV-Mac Mac Same as OpenCV, but has camera enumeration + VideoCapture Windows Uses abandoned `VideoCapture` module, can't enumerate + cameras, may be removed in the future + + There are two main differences among backends. + + The _camera backends are built in to pygame itself, and require no third + party imports. All the other backends do. For the OpenCV and VideoCapture + backends, those modules need to be installed on your system. + + The other big difference is "camera enumeration." Some backends don't have + a way to list out camera names, or even the number of cameras on the + system. In these cases, :func:`list_cameras()` will return + something like ``[0]``. If you know you have multiple cameras on the + system, these backend ports will pass through a "camera index number" + through if you use that as the ``device`` parameter. + + .. versionadded:: 2.0.3 + + .. ## pygame.camera.get_backends ## + +.. function:: colorspace + + | :sl:`Surface colorspace conversion` + | :sg:`colorspace(Surface, format, DestSurface = None) -> Surface` + + Allows for conversion from "RGB" to a destination colorspace of "HSV" or + "YUV". The source and destination surfaces must be the same size and pixel + depth. This is useful for computer vision on devices with limited processing + power. Capture as small of an image as possible, ``transform.scale()`` it + even smaller, and then convert the colorspace to ``YUV`` or ``HSV`` before + doing any processing on it. + + .. ## pygame.camera.colorspace ## + +.. function:: list_cameras + + | :sl:`returns a list of available cameras` + | :sg:`list_cameras() -> [cameras]` + + Checks the computer for available cameras and returns a list of strings of + camera names, ready to be fed into :class:`pygame.camera.Camera`. + + If the camera backend doesn't support webcam enumeration, this will return + something like ``[0]``. See :func:`get_backends()` for much more + information. + + .. ## pygame.camera.list_cameras ## + +.. class:: Camera + + | :sl:`load a camera` + | :sg:`Camera(device, (width, height), format) -> Camera` + + Loads a camera. On Linux, the device is typically something like + "/dev/video0". Default width and height are 640 by 480. + Format is the desired colorspace of the output. + This is useful for computer vision purposes. The default is + ``RGB``. The following are supported: + + * ``RGB`` - Red, Green, Blue + + * ``YUV`` - Luma, Blue Chrominance, Red Chrominance + + * ``HSV`` - Hue, Saturation, Value + + .. method:: start + + | :sl:`opens, initializes, and starts capturing` + | :sg:`start() -> None` + + Opens the camera device, attempts to initialize it, and begins recording + images to a buffer. The camera must be started before any of the below + functions can be used. + + .. ## Camera.start ## + + .. method:: stop + + | :sl:`stops, uninitializes, and closes the camera` + | :sg:`stop() -> None` + + Stops recording, uninitializes the camera, and closes it. Once a camera + is stopped, the below functions cannot be used until it is started again. + + .. ## Camera.stop ## + + .. method:: get_controls + + | :sl:`gets current values of user controls` + | :sg:`get_controls() -> (hflip = bool, vflip = bool, brightness)` + + If the camera supports it, get_controls will return the current settings + for horizontal and vertical image flip as bools and brightness as an int. + If unsupported, it will return the default values of (0, 0, 0). Note that + the return values here may be different than those returned by + set_controls, though these are more likely to be correct. + + .. ## Camera.get_controls ## + + .. method:: set_controls + + | :sl:`changes camera settings if supported by the camera` + | :sg:`set_controls(hflip = bool, vflip = bool, brightness) -> (hflip = bool, vflip = bool, brightness)` + + Allows you to change camera settings if the camera supports it. The + return values will be the input values if the camera claims it succeeded + or the values previously in use if not. Each argument is optional, and + the desired one can be chosen by supplying the keyword, like hflip. Note + that the actual settings being used by the camera may not be the same as + those returned by set_controls. On Windows, :code:`hflip` and :code:`vflip` are + implemented by pygame, not by the Camera, so they should always work, but + :code:`brightness` is unsupported. + + .. ## Camera.set_controls ## + + .. method:: get_size + + | :sl:`returns the dimensions of the images being recorded` + | :sg:`get_size() -> (width, height)` + + Returns the current dimensions of the images being captured by the + camera. This will return the actual size, which may be different than the + one specified during initialization if the camera did not support that + size. + + .. ## Camera.get_size ## + + .. method:: query_image + + | :sl:`checks if a frame is ready` + | :sg:`query_image() -> bool` + + If an image is ready to get, it returns true. Otherwise it returns false. + Note that some webcams will always return False and will only queue a + frame when called with a blocking function like :func:`get_image()`. + On Windows (MSMF), and the OpenCV backends, :func:`query_image()` + should be reliable, though. This is useful to separate the framerate of + the game from that of the camera without having to use threading. + + .. ## Camera.query_image ## + + .. method:: get_image + + | :sl:`captures an image as a Surface` + | :sg:`get_image(Surface = None) -> Surface` + + Pulls an image off of the buffer as an ``RGB`` Surface. It can optionally + reuse an existing Surface to save time. The bit-depth of the surface is + 24 bits on Linux, 32 bits on Windows, or the same as the optionally + supplied Surface. + + .. ## Camera.get_image ## + + .. method:: get_raw + + | :sl:`returns an unmodified image as bytes` + | :sg:`get_raw() -> bytes` + + Gets an image from a camera as a string in the native pixelformat of the + camera. Useful for integration with other libraries. This returns a + bytes object + + .. ## Camera.get_raw ## + + .. ## pygame.camera.Camera ## + +.. ## pygame.camera ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/cdrom.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/cdrom.rst.txt new file mode 100644 index 00000000..62688c9d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/cdrom.rst.txt @@ -0,0 +1,310 @@ +.. include:: common.txt + +:mod:`pygame.cdrom` +=================== + +.. module:: pygame.cdrom + :synopsis: pygame module for audio cdrom control + +| :sl:`pygame module for audio cdrom control` + +.. warning:: + This module is non functional in pygame 2.0 and above, unless you have manually compiled pygame with SDL1. + This module will not be supported in the future. + One alternative for python cdrom functionality is `pycdio `_. + +The cdrom module manages the ``CD`` and ``DVD`` drives on a computer. It can +also control the playback of audio CDs. This module needs to be initialized +before it can do anything. Each ``CD`` object you create represents a cdrom +drive and must also be initialized individually before it can do most things. + +.. function:: init + + | :sl:`initialize the cdrom module` + | :sg:`init() -> None` + + Initialize the cdrom module. This will scan the system for all ``CD`` + devices. The module must be initialized before any other functions will + work. This automatically happens when you call ``pygame.init()``. + + It is safe to call this function more than once. + + .. ## pygame.cdrom.init ## + +.. function:: quit + + | :sl:`uninitialize the cdrom module` + | :sg:`quit() -> None` + + Uninitialize the cdrom module. After you call this any existing ``CD`` + objects will no longer work. + + It is safe to call this function more than once. + + .. ## pygame.cdrom.quit ## + +.. function:: get_init + + | :sl:`true if the cdrom module is initialized` + | :sg:`get_init() -> bool` + + Test if the cdrom module is initialized or not. This is different than the + ``CD.init()`` since each drive must also be initialized individually. + + .. ## pygame.cdrom.get_init ## + +.. function:: get_count + + | :sl:`number of cd drives on the system` + | :sg:`get_count() -> count` + + Return the number of cd drives on the system. When you create ``CD`` objects + you need to pass an integer id that must be lower than this count. The count + will be 0 if there are no drives on the system. + + .. ## pygame.cdrom.get_count ## + +.. class:: CD + + | :sl:`class to manage a cdrom drive` + | :sg:`CD(id) -> CD` + + You can create a ``CD`` object for each cdrom on the system. Use + ``pygame.cdrom.get_count()`` to determine how many drives actually exist. + The id argument is an integer of the drive, starting at zero. + + The ``CD`` object is not initialized, you can only call ``CD.get_id()`` and + ``CD.get_name()`` on an uninitialized drive. + + It is safe to create multiple ``CD`` objects for the same drive, they will + all cooperate normally. + + .. method:: init + + | :sl:`initialize a cdrom drive for use` + | :sg:`init() -> None` + + Initialize the cdrom drive for use. The drive must be initialized for + most ``CD`` methods to work. Even if the rest of pygame has been + initialized. + + There may be a brief pause while the drive is initialized. Avoid + ``CD.init()`` if the program should not stop for a second or two. + + .. ## CD.init ## + + .. method:: quit + + | :sl:`uninitialize a cdrom drive for use` + | :sg:`quit() -> None` + + Uninitialize a drive for use. Call this when your program will not be + accessing the drive for awhile. + + .. ## CD.quit ## + + .. method:: get_init + + | :sl:`true if this cd device initialized` + | :sg:`get_init() -> bool` + + Test if this ``CDROM`` device is initialized. This is different than the + ``pygame.cdrom.init()`` since each drive must also be initialized + individually. + + .. ## CD.get_init ## + + .. method:: play + + | :sl:`start playing audio` + | :sg:`play(track, start=None, end=None) -> None` + + Playback audio from an audio cdrom in the drive. Besides the track number + argument, you can also pass a starting and ending time for playback. The + start and end time are in seconds, and can limit the section of an audio + track played. + + If you pass a start time but no end, the audio will play to the end of + the track. If you pass a start time and 'None' for the end time, the + audio will play to the end of the entire disc. + + See the ``CD.get_numtracks()`` and ``CD.get_track_audio()`` to find + tracks to playback. + + Note, track 0 is the first track on the ``CD``. Track numbers start at + zero. + + .. ## CD.play ## + + .. method:: stop + + | :sl:`stop audio playback` + | :sg:`stop() -> None` + + Stops playback of audio from the cdrom. This will also lose the current + playback position. This method does nothing if the drive isn't already + playing audio. + + .. ## CD.stop ## + + .. method:: pause + + | :sl:`temporarily stop audio playback` + | :sg:`pause() -> None` + + Temporarily stop audio playback on the ``CD``. The playback can be + resumed at the same point with the ``CD.resume()`` method. If the ``CD`` + is not playing this method does nothing. + + Note, track 0 is the first track on the ``CD``. Track numbers start at + zero. + + .. ## CD.pause ## + + .. method:: resume + + | :sl:`unpause audio playback` + | :sg:`resume() -> None` + + Unpause a paused ``CD``. If the ``CD`` is not paused or already playing, + this method does nothing. + + .. ## CD.resume ## + + .. method:: eject + + | :sl:`eject or open the cdrom drive` + | :sg:`eject() -> None` + + This will open the cdrom drive and eject the cdrom. If the drive is + playing or paused it will be stopped. + + .. ## CD.eject ## + + .. method:: get_id + + | :sl:`the index of the cdrom drive` + | :sg:`get_id() -> id` + + Returns the integer id that was used to create the ``CD`` instance. This + method can work on an uninitialized ``CD``. + + .. ## CD.get_id ## + + .. method:: get_name + + | :sl:`the system name of the cdrom drive` + | :sg:`get_name() -> name` + + Return the string name of the drive. This is the system name used to + represent the drive. It is often the drive letter or device name. This + method can work on an uninitialized ``CD``. + + .. ## CD.get_name ## + + .. method:: get_busy + + | :sl:`true if the drive is playing audio` + | :sg:`get_busy() -> bool` + + Returns True if the drive busy playing back audio. + + .. ## CD.get_busy ## + + .. method:: get_paused + + | :sl:`true if the drive is paused` + | :sg:`get_paused() -> bool` + + Returns True if the drive is currently paused. + + .. ## CD.get_paused ## + + .. method:: get_current + + | :sl:`the current audio playback position` + | :sg:`get_current() -> track, seconds` + + Returns both the current track and time of that track. This method works + when the drive is either playing or paused. + + Note, track 0 is the first track on the ``CD``. Track numbers start at + zero. + + .. ## CD.get_current ## + + .. method:: get_empty + + | :sl:`False if a cdrom is in the drive` + | :sg:`get_empty() -> bool` + + Return False if there is a cdrom currently in the drive. If the drive is + empty this will return True. + + .. ## CD.get_empty ## + + .. method:: get_numtracks + + | :sl:`the number of tracks on the cdrom` + | :sg:`get_numtracks() -> count` + + Return the number of tracks on the cdrom in the drive. This will return + zero of the drive is empty or has no tracks. + + .. ## CD.get_numtracks ## + + .. method:: get_track_audio + + | :sl:`true if the cdrom track has audio data` + | :sg:`get_track_audio(track) -> bool` + + Determine if a track on a cdrom contains audio data. You can also call + ``CD.num_tracks()`` and ``CD.get_all()`` to determine more information + about the cdrom. + + Note, track 0 is the first track on the ``CD``. Track numbers start at + zero. + + .. ## CD.get_track_audio ## + + .. method:: get_all + + | :sl:`get all track information` + | :sg:`get_all() -> [(audio, start, end, length), ...]` + + Return a list with information for every track on the cdrom. The + information consists of a tuple with four values. The audio value is True + if the track contains audio data. The start, end, and length values are + floating point numbers in seconds. Start and end represent absolute times + on the entire disc. + + .. ## CD.get_all ## + + .. method:: get_track_start + + | :sl:`start time of a cdrom track` + | :sg:`get_track_start(track) -> seconds` + + Return the absolute time in seconds where at start of the cdrom track. + + Note, track 0 is the first track on the ``CD``. Track numbers start at + zero. + + .. ## CD.get_track_start ## + + .. method:: get_track_length + + | :sl:`length of a cdrom track` + | :sg:`get_track_length(track) -> seconds` + + Return a floating point value in seconds of the length of the cdrom + track. + + Note, track 0 is the first track on the ``CD``. Track numbers start at + zero. + + .. ## CD.get_track_length ## + + .. ## pygame.cdrom.CD ## + +.. ## pygame.cdrom ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/color.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/color.rst.txt new file mode 100644 index 00000000..fc5c123a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/color.rst.txt @@ -0,0 +1,283 @@ +.. include:: common.txt + +:mod:`pygame.Color` +=================== + +.. currentmodule:: pygame + +.. class:: Color + + | :sl:`pygame object for color representations` + | :sg:`Color(r, g, b) -> Color` + | :sg:`Color(r, g, b, a=255) -> Color` + | :sg:`Color(color_value) -> Color` + + The ``Color`` class represents ``RGBA`` color values using a value range of + 0 to 255 inclusive. It allows basic arithmetic operations — binary + operations ``+``, ``-``, ``*``, ``//``, ``%``, and unary operation ``~`` — to + create new colors, supports conversions to other color spaces such as ``HSV`` + or ``HSL`` and lets you adjust single color channels. + Alpha defaults to 255 (fully opaque) when not given. + The arithmetic operations and ``correct_gamma()`` method preserve subclasses. + For the binary operators, the class of the returned color is that of the + left hand color object of the operator. + + Color objects support equality comparison with other color objects and 3 or + 4 element tuples of integers. There was a bug in pygame 1.8.1 + where the default alpha was 0, not 255 like previously. + + Color objects export the C level array interface. The interface exports a + read-only one dimensional unsigned byte array of the same assigned length + as the color. The new buffer interface is also exported, with the same + characteristics as the array interface. + + The floor division, ``//``, and modulus, ``%``, operators do not raise + an exception for division by zero. Instead, if a color, or alpha, channel + in the right hand color is 0, then the result is 0. For example: :: + + # These expressions are True + Color(255, 255, 255, 255) // Color(0, 64, 64, 64) == Color(0, 3, 3, 3) + Color(255, 255, 255, 255) % Color(64, 64, 64, 0) == Color(63, 63, 63, 0) + + Use ``int(color)`` to return the immutable integer value of the color, + usable as a ``dict`` key. This integer value differs from the mapped + pixel values of :meth:`pygame.Surface.get_at_mapped`, + :meth:`pygame.Surface.map_rgb` and :meth:`pygame.Surface.unmap_rgb`. + It can be passed as a ``color_value`` argument to :class:`Color` + (useful with sets). + + See :doc:`color_list` for samples of the available named colors. + + :param int r: red value in the range of 0 to 255 inclusive + :param int g: green value in the range of 0 to 255 inclusive + :param int b: blue value in the range of 0 to 255 inclusive + :param int a: (optional) alpha value in the range of 0 to 255 inclusive, + default is 255 + :param color_value: color value (see note below for the supported formats) + + .. note:: + Supported ``color_value`` formats: + | - **Color object:** clones the given :class:`Color` object + | - **Color name: str:** name of the color to use, e.g. ``'red'`` + (all the supported name strings can be found in the + :doc:`color_list`, with sample swatches) + | - **HTML color format str:** ``'#rrggbbaa'`` or ``'#rrggbb'``, + where rr, gg, bb, and aa are 2-digit hex numbers in the range + of 0 to 0xFF inclusive, the aa (alpha) value defaults to 0xFF + if not provided + | - **hex number str:** ``'0xrrggbbaa'`` or ``'0xrrggbb'``, where + rr, gg, bb, and aa are 2-digit hex numbers in the range of 0x00 + to 0xFF inclusive, the aa (alpha) value defaults to 0xFF if not + provided + | - **int:** int value of the color to use, using hex numbers can + make this parameter more readable, e.g. ``0xrrggbbaa``, where rr, + gg, bb, and aa are 2-digit hex numbers in the range of 0x00 to + 0xFF inclusive, note that the aa (alpha) value is not optional for + the int format and must be provided + | - **tuple/list of int color values:** ``(R, G, B, A)`` or + ``(R, G, B)``, where R, G, B, and A are int values in the range of + 0 to 255 inclusive, the A (alpha) value defaults to 255 if not + provided + + :type color_value: Color or str or int or tuple(int, int, int, [int]) or + list(int, int, int, [int]) + + :returns: a newly created :class:`Color` object + :rtype: Color + + .. versionchanged:: 2.0.0 + Support for tuples, lists, and :class:`Color` objects when creating + :class:`Color` objects. + .. versionchanged:: 1.9.2 Color objects export the C level array interface. + .. versionchanged:: 1.9.0 Color objects support 4-element tuples of integers. + .. versionchanged:: 1.8.1 New implementation of the class. + + .. attribute:: r + + | :sl:`Gets or sets the red value of the Color.` + | :sg:`r -> int` + + The red value of the Color. + + .. ## Color.r ## + + .. attribute:: g + + | :sl:`Gets or sets the green value of the Color.` + | :sg:`g -> int` + + The green value of the Color. + + .. ## Color.g ## + + .. attribute:: b + + | :sl:`Gets or sets the blue value of the Color.` + | :sg:`b -> int` + + The blue value of the Color. + + .. ## Color.b ## + + .. attribute:: a + + | :sl:`Gets or sets the alpha value of the Color.` + | :sg:`a -> int` + + The alpha value of the Color. + + .. ## Color.a ## + + .. attribute:: cmy + + | :sl:`Gets or sets the CMY representation of the Color.` + | :sg:`cmy -> tuple` + + The ``CMY`` representation of the Color. The ``CMY`` components are in + the ranges ``C`` = [0, 1], ``M`` = [0, 1], ``Y`` = [0, 1]. Note that this + will not return the absolutely exact ``CMY`` values for the set ``RGB`` + values in all cases. Due to the ``RGB`` mapping from 0-255 and the + ``CMY`` mapping from 0-1 rounding errors may cause the ``CMY`` values to + differ slightly from what you might expect. + + .. ## Color.cmy ## + + .. attribute:: hsva + + | :sl:`Gets or sets the HSVA representation of the Color.` + | :sg:`hsva -> tuple` + + The ``HSVA`` representation of the Color. The ``HSVA`` components are in + the ranges ``H`` = [0, 360], ``S`` = [0, 100], ``V`` = [0, 100], A = [0, + 100]. Note that this will not return the absolutely exact ``HSV`` values + for the set ``RGB`` values in all cases. Due to the ``RGB`` mapping from + 0-255 and the ``HSV`` mapping from 0-100 and 0-360 rounding errors may + cause the ``HSV`` values to differ slightly from what you might expect. + + .. ## Color.hsva ## + + .. attribute:: hsla + + | :sl:`Gets or sets the HSLA representation of the Color.` + | :sg:`hsla -> tuple` + + The ``HSLA`` representation of the Color. The ``HSLA`` components are in + the ranges ``H`` = [0, 360], ``S`` = [0, 100], ``L`` = [0, 100], A = [0, + 100]. Note that this will not return the absolutely exact ``HSL`` values + for the set ``RGB`` values in all cases. Due to the ``RGB`` mapping from + 0-255 and the ``HSL`` mapping from 0-100 and 0-360 rounding errors may + cause the ``HSL`` values to differ slightly from what you might expect. + + .. ## Color.hsla ## + + .. attribute:: i1i2i3 + + | :sl:`Gets or sets the I1I2I3 representation of the Color.` + | :sg:`i1i2i3 -> tuple` + + The ``I1I2I3`` representation of the Color. The ``I1I2I3`` components are + in the ranges ``I1`` = [0, 1], ``I2`` = [-0.5, 0.5], ``I3`` = [-0.5, + 0.5]. Note that this will not return the absolutely exact ``I1I2I3`` + values for the set ``RGB`` values in all cases. Due to the ``RGB`` + mapping from 0-255 and the ``I1I2I3`` mapping from 0-1 rounding errors + may cause the ``I1I2I3`` values to differ slightly from what you might + expect. + + .. ## Color.i1i2i3 ## + + .. method:: normalize + + | :sl:`Returns the normalized RGBA values of the Color.` + | :sg:`normalize() -> tuple` + + Returns the normalized ``RGBA`` values of the Color as floating point + values. + + .. ## Color.normalize ## + + .. method:: correct_gamma + + | :sl:`Applies a certain gamma value to the Color.` + | :sg:`correct_gamma (gamma) -> Color` + + Applies a certain gamma value to the Color and returns a new Color with + the adjusted ``RGBA`` values. + + .. ## Color.correct_gamma ## + + .. method:: set_length + + | :sl:`Set the number of elements in the Color to 1,2,3, or 4.` + | :sg:`set_length(len) -> None` + + DEPRECATED: You may unpack the values you need like so, + ``r, g, b, _ = pygame.Color(100, 100, 100)`` + If you only want r, g and b + Or + ``r, g, *_ = pygame.Color(100, 100, 100)`` + if you only want r and g + + The default Color length is 4. Colors can have lengths 1,2,3 or 4. This + is useful if you want to unpack to r,g,b and not r,g,b,a. If you want to + get the length of a Color do ``len(acolor)``. + + .. deprecated:: 2.1.3 + .. versionadded:: 1.9.0 + + .. ## Color.set_length ## + + .. method:: grayscale + + | :sl:`returns the grayscale of a Color` + | :sg:`grayscale() -> Color` + + Returns a Color which represents the grayscaled version of self using the luminosity formula which weights red, green and blue according to their wavelengths.. + + .. ## Color.grayscale ## + + .. method:: lerp + + | :sl:`returns a linear interpolation to the given Color.` + | :sg:`lerp(Color, float) -> Color` + + Returns a Color which is a linear interpolation between self and the + given Color in RGBA space. The second parameter determines how far + between self and other the result is going to be. + It must be a value between 0 and 1 where 0 means self and 1 means + other will be returned. + + .. versionadded:: 2.0.1 + + .. ## Color.lerp ## + + .. method:: premul_alpha + + | :sl:`returns a Color where the r,g,b components have been multiplied by the alpha.` + | :sg:`premul_alpha() -> Color` + + Returns a new Color where each of the red, green and blue colour + channels have been multiplied by the alpha channel of the original + color. The alpha channel remains unchanged. + + This is useful when working with the ``BLEND_PREMULTIPLIED`` blending mode + flag for :meth:`pygame.Surface.blit()`, which assumes that all surfaces using + it are using pre-multiplied alpha colors. + + .. versionadded:: 2.0.0 + + .. ## Color.premul_alpha ## + + .. method:: update + + | :sl:`Sets the elements of the color` + | :sg:`update(r, g, b) -> None` + | :sg:`update(r, g, b, a=255) -> None` + | :sg:`update(color_value) -> None` + + Sets the elements of the color. See parameters for :meth:`pygame.Color` for the + parameters of this function. If the alpha value was not set it will not change. + + .. versionadded:: 2.0.1 + + .. ## Color.update ## + .. ## pygame.Color ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/color_list.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/color_list.rst.txt new file mode 100644 index 00000000..b6cf2895 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/color_list.rst.txt @@ -0,0 +1,2014 @@ +.. include:: common.txt + +Named Colors +============ + +.. raw:: html + + + +:doc:`color` lets you specify any of these named colors when creating a new +``pygame.Color`` (taken from the +`colordict module `_). + +.. role:: aliceblue +.. role:: antiquewhite +.. role:: antiquewhite1 +.. role:: antiquewhite2 +.. role:: antiquewhite3 +.. role:: antiquewhite4 +.. role:: aqua +.. role:: aquamarine +.. role:: aquamarine1 +.. role:: aquamarine2 +.. role:: aquamarine3 +.. role:: aquamarine4 +.. role:: azure +.. role:: azure1 +.. role:: azure2 +.. role:: azure3 +.. role:: azure4 +.. role:: beige +.. role:: bisque +.. role:: bisque1 +.. role:: bisque2 +.. role:: bisque3 +.. role:: bisque4 +.. role:: black +.. role:: blanchedalmond +.. role:: blue +.. role:: blue1 +.. role:: blue2 +.. role:: blue3 +.. role:: blue4 +.. role:: blueviolet +.. role:: brown +.. role:: brown1 +.. role:: brown2 +.. role:: brown3 +.. role:: brown4 +.. role:: burlywood +.. role:: burlywood1 +.. role:: burlywood2 +.. role:: burlywood3 +.. role:: burlywood4 +.. role:: cadetblue +.. role:: cadetblue1 +.. role:: cadetblue2 +.. role:: cadetblue3 +.. role:: cadetblue4 +.. role:: chartreuse +.. role:: chartreuse1 +.. role:: chartreuse2 +.. role:: chartreuse3 +.. role:: chartreuse4 +.. role:: chocolate +.. role:: chocolate1 +.. role:: chocolate2 +.. role:: chocolate3 +.. role:: chocolate4 +.. role:: coral +.. role:: coral1 +.. role:: coral2 +.. role:: coral3 +.. role:: coral4 +.. role:: cornflowerblue +.. role:: cornsilk +.. role:: cornsilk1 +.. role:: cornsilk2 +.. role:: cornsilk3 +.. role:: cornsilk4 +.. role:: crimson +.. role:: cyan +.. role:: cyan1 +.. role:: cyan2 +.. role:: cyan3 +.. role:: cyan4 +.. role:: darkblue +.. role:: darkcyan +.. role:: darkgoldenrod +.. role:: darkgoldenrod1 +.. role:: darkgoldenrod2 +.. role:: darkgoldenrod3 +.. role:: darkgoldenrod4 +.. role:: darkgray +.. role:: darkgreen +.. role:: darkgrey +.. role:: darkkhaki +.. role:: darkmagenta +.. role:: darkolivegreen +.. role:: darkolivegreen1 +.. role:: darkolivegreen2 +.. role:: darkolivegreen3 +.. role:: darkolivegreen4 +.. role:: darkorange +.. role:: darkorange1 +.. role:: darkorange2 +.. role:: darkorange3 +.. role:: darkorange4 +.. role:: darkorchid +.. role:: darkorchid1 +.. role:: darkorchid2 +.. role:: darkorchid3 +.. role:: darkorchid4 +.. role:: darkred +.. role:: darksalmon +.. role:: darkseagreen +.. role:: darkseagreen1 +.. role:: darkseagreen2 +.. role:: darkseagreen3 +.. role:: darkseagreen4 +.. role:: darkslateblue +.. role:: darkslategray +.. role:: darkslategray1 +.. role:: darkslategray2 +.. role:: darkslategray3 +.. role:: darkslategray4 +.. role:: darkslategrey +.. role:: darkturquoise +.. role:: darkviolet +.. role:: deeppink +.. role:: deeppink1 +.. role:: deeppink2 +.. role:: deeppink3 +.. role:: deeppink4 +.. role:: deepskyblue +.. role:: deepskyblue1 +.. role:: deepskyblue2 +.. role:: deepskyblue3 +.. role:: deepskyblue4 +.. role:: dimgray +.. role:: dimgrey +.. role:: dodgerblue +.. role:: dodgerblue1 +.. role:: dodgerblue2 +.. role:: dodgerblue3 +.. role:: dodgerblue4 +.. role:: firebrick +.. role:: firebrick1 +.. role:: firebrick2 +.. role:: firebrick3 +.. role:: firebrick4 +.. role:: floralwhite +.. role:: forestgreen +.. role:: fuchsia +.. role:: gainsboro +.. role:: ghostwhite +.. role:: gold +.. role:: gold1 +.. role:: gold2 +.. role:: gold3 +.. role:: gold4 +.. role:: goldenrod +.. role:: goldenrod1 +.. role:: goldenrod2 +.. role:: goldenrod3 +.. role:: goldenrod4 +.. role:: gray +.. role:: gray0 +.. role:: gray1 +.. role:: gray2 +.. role:: gray3 +.. role:: gray4 +.. role:: gray5 +.. role:: gray6 +.. role:: gray7 +.. role:: gray8 +.. role:: gray9 +.. role:: gray10 +.. role:: gray11 +.. role:: gray12 +.. role:: gray13 +.. role:: gray14 +.. role:: gray15 +.. role:: gray16 +.. role:: gray17 +.. role:: gray18 +.. role:: gray19 +.. role:: gray20 +.. role:: gray21 +.. role:: gray22 +.. role:: gray23 +.. role:: gray24 +.. role:: gray25 +.. role:: gray26 +.. role:: gray27 +.. role:: gray28 +.. role:: gray29 +.. role:: gray30 +.. role:: gray31 +.. role:: gray32 +.. role:: gray33 +.. role:: gray34 +.. role:: gray35 +.. role:: gray36 +.. role:: gray37 +.. role:: gray38 +.. role:: gray39 +.. role:: gray40 +.. role:: gray41 +.. role:: gray42 +.. role:: gray43 +.. role:: gray44 +.. role:: gray45 +.. role:: gray46 +.. role:: gray47 +.. role:: gray48 +.. role:: gray49 +.. role:: gray50 +.. role:: gray51 +.. role:: gray52 +.. role:: gray53 +.. role:: gray54 +.. role:: gray55 +.. role:: gray56 +.. role:: gray57 +.. role:: gray58 +.. role:: gray59 +.. role:: gray60 +.. role:: gray61 +.. role:: gray62 +.. role:: gray63 +.. role:: gray64 +.. role:: gray65 +.. role:: gray66 +.. role:: gray67 +.. role:: gray68 +.. role:: gray69 +.. role:: gray70 +.. role:: gray71 +.. role:: gray72 +.. role:: gray73 +.. role:: gray74 +.. role:: gray75 +.. role:: gray76 +.. role:: gray77 +.. role:: gray78 +.. role:: gray79 +.. role:: gray80 +.. role:: gray81 +.. role:: gray82 +.. role:: gray83 +.. role:: gray84 +.. role:: gray85 +.. role:: gray86 +.. role:: gray87 +.. role:: gray88 +.. role:: gray89 +.. role:: gray90 +.. role:: gray91 +.. role:: gray92 +.. role:: gray93 +.. role:: gray94 +.. role:: gray95 +.. role:: gray96 +.. role:: gray97 +.. role:: gray98 +.. role:: gray99 +.. role:: gray100 +.. role:: green +.. role:: green1 +.. role:: green2 +.. role:: green3 +.. role:: green4 +.. role:: greenyellow +.. role:: grey +.. role:: grey0 +.. role:: grey1 +.. role:: grey2 +.. role:: grey3 +.. role:: grey4 +.. role:: grey5 +.. role:: grey6 +.. role:: grey7 +.. role:: grey8 +.. role:: grey9 +.. role:: grey10 +.. role:: grey11 +.. role:: grey12 +.. role:: grey13 +.. role:: grey14 +.. role:: grey15 +.. role:: grey16 +.. role:: grey17 +.. role:: grey18 +.. role:: grey19 +.. role:: grey20 +.. role:: grey21 +.. role:: grey22 +.. role:: grey23 +.. role:: grey24 +.. role:: grey25 +.. role:: grey26 +.. role:: grey27 +.. role:: grey28 +.. role:: grey29 +.. role:: grey30 +.. role:: grey31 +.. role:: grey32 +.. role:: grey33 +.. role:: grey34 +.. role:: grey35 +.. role:: grey36 +.. role:: grey37 +.. role:: grey38 +.. role:: grey39 +.. role:: grey40 +.. role:: grey41 +.. role:: grey42 +.. role:: grey43 +.. role:: grey44 +.. role:: grey45 +.. role:: grey46 +.. role:: grey47 +.. role:: grey48 +.. role:: grey49 +.. role:: grey50 +.. role:: grey51 +.. role:: grey52 +.. role:: grey53 +.. role:: grey54 +.. role:: grey55 +.. role:: grey56 +.. role:: grey57 +.. role:: grey58 +.. role:: grey59 +.. role:: grey60 +.. role:: grey61 +.. role:: grey62 +.. role:: grey63 +.. role:: grey64 +.. role:: grey65 +.. role:: grey66 +.. role:: grey67 +.. role:: grey68 +.. role:: grey69 +.. role:: grey70 +.. role:: grey71 +.. role:: grey72 +.. role:: grey73 +.. role:: grey74 +.. role:: grey75 +.. role:: grey76 +.. role:: grey77 +.. role:: grey78 +.. role:: grey79 +.. role:: grey80 +.. role:: grey81 +.. role:: grey82 +.. role:: grey83 +.. role:: grey84 +.. role:: grey85 +.. role:: grey86 +.. role:: grey87 +.. role:: grey88 +.. role:: grey89 +.. role:: grey90 +.. role:: grey91 +.. role:: grey92 +.. role:: grey93 +.. role:: grey94 +.. role:: grey95 +.. role:: grey96 +.. role:: grey97 +.. role:: grey98 +.. role:: grey99 +.. role:: grey100 +.. role:: honeydew +.. role:: honeydew1 +.. role:: honeydew2 +.. role:: honeydew3 +.. role:: honeydew4 +.. role:: hotpink +.. role:: hotpink1 +.. role:: hotpink2 +.. role:: hotpink3 +.. role:: hotpink4 +.. role:: indianred +.. role:: indianred1 +.. role:: indianred2 +.. role:: indianred3 +.. role:: indianred4 +.. role:: indigo +.. role:: ivory +.. role:: ivory1 +.. role:: ivory2 +.. role:: ivory3 +.. role:: ivory4 +.. role:: khaki +.. role:: khaki1 +.. role:: khaki2 +.. role:: khaki3 +.. role:: khaki4 +.. role:: lavender +.. role:: lavenderblush +.. role:: lavenderblush1 +.. role:: lavenderblush2 +.. role:: lavenderblush3 +.. role:: lavenderblush4 +.. role:: lawngreen +.. role:: lemonchiffon +.. role:: lemonchiffon1 +.. role:: lemonchiffon2 +.. role:: lemonchiffon3 +.. role:: lemonchiffon4 +.. role:: lightblue +.. role:: lightblue1 +.. role:: lightblue2 +.. role:: lightblue3 +.. role:: lightblue4 +.. role:: lightcoral +.. role:: lightcyan +.. role:: lightcyan1 +.. role:: lightcyan2 +.. role:: lightcyan3 +.. role:: lightcyan4 +.. role:: lightgoldenrod +.. role:: lightgoldenrod1 +.. role:: lightgoldenrod2 +.. role:: lightgoldenrod3 +.. role:: lightgoldenrod4 +.. role:: lightgoldenrodyellow +.. role:: lightgray +.. role:: lightgreen +.. role:: lightgrey +.. role:: lightpink +.. role:: lightpink1 +.. role:: lightpink2 +.. role:: lightpink3 +.. role:: lightpink4 +.. role:: lightsalmon +.. role:: lightsalmon1 +.. role:: lightsalmon2 +.. role:: lightsalmon3 +.. role:: lightsalmon4 +.. role:: lightseagreen +.. role:: lightskyblue +.. role:: lightskyblue1 +.. role:: lightskyblue2 +.. role:: lightskyblue3 +.. role:: lightskyblue4 +.. role:: lightslateblue +.. role:: lightslategray +.. role:: lightslategrey +.. role:: lightsteelblue +.. role:: lightsteelblue1 +.. role:: lightsteelblue2 +.. role:: lightsteelblue3 +.. role:: lightsteelblue4 +.. role:: lightyellow +.. role:: lightyellow1 +.. role:: lightyellow2 +.. role:: lightyellow3 +.. role:: lightyellow4 +.. role:: limegreen +.. role:: lime +.. role:: linen +.. role:: magenta +.. role:: magenta1 +.. role:: magenta2 +.. role:: magenta3 +.. role:: magenta4 +.. role:: maroon +.. role:: maroon1 +.. role:: maroon2 +.. role:: maroon3 +.. role:: maroon4 +.. role:: mediumaquamarine +.. role:: mediumblue +.. role:: mediumorchid +.. role:: mediumorchid1 +.. role:: mediumorchid2 +.. role:: mediumorchid3 +.. role:: mediumorchid4 +.. role:: mediumpurple +.. role:: mediumpurple1 +.. role:: mediumpurple2 +.. role:: mediumpurple3 +.. role:: mediumpurple4 +.. role:: mediumseagreen +.. role:: mediumslateblue +.. role:: mediumspringgreen +.. role:: mediumturquoise +.. role:: mediumvioletred +.. role:: midnightblue +.. role:: mintcream +.. role:: mistyrose +.. role:: mistyrose1 +.. role:: mistyrose2 +.. role:: mistyrose3 +.. role:: mistyrose4 +.. role:: moccasin +.. role:: navajowhite +.. role:: navajowhite1 +.. role:: navajowhite2 +.. role:: navajowhite3 +.. role:: navajowhite4 +.. role:: navy +.. role:: navyblue +.. role:: oldlace +.. role:: olive +.. role:: olivedrab +.. role:: olivedrab1 +.. role:: olivedrab2 +.. role:: olivedrab3 +.. role:: olivedrab4 +.. role:: orange +.. role:: orange1 +.. role:: orange2 +.. role:: orange3 +.. role:: orange4 +.. role:: orangered +.. role:: orangered1 +.. role:: orangered2 +.. role:: orangered3 +.. role:: orangered4 +.. role:: orchid +.. role:: orchid1 +.. role:: orchid2 +.. role:: orchid3 +.. role:: orchid4 +.. role:: palegoldenrod +.. role:: palegreen +.. role:: palegreen1 +.. role:: palegreen2 +.. role:: palegreen3 +.. role:: palegreen4 +.. role:: paleturquoise +.. role:: paleturquoise1 +.. role:: paleturquoise2 +.. role:: paleturquoise3 +.. role:: paleturquoise4 +.. role:: palevioletred +.. role:: palevioletred1 +.. role:: palevioletred2 +.. role:: palevioletred3 +.. role:: palevioletred4 +.. role:: papayawhip +.. role:: peachpuff +.. role:: peachpuff1 +.. role:: peachpuff2 +.. role:: peachpuff3 +.. role:: peachpuff4 +.. role:: peru +.. role:: pink +.. role:: pink1 +.. role:: pink2 +.. role:: pink3 +.. role:: pink4 +.. role:: plum +.. role:: plum1 +.. role:: plum2 +.. role:: plum3 +.. role:: plum4 +.. role:: powderblue +.. role:: purple +.. role:: purple1 +.. role:: purple2 +.. role:: purple3 +.. role:: purple4 +.. role:: red +.. role:: red1 +.. role:: red2 +.. role:: red3 +.. role:: red4 +.. role:: rosybrown +.. role:: rosybrown1 +.. role:: rosybrown2 +.. role:: rosybrown3 +.. role:: rosybrown4 +.. role:: royalblue +.. role:: royalblue1 +.. role:: royalblue2 +.. role:: royalblue3 +.. role:: royalblue4 +.. role:: saddlebrown +.. role:: salmon +.. role:: salmon1 +.. role:: salmon2 +.. role:: salmon3 +.. role:: salmon4 +.. role:: sandybrown +.. role:: seagreen +.. role:: seagreen1 +.. role:: seagreen2 +.. role:: seagreen3 +.. role:: seagreen4 +.. role:: seashell +.. role:: seashell1 +.. role:: seashell2 +.. role:: seashell3 +.. role:: seashell4 +.. role:: sienna +.. role:: sienna1 +.. role:: sienna2 +.. role:: sienna3 +.. role:: sienna4 +.. role:: silver +.. role:: skyblue +.. role:: skyblue1 +.. role:: skyblue2 +.. role:: skyblue3 +.. role:: skyblue4 +.. role:: slateblue +.. role:: slateblue1 +.. role:: slateblue2 +.. role:: slateblue3 +.. role:: slateblue4 +.. role:: slategray +.. role:: slategray1 +.. role:: slategray2 +.. role:: slategray3 +.. role:: slategray4 +.. role:: slategrey +.. role:: snow +.. role:: snow1 +.. role:: snow2 +.. role:: snow3 +.. role:: snow4 +.. role:: springgreen +.. role:: springgreen1 +.. role:: springgreen2 +.. role:: springgreen3 +.. role:: springgreen4 +.. role:: steelblue +.. role:: steelblue1 +.. role:: steelblue2 +.. role:: steelblue3 +.. role:: steelblue4 +.. role:: tan +.. role:: tan1 +.. role:: tan2 +.. role:: tan3 +.. role:: tan4 +.. role:: teal +.. role:: thistle +.. role:: thistle1 +.. role:: thistle2 +.. role:: thistle3 +.. role:: thistle4 +.. role:: tomato +.. role:: tomato1 +.. role:: tomato2 +.. role:: tomato3 +.. role:: tomato4 +.. role:: turquoise +.. role:: turquoise1 +.. role:: turquoise2 +.. role:: turquoise3 +.. role:: turquoise4 +.. role:: violet +.. role:: violetred +.. role:: violetred1 +.. role:: violetred2 +.. role:: violetred3 +.. role:: violetred4 +.. role:: wheat +.. role:: wheat1 +.. role:: wheat2 +.. role:: wheat3 +.. role:: wheat4 +.. role:: white +.. role:: whitesmoke +.. role:: yellow +.. role:: yellow1 +.. role:: yellow2 +.. role:: yellow3 +.. role:: yellow4 +.. role:: yellowgreen + +========================== ====================================================================================================== +Name Color +========================== ====================================================================================================== +``aliceblue`` :aliceblue:`████████` +``antiquewhite`` :antiquewhite:`████████` +``antiquewhite1`` :antiquewhite1:`████████` +``antiquewhite2`` :antiquewhite2:`████████` +``antiquewhite3`` :antiquewhite3:`████████` +``antiquewhite4`` :antiquewhite4:`████████` +``aqua`` :aqua:`████████` +``aquamarine`` :aquamarine:`████████` +``aquamarine1`` :aquamarine1:`████████` +``aquamarine2`` :aquamarine2:`████████` +``aquamarine3`` :aquamarine3:`████████` +``aquamarine4`` :aquamarine4:`████████` +``azure`` :azure:`████████` +``azure1`` :azure1:`████████` +``azure2`` :azure2:`████████` +``azure3`` :azure3:`████████` +``azure4`` :azure4:`████████` +``beige`` :beige:`████████` +``bisque`` :bisque:`████████` +``bisque1`` :bisque1:`████████` +``bisque2`` :bisque2:`████████` +``bisque3`` :bisque3:`████████` +``bisque4`` :bisque4:`████████` +``black`` :black:`████████` +``blanchedalmond`` :blanchedalmond:`████████` +``blue`` :blue:`████████` +``blue1`` :blue1:`████████` +``blue2`` :blue2:`████████` +``blue3`` :blue3:`████████` +``blue4`` :blue4:`████████` +``blueviolet`` :blueviolet:`████████` +``brown`` :brown:`████████` +``brown1`` :brown1:`████████` +``brown2`` :brown2:`████████` +``brown3`` :brown3:`████████` +``brown4`` :brown4:`████████` +``burlywood`` :burlywood:`████████` +``burlywood1`` :burlywood1:`████████` +``burlywood2`` :burlywood2:`████████` +``burlywood3`` :burlywood3:`████████` +``burlywood4`` :burlywood4:`████████` +``cadetblue`` :cadetblue:`████████` +``cadetblue1`` :cadetblue1:`████████` +``cadetblue2`` :cadetblue2:`████████` +``cadetblue3`` :cadetblue3:`████████` +``cadetblue4`` :cadetblue4:`████████` +``chartreuse`` :chartreuse:`████████` +``chartreuse1`` :chartreuse1:`████████` +``chartreuse2`` :chartreuse2:`████████` +``chartreuse3`` :chartreuse3:`████████` +``chartreuse4`` :chartreuse4:`████████` +``chocolate`` :chocolate:`████████` +``chocolate1`` :chocolate1:`████████` +``chocolate2`` :chocolate2:`████████` +``chocolate3`` :chocolate3:`████████` +``chocolate4`` :chocolate4:`████████` +``coral`` :coral:`████████` +``coral1`` :coral1:`████████` +``coral2`` :coral2:`████████` +``coral3`` :coral3:`████████` +``coral4`` :coral4:`████████` +``cornflowerblue`` :cornflowerblue:`████████` +``cornsilk`` :cornsilk:`████████` +``cornsilk1`` :cornsilk1:`████████` +``cornsilk2`` :cornsilk2:`████████` +``cornsilk3`` :cornsilk3:`████████` +``cornsilk4`` :cornsilk4:`████████` +``crimson`` :crimson:`████████` +``cyan`` :cyan:`████████` +``cyan1`` :cyan1:`████████` +``cyan2`` :cyan2:`████████` +``cyan3`` :cyan3:`████████` +``cyan4`` :cyan4:`████████` +``darkblue`` :darkblue:`████████` +``darkcyan`` :darkcyan:`████████` +``darkgoldenrod`` :darkgoldenrod:`████████` +``darkgoldenrod1`` :darkgoldenrod1:`████████` +``darkgoldenrod2`` :darkgoldenrod2:`████████` +``darkgoldenrod3`` :darkgoldenrod3:`████████` +``darkgoldenrod4`` :darkgoldenrod4:`████████` +``darkgray`` :darkgray:`████████` +``darkgreen`` :darkgreen:`████████` +``darkgrey`` :darkgrey:`████████` +``darkkhaki`` :darkkhaki:`████████` +``darkmagenta`` :darkmagenta:`████████` +``darkolivegreen`` :darkolivegreen:`████████` +``darkolivegreen1`` :darkolivegreen1:`████████` +``darkolivegreen2`` :darkolivegreen2:`████████` +``darkolivegreen3`` :darkolivegreen3:`████████` +``darkolivegreen4`` :darkolivegreen4:`████████` +``darkorange`` :darkorange:`████████` +``darkorange1`` :darkorange1:`████████` +``darkorange2`` :darkorange2:`████████` +``darkorange3`` :darkorange3:`████████` +``darkorange4`` :darkorange4:`████████` +``darkorchid`` :darkorchid:`████████` +``darkorchid1`` :darkorchid1:`████████` +``darkorchid2`` :darkorchid2:`████████` +``darkorchid3`` :darkorchid3:`████████` +``darkorchid4`` :darkorchid4:`████████` +``darkred`` :darkred:`████████` +``darksalmon`` :darksalmon:`████████` +``darkseagreen`` :darkseagreen:`████████` +``darkseagreen1`` :darkseagreen1:`████████` +``darkseagreen2`` :darkseagreen2:`████████` +``darkseagreen3`` :darkseagreen3:`████████` +``darkseagreen4`` :darkseagreen4:`████████` +``darkslateblue`` :darkslateblue:`████████` +``darkslategray`` :darkslategray:`████████` +``darkslategray1`` :darkslategray1:`████████` +``darkslategray2`` :darkslategray2:`████████` +``darkslategray3`` :darkslategray3:`████████` +``darkslategray4`` :darkslategray4:`████████` +``darkslategrey`` :darkslategrey:`████████` +``darkturquoise`` :darkturquoise:`████████` +``darkviolet`` :darkviolet:`████████` +``deeppink`` :deeppink:`████████` +``deeppink1`` :deeppink1:`████████` +``deeppink2`` :deeppink2:`████████` +``deeppink3`` :deeppink3:`████████` +``deeppink4`` :deeppink4:`████████` +``deepskyblue`` :deepskyblue:`████████` +``deepskyblue1`` :deepskyblue1:`████████` +``deepskyblue2`` :deepskyblue2:`████████` +``deepskyblue3`` :deepskyblue3:`████████` +``deepskyblue4`` :deepskyblue4:`████████` +``dimgray`` :dimgray:`████████` +``dimgrey`` :dimgrey:`████████` +``dodgerblue`` :dodgerblue:`████████` +``dodgerblue1`` :dodgerblue1:`████████` +``dodgerblue2`` :dodgerblue2:`████████` +``dodgerblue3`` :dodgerblue3:`████████` +``dodgerblue4`` :dodgerblue4:`████████` +``firebrick`` :firebrick:`████████` +``firebrick1`` :firebrick1:`████████` +``firebrick2`` :firebrick2:`████████` +``firebrick3`` :firebrick3:`████████` +``firebrick4`` :firebrick4:`████████` +``floralwhite`` :floralwhite:`████████` +``forestgreen`` :forestgreen:`████████` +``fuchsia`` :fuchsia:`████████` +``gainsboro`` :gainsboro:`████████` +``ghostwhite`` :ghostwhite:`████████` +``gold`` :gold:`████████` +``gold1`` :gold1:`████████` +``gold2`` :gold2:`████████` +``gold3`` :gold3:`████████` +``gold4`` :gold4:`████████` +``goldenrod`` :goldenrod:`████████` +``goldenrod1`` :goldenrod1:`████████` +``goldenrod2`` :goldenrod2:`████████` +``goldenrod3`` :goldenrod3:`████████` +``goldenrod4`` :goldenrod4:`████████` +``gray`` :gray:`████████` +``gray0`` :gray0:`████████` +``gray1`` :gray1:`████████` +``gray2`` :gray2:`████████` +``gray3`` :gray3:`████████` +``gray4`` :gray4:`████████` +``gray5`` :gray5:`████████` +``gray6`` :gray6:`████████` +``gray7`` :gray7:`████████` +``gray8`` :gray8:`████████` +``gray9`` :gray9:`████████` +``gray10`` :gray10:`████████` +``gray11`` :gray11:`████████` +``gray12`` :gray12:`████████` +``gray13`` :gray13:`████████` +``gray14`` :gray14:`████████` +``gray15`` :gray15:`████████` +``gray16`` :gray16:`████████` +``gray17`` :gray17:`████████` +``gray18`` :gray18:`████████` +``gray19`` :gray19:`████████` +``gray20`` :gray20:`████████` +``gray21`` :gray21:`████████` +``gray22`` :gray22:`████████` +``gray23`` :gray23:`████████` +``gray24`` :gray24:`████████` +``gray25`` :gray25:`████████` +``gray26`` :gray26:`████████` +``gray27`` :gray27:`████████` +``gray28`` :gray28:`████████` +``gray29`` :gray29:`████████` +``gray30`` :gray30:`████████` +``gray31`` :gray31:`████████` +``gray32`` :gray32:`████████` +``gray33`` :gray33:`████████` +``gray34`` :gray34:`████████` +``gray35`` :gray35:`████████` +``gray36`` :gray36:`████████` +``gray37`` :gray37:`████████` +``gray38`` :gray38:`████████` +``gray39`` :gray39:`████████` +``gray40`` :gray40:`████████` +``gray41`` :gray41:`████████` +``gray42`` :gray42:`████████` +``gray43`` :gray43:`████████` +``gray44`` :gray44:`████████` +``gray45`` :gray45:`████████` +``gray46`` :gray46:`████████` +``gray47`` :gray47:`████████` +``gray48`` :gray48:`████████` +``gray49`` :gray49:`████████` +``gray50`` :gray50:`████████` +``gray51`` :gray51:`████████` +``gray52`` :gray52:`████████` +``gray53`` :gray53:`████████` +``gray54`` :gray54:`████████` +``gray55`` :gray55:`████████` +``gray56`` :gray56:`████████` +``gray57`` :gray57:`████████` +``gray58`` :gray58:`████████` +``gray59`` :gray59:`████████` +``gray60`` :gray60:`████████` +``gray61`` :gray61:`████████` +``gray62`` :gray62:`████████` +``gray63`` :gray63:`████████` +``gray64`` :gray64:`████████` +``gray65`` :gray65:`████████` +``gray66`` :gray66:`████████` +``gray67`` :gray67:`████████` +``gray68`` :gray68:`████████` +``gray69`` :gray69:`████████` +``gray70`` :gray70:`████████` +``gray71`` :gray71:`████████` +``gray72`` :gray72:`████████` +``gray73`` :gray73:`████████` +``gray74`` :gray74:`████████` +``gray75`` :gray75:`████████` +``gray76`` :gray76:`████████` +``gray77`` :gray77:`████████` +``gray78`` :gray78:`████████` +``gray79`` :gray79:`████████` +``gray80`` :gray80:`████████` +``gray81`` :gray81:`████████` +``gray82`` :gray82:`████████` +``gray83`` :gray83:`████████` +``gray84`` :gray84:`████████` +``gray85`` :gray85:`████████` +``gray86`` :gray86:`████████` +``gray87`` :gray87:`████████` +``gray88`` :gray88:`████████` +``gray89`` :gray89:`████████` +``gray90`` :gray90:`████████` +``gray91`` :gray91:`████████` +``gray92`` :gray92:`████████` +``gray93`` :gray93:`████████` +``gray94`` :gray94:`████████` +``gray95`` :gray95:`████████` +``gray96`` :gray96:`████████` +``gray97`` :gray97:`████████` +``gray98`` :gray98:`████████` +``gray99`` :gray99:`████████` +``gray100`` :gray100:`████████` +``green`` :green:`████████` +``green1`` :green1:`████████` +``green2`` :green2:`████████` +``green3`` :green3:`████████` +``green4`` :green4:`████████` +``greenyellow`` :greenyellow:`████████` +``grey`` :grey:`████████` +``grey0`` :grey0:`████████` +``grey1`` :grey1:`████████` +``grey2`` :grey2:`████████` +``grey3`` :grey3:`████████` +``grey4`` :grey4:`████████` +``grey5`` :grey5:`████████` +``grey6`` :grey6:`████████` +``grey7`` :grey7:`████████` +``grey8`` :grey8:`████████` +``grey9`` :grey9:`████████` +``grey10`` :grey10:`████████` +``grey11`` :grey11:`████████` +``grey12`` :grey12:`████████` +``grey13`` :grey13:`████████` +``grey14`` :grey14:`████████` +``grey15`` :grey15:`████████` +``grey16`` :grey16:`████████` +``grey17`` :grey17:`████████` +``grey18`` :grey18:`████████` +``grey19`` :grey19:`████████` +``grey20`` :grey20:`████████` +``grey21`` :grey21:`████████` +``grey22`` :grey22:`████████` +``grey23`` :grey23:`████████` +``grey24`` :grey24:`████████` +``grey25`` :grey25:`████████` +``grey26`` :grey26:`████████` +``grey27`` :grey27:`████████` +``grey28`` :grey28:`████████` +``grey29`` :grey29:`████████` +``grey30`` :grey30:`████████` +``grey31`` :grey31:`████████` +``grey32`` :grey32:`████████` +``grey33`` :grey33:`████████` +``grey34`` :grey34:`████████` +``grey35`` :grey35:`████████` +``grey36`` :grey36:`████████` +``grey37`` :grey37:`████████` +``grey38`` :grey38:`████████` +``grey39`` :grey39:`████████` +``grey40`` :grey40:`████████` +``grey41`` :grey41:`████████` +``grey42`` :grey42:`████████` +``grey43`` :grey43:`████████` +``grey44`` :grey44:`████████` +``grey45`` :grey45:`████████` +``grey46`` :grey46:`████████` +``grey47`` :grey47:`████████` +``grey48`` :grey48:`████████` +``grey49`` :grey49:`████████` +``grey50`` :grey50:`████████` +``grey51`` :grey51:`████████` +``grey52`` :grey52:`████████` +``grey53`` :grey53:`████████` +``grey54`` :grey54:`████████` +``grey55`` :grey55:`████████` +``grey56`` :grey56:`████████` +``grey57`` :grey57:`████████` +``grey58`` :grey58:`████████` +``grey59`` :grey59:`████████` +``grey60`` :grey60:`████████` +``grey61`` :grey61:`████████` +``grey62`` :grey62:`████████` +``grey63`` :grey63:`████████` +``grey64`` :grey64:`████████` +``grey65`` :grey65:`████████` +``grey66`` :grey66:`████████` +``grey67`` :grey67:`████████` +``grey68`` :grey68:`████████` +``grey69`` :grey69:`████████` +``grey70`` :grey70:`████████` +``grey71`` :grey71:`████████` +``grey72`` :grey72:`████████` +``grey73`` :grey73:`████████` +``grey74`` :grey74:`████████` +``grey75`` :grey75:`████████` +``grey76`` :grey76:`████████` +``grey77`` :grey77:`████████` +``grey78`` :grey78:`████████` +``grey79`` :grey79:`████████` +``grey80`` :grey80:`████████` +``grey81`` :grey81:`████████` +``grey82`` :grey82:`████████` +``grey83`` :grey83:`████████` +``grey84`` :grey84:`████████` +``grey85`` :grey85:`████████` +``grey86`` :grey86:`████████` +``grey87`` :grey87:`████████` +``grey88`` :grey88:`████████` +``grey89`` :grey89:`████████` +``grey90`` :grey90:`████████` +``grey91`` :grey91:`████████` +``grey92`` :grey92:`████████` +``grey93`` :grey93:`████████` +``grey94`` :grey94:`████████` +``grey95`` :grey95:`████████` +``grey96`` :grey96:`████████` +``grey97`` :grey97:`████████` +``grey98`` :grey98:`████████` +``grey99`` :grey99:`████████` +``grey100`` :grey100:`████████` +``honeydew`` :honeydew:`████████` +``honeydew1`` :honeydew1:`████████` +``honeydew2`` :honeydew2:`████████` +``honeydew3`` :honeydew3:`████████` +``honeydew4`` :honeydew4:`████████` +``hotpink`` :hotpink:`████████` +``hotpink1`` :hotpink1:`████████` +``hotpink2`` :hotpink2:`████████` +``hotpink3`` :hotpink3:`████████` +``hotpink4`` :hotpink4:`████████` +``indianred`` :indianred:`████████` +``indianred1`` :indianred1:`████████` +``indianred2`` :indianred2:`████████` +``indianred3`` :indianred3:`████████` +``indianred4`` :indianred4:`████████` +``indigo`` :indigo:`████████` +``ivory`` :ivory:`████████` +``ivory1`` :ivory1:`████████` +``ivory2`` :ivory2:`████████` +``ivory3`` :ivory3:`████████` +``ivory4`` :ivory4:`████████` +``khaki`` :khaki:`████████` +``khaki1`` :khaki1:`████████` +``khaki2`` :khaki2:`████████` +``khaki3`` :khaki3:`████████` +``khaki4`` :khaki4:`████████` +``lavender`` :lavender:`████████` +``lavenderblush`` :lavenderblush:`████████` +``lavenderblush1`` :lavenderblush1:`████████` +``lavenderblush2`` :lavenderblush2:`████████` +``lavenderblush3`` :lavenderblush3:`████████` +``lavenderblush4`` :lavenderblush4:`████████` +``lawngreen`` :lawngreen:`████████` +``lemonchiffon`` :lemonchiffon:`████████` +``lemonchiffon1`` :lemonchiffon1:`████████` +``lemonchiffon2`` :lemonchiffon2:`████████` +``lemonchiffon3`` :lemonchiffon3:`████████` +``lemonchiffon4`` :lemonchiffon4:`████████` +``lightblue`` :lightblue:`████████` +``lightblue1`` :lightblue1:`████████` +``lightblue2`` :lightblue2:`████████` +``lightblue3`` :lightblue3:`████████` +``lightblue4`` :lightblue4:`████████` +``lightcoral`` :lightcoral:`████████` +``lightcyan`` :lightcyan:`████████` +``lightcyan1`` :lightcyan1:`████████` +``lightcyan2`` :lightcyan2:`████████` +``lightcyan3`` :lightcyan3:`████████` +``lightcyan4`` :lightcyan4:`████████` +``lightgoldenrod`` :lightgoldenrod:`████████` +``lightgoldenrod1`` :lightgoldenrod1:`████████` +``lightgoldenrod2`` :lightgoldenrod2:`████████` +``lightgoldenrod3`` :lightgoldenrod3:`████████` +``lightgoldenrod4`` :lightgoldenrod4:`████████` +``lightgoldenrodyellow`` :lightgoldenrodyellow:`████████` +``lightgray`` :lightgray:`████████` +``lightgreen`` :lightgreen:`████████` +``lightgrey`` :lightgrey:`████████` +``lightpink`` :lightpink:`████████` +``lightpink1`` :lightpink1:`████████` +``lightpink2`` :lightpink2:`████████` +``lightpink3`` :lightpink3:`████████` +``lightpink4`` :lightpink4:`████████` +``lightsalmon`` :lightsalmon:`████████` +``lightsalmon1`` :lightsalmon1:`████████` +``lightsalmon2`` :lightsalmon2:`████████` +``lightsalmon3`` :lightsalmon3:`████████` +``lightsalmon4`` :lightsalmon4:`████████` +``lightseagreen`` :lightseagreen:`████████` +``lightskyblue`` :lightskyblue:`████████` +``lightskyblue1`` :lightskyblue1:`████████` +``lightskyblue2`` :lightskyblue2:`████████` +``lightskyblue3`` :lightskyblue3:`████████` +``lightskyblue4`` :lightskyblue4:`████████` +``lightslateblue`` :lightslateblue:`████████` +``lightslategray`` :lightslategray:`████████` +``lightslategrey`` :lightslategrey:`████████` +``lightsteelblue`` :lightsteelblue:`████████` +``lightsteelblue1`` :lightsteelblue1:`████████` +``lightsteelblue2`` :lightsteelblue2:`████████` +``lightsteelblue3`` :lightsteelblue3:`████████` +``lightsteelblue4`` :lightsteelblue4:`████████` +``lightyellow`` :lightyellow:`████████` +``lightyellow1`` :lightyellow1:`████████` +``lightyellow2`` :lightyellow2:`████████` +``lightyellow3`` :lightyellow3:`████████` +``lightyellow4`` :lightyellow4:`████████` +``lime`` :lime:`████████` +``limegreen`` :limegreen:`████████` +``linen`` :linen:`████████` +``magenta`` :magenta:`████████` +``magenta1`` :magenta1:`████████` +``magenta2`` :magenta2:`████████` +``magenta3`` :magenta3:`████████` +``magenta4`` :magenta4:`████████` +``maroon`` :maroon:`████████` +``maroon1`` :maroon1:`████████` +``maroon2`` :maroon2:`████████` +``maroon3`` :maroon3:`████████` +``maroon4`` :maroon4:`████████` +``mediumaquamarine`` :mediumaquamarine:`████████` +``mediumblue`` :mediumblue:`████████` +``mediumorchid`` :mediumorchid:`████████` +``mediumorchid1`` :mediumorchid1:`████████` +``mediumorchid2`` :mediumorchid2:`████████` +``mediumorchid3`` :mediumorchid3:`████████` +``mediumorchid4`` :mediumorchid4:`████████` +``mediumpurple`` :mediumpurple:`████████` +``mediumpurple1`` :mediumpurple1:`████████` +``mediumpurple2`` :mediumpurple2:`████████` +``mediumpurple3`` :mediumpurple3:`████████` +``mediumpurple4`` :mediumpurple4:`████████` +``mediumseagreen`` :mediumseagreen:`████████` +``mediumslateblue`` :mediumslateblue:`████████` +``mediumspringgreen`` :mediumspringgreen:`████████` +``mediumturquoise`` :mediumturquoise:`████████` +``mediumvioletred`` :mediumvioletred:`████████` +``midnightblue`` :midnightblue:`████████` +``mintcream`` :mintcream:`████████` +``mistyrose`` :mistyrose:`████████` +``mistyrose1`` :mistyrose1:`████████` +``mistyrose2`` :mistyrose2:`████████` +``mistyrose3`` :mistyrose3:`████████` +``mistyrose4`` :mistyrose4:`████████` +``moccasin`` :moccasin:`████████` +``navajowhite`` :navajowhite:`████████` +``navajowhite1`` :navajowhite1:`████████` +``navajowhite2`` :navajowhite2:`████████` +``navajowhite3`` :navajowhite3:`████████` +``navajowhite4`` :navajowhite4:`████████` +``navy`` :navy:`████████` +``navyblue`` :navyblue:`████████` +``oldlace`` :oldlace:`████████` +``olive`` :olive:`████████` +``olivedrab`` :olivedrab:`████████` +``olivedrab1`` :olivedrab1:`████████` +``olivedrab2`` :olivedrab2:`████████` +``olivedrab3`` :olivedrab3:`████████` +``olivedrab4`` :olivedrab4:`████████` +``orange`` :orange:`████████` +``orange1`` :orange1:`████████` +``orange2`` :orange2:`████████` +``orange3`` :orange3:`████████` +``orange4`` :orange4:`████████` +``orangered`` :orangered:`████████` +``orangered1`` :orangered1:`████████` +``orangered2`` :orangered2:`████████` +``orangered3`` :orangered3:`████████` +``orangered4`` :orangered4:`████████` +``orchid`` :orchid:`████████` +``orchid1`` :orchid1:`████████` +``orchid2`` :orchid2:`████████` +``orchid3`` :orchid3:`████████` +``orchid4`` :orchid4:`████████` +``palegoldenrod`` :palegoldenrod:`████████` +``palegreen`` :palegreen:`████████` +``palegreen1`` :palegreen1:`████████` +``palegreen2`` :palegreen2:`████████` +``palegreen3`` :palegreen3:`████████` +``palegreen4`` :palegreen4:`████████` +``paleturquoise`` :paleturquoise:`████████` +``paleturquoise1`` :paleturquoise1:`████████` +``paleturquoise2`` :paleturquoise2:`████████` +``paleturquoise3`` :paleturquoise3:`████████` +``paleturquoise4`` :paleturquoise4:`████████` +``palevioletred`` :palevioletred:`████████` +``palevioletred1`` :palevioletred1:`████████` +``palevioletred2`` :palevioletred2:`████████` +``palevioletred3`` :palevioletred3:`████████` +``palevioletred4`` :palevioletred4:`████████` +``papayawhip`` :papayawhip:`████████` +``peachpuff`` :peachpuff:`████████` +``peachpuff1`` :peachpuff1:`████████` +``peachpuff2`` :peachpuff2:`████████` +``peachpuff3`` :peachpuff3:`████████` +``peachpuff4`` :peachpuff4:`████████` +``peru`` :peru:`████████` +``pink`` :pink:`████████` +``pink1`` :pink1:`████████` +``pink2`` :pink2:`████████` +``pink3`` :pink3:`████████` +``pink4`` :pink4:`████████` +``plum`` :plum:`████████` +``plum1`` :plum1:`████████` +``plum2`` :plum2:`████████` +``plum3`` :plum3:`████████` +``plum4`` :plum4:`████████` +``powderblue`` :powderblue:`████████` +``purple`` :purple:`████████` +``purple1`` :purple1:`████████` +``purple2`` :purple2:`████████` +``purple3`` :purple3:`████████` +``purple4`` :purple4:`████████` +``red`` :red:`████████` +``red1`` :red1:`████████` +``red2`` :red2:`████████` +``red3`` :red3:`████████` +``red4`` :red4:`████████` +``rosybrown`` :rosybrown:`████████` +``rosybrown1`` :rosybrown1:`████████` +``rosybrown2`` :rosybrown2:`████████` +``rosybrown3`` :rosybrown3:`████████` +``rosybrown4`` :rosybrown4:`████████` +``royalblue`` :royalblue:`████████` +``royalblue1`` :royalblue1:`████████` +``royalblue2`` :royalblue2:`████████` +``royalblue3`` :royalblue3:`████████` +``royalblue4`` :royalblue4:`████████` +``saddlebrown`` :saddlebrown:`████████` +``salmon`` :salmon:`████████` +``salmon1`` :salmon1:`████████` +``salmon2`` :salmon2:`████████` +``salmon3`` :salmon3:`████████` +``salmon4`` :salmon4:`████████` +``sandybrown`` :sandybrown:`████████` +``seagreen`` :seagreen:`████████` +``seagreen1`` :seagreen1:`████████` +``seagreen2`` :seagreen2:`████████` +``seagreen3`` :seagreen3:`████████` +``seagreen4`` :seagreen4:`████████` +``seashell`` :seashell:`████████` +``seashell1`` :seashell1:`████████` +``seashell2`` :seashell2:`████████` +``seashell3`` :seashell3:`████████` +``seashell4`` :seashell4:`████████` +``sienna`` :sienna:`████████` +``sienna1`` :sienna1:`████████` +``sienna2`` :sienna2:`████████` +``sienna3`` :sienna3:`████████` +``sienna4`` :sienna4:`████████` +``silver`` :silver:`████████` +``skyblue`` :skyblue:`████████` +``skyblue1`` :skyblue1:`████████` +``skyblue2`` :skyblue2:`████████` +``skyblue3`` :skyblue3:`████████` +``skyblue4`` :skyblue4:`████████` +``slateblue`` :slateblue:`████████` +``slateblue1`` :slateblue1:`████████` +``slateblue2`` :slateblue2:`████████` +``slateblue3`` :slateblue3:`████████` +``slateblue4`` :slateblue4:`████████` +``slategray`` :slategray:`████████` +``slategray1`` :slategray1:`████████` +``slategray2`` :slategray2:`████████` +``slategray3`` :slategray3:`████████` +``slategray4`` :slategray4:`████████` +``slategrey`` :slategrey:`████████` +``snow`` :snow:`████████` +``snow1`` :snow1:`████████` +``snow2`` :snow2:`████████` +``snow3`` :snow3:`████████` +``snow4`` :snow4:`████████` +``springgreen`` :springgreen:`████████` +``springgreen1`` :springgreen1:`████████` +``springgreen2`` :springgreen2:`████████` +``springgreen3`` :springgreen3:`████████` +``springgreen4`` :springgreen4:`████████` +``steelblue`` :steelblue:`████████` +``steelblue1`` :steelblue1:`████████` +``steelblue2`` :steelblue2:`████████` +``steelblue3`` :steelblue3:`████████` +``steelblue4`` :steelblue4:`████████` +``tan`` :tan:`████████` +``tan1`` :tan1:`████████` +``tan2`` :tan2:`████████` +``tan3`` :tan3:`████████` +``tan4`` :tan4:`████████` +``teal`` :teal:`████████` +``thistle`` :thistle:`████████` +``thistle1`` :thistle1:`████████` +``thistle2`` :thistle2:`████████` +``thistle3`` :thistle3:`████████` +``thistle4`` :thistle4:`████████` +``tomato`` :tomato:`████████` +``tomato1`` :tomato1:`████████` +``tomato2`` :tomato2:`████████` +``tomato3`` :tomato3:`████████` +``tomato4`` :tomato4:`████████` +``turquoise`` :turquoise:`████████` +``turquoise1`` :turquoise1:`████████` +``turquoise2`` :turquoise2:`████████` +``turquoise3`` :turquoise3:`████████` +``turquoise4`` :turquoise4:`████████` +``violet`` :violet:`████████` +``violetred`` :violetred:`████████` +``violetred1`` :violetred1:`████████` +``violetred2`` :violetred2:`████████` +``violetred3`` :violetred3:`████████` +``violetred4`` :violetred4:`████████` +``wheat`` :wheat:`████████` +``wheat1`` :wheat1:`████████` +``wheat2`` :wheat2:`████████` +``wheat3`` :wheat3:`████████` +``wheat4`` :wheat4:`████████` +``white`` :white:`████████` +``whitesmoke`` :whitesmoke:`████████` +``yellow`` :yellow:`████████` +``yellow1`` :yellow1:`████████` +``yellow2`` :yellow2:`████████` +``yellow3`` :yellow3:`████████` +``yellow4`` :yellow4:`████████` +``yellowgreen`` :yellowgreen:`████████` +========================== ====================================================================================================== diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/cursors.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/cursors.rst.txt new file mode 100644 index 00000000..6ea68e24 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/cursors.rst.txt @@ -0,0 +1,251 @@ +.. include:: common.txt + +:mod:`pygame.cursors` +===================== + +.. module:: pygame.cursors + :synopsis: pygame module for cursor resources + +| :sl:`pygame module for cursor resources` + +Pygame offers control over the system hardware cursor. Pygame supports +black and white cursors (bitmap cursors), as well as system variant cursors and color cursors. +You control the cursor with functions inside :mod:`pygame.mouse`. + +This cursors module contains functions for loading and decoding various +cursor formats. These allow you to easily store your cursors in external files +or directly as encoded python strings. + +The module includes several standard cursors. The :func:`pygame.mouse.set_cursor()` +function takes several arguments. All those arguments have been stored in a +single tuple you can call like this: + +:: + + >>> pygame.mouse.set_cursor(*pygame.cursors.arrow) + +The following variables can be passed to ``pygame.mouse.set_cursor`` function: + + * ``pygame.cursors.arrow`` + + * ``pygame.cursors.diamond`` + + * ``pygame.cursors.broken_x`` + + * ``pygame.cursors.tri_left`` + + * ``pygame.cursors.tri_right`` + +This module also contains a few cursors as formatted strings. You'll need to +pass these to ``pygame.cursors.compile()`` function before you can use them. +The example call would look like this: + +:: + + >>> cursor = pygame.cursors.compile(pygame.cursors.textmarker_strings) + >>> pygame.mouse.set_cursor((8, 16), (0, 0), *cursor) + +The following strings can be converted into cursor bitmaps with +``pygame.cursors.compile()`` : + + * ``pygame.cursors.thickarrow_strings`` + + * ``pygame.cursors.sizer_x_strings`` + + * ``pygame.cursors.sizer_y_strings`` + + * ``pygame.cursors.sizer_xy_strings`` + + * ``pygame.cursor.textmarker_strings`` + +.. function:: compile + + | :sl:`create binary cursor data from simple strings` + | :sg:`compile(strings, black='X', white='.', xor='o') -> data, mask` + + A sequence of strings can be used to create binary cursor data for the + system cursor. This returns the binary data in the form of two tuples. + Those can be passed as the third and fourth arguments respectively of the + :func:`pygame.mouse.set_cursor()` function. + + If you are creating your own cursor strings, you can use any value represent + the black and white pixels. Some system allow you to set a special toggle + color for the system color, this is also called the xor color. If the system + does not support xor cursors, that color will simply be black. + + The height must be divisible by 8. The width of the strings must all be equal + and be divisible by 8. If these two conditions are not met, ``ValueError`` is + raised. + An example set of cursor strings looks like this + + :: + + thickarrow_strings = ( #sized 24x24 + "XX ", + "XXX ", + "XXXX ", + "XX.XX ", + "XX..XX ", + "XX...XX ", + "XX....XX ", + "XX.....XX ", + "XX......XX ", + "XX.......XX ", + "XX........XX ", + "XX........XXX ", + "XX......XXXXX ", + "XX.XXX..XX ", + "XXXX XX..XX ", + "XX XX..XX ", + " XX..XX ", + " XX..XX ", + " XX..XX ", + " XXXX ", + " XX ", + " ", + " ", + " ") + + .. ## pygame.cursors.compile ## + +.. function:: load_xbm + + | :sl:`load cursor data from an XBM file` + | :sg:`load_xbm(cursorfile) -> cursor_args` + | :sg:`load_xbm(cursorfile, maskfile) -> cursor_args` + + This loads cursors for a simple subset of ``XBM`` files. ``XBM`` files are + traditionally used to store cursors on UNIX systems, they are an ASCII + format used to represent simple images. + + Sometimes the black and white color values will be split into two separate + ``XBM`` files. You can pass a second maskfile argument to load the two + images into a single cursor. + + The cursorfile and maskfile arguments can either be filenames or file-like + object with the readlines method. + + The return value cursor_args can be passed directly to the + ``pygame.mouse.set_cursor()`` function. + + .. ## pygame.cursors.load_xbm ## + + + +.. class:: Cursor + + | :sl:`pygame object representing a cursor` + | :sg:`Cursor(size, hotspot, xormasks, andmasks) -> Cursor` + | :sg:`Cursor(hotspot, surface) -> Cursor` + | :sg:`Cursor(constant) -> Cursor` + | :sg:`Cursor(Cursor) -> Cursor` + | :sg:`Cursor() -> Cursor` + + In pygame 2, there are 3 types of cursors you can create to give your + game that little bit of extra polish. There's **bitmap** type cursors, + which existed in pygame 1.x, and are compiled from a string or load from an xbm file. + Then there are **system** type cursors, where you choose a preset that will + convey the same meaning but look native across different operating systems. + Finally you can create a **color** cursor, which displays a pygame surface as the cursor. + + **Creating a system cursor** + + Choose a constant from this list, pass it into ``pygame.cursors.Cursor(constant)``, + and you're good to go. Be advised that not all systems support every system + cursor, and you may get a substitution instead. For example, on MacOS, + WAIT/WAITARROW should show up as an arrow, and SIZENWSE/SIZENESW/SIZEALL + should show up as a closed hand. And on Wayland, every SIZE cursor should + show up as a hand. + + :: + + Pygame Cursor Constant Description + -------------------------------------------- + pygame.SYSTEM_CURSOR_ARROW arrow + pygame.SYSTEM_CURSOR_IBEAM i-beam + pygame.SYSTEM_CURSOR_WAIT wait + pygame.SYSTEM_CURSOR_CROSSHAIR crosshair + pygame.SYSTEM_CURSOR_WAITARROW small wait cursor + (or wait if not available) + pygame.SYSTEM_CURSOR_SIZENWSE double arrow pointing + northwest and southeast + pygame.SYSTEM_CURSOR_SIZENESW double arrow pointing + northeast and southwest + pygame.SYSTEM_CURSOR_SIZEWE double arrow pointing + west and east + pygame.SYSTEM_CURSOR_SIZENS double arrow pointing + north and south + pygame.SYSTEM_CURSOR_SIZEALL four pointed arrow pointing + north, south, east, and west + pygame.SYSTEM_CURSOR_NO slashed circle or crossbones + pygame.SYSTEM_CURSOR_HAND hand + + **Creating a cursor without passing arguments** + + In addition to the cursor constants available and described above, + you can also call ``pygame.cursors.Cursor()``, and your cursor is ready (doing that is the same as + calling ``pygame.cursors.Cursor(pygame.SYSTEM_CURSOR_ARROW)``. + Doing one of those calls actually creates a system cursor using the default native image. + + **Creating a color cursor** + + To create a color cursor, create a ``Cursor`` from a ``hotspot`` and a ``surface``. + ``hotspot`` is an (x,y) coordinate that determines where in the cursor the exact point is. + The hotspot position must be within the bounds of the ``surface``. + + **Creating a bitmap cursor** + + When the mouse cursor is visible, it will be displayed as a black and white + bitmap using the given bitmask arrays. The ``size`` is a sequence containing + the cursor width and height. ``hotspot`` is a sequence containing the cursor + hotspot position. + + A cursor has a width and height, but a mouse position is represented by a + set of point coordinates. So the value passed into the cursor ``hotspot`` + variable helps pygame to actually determine at what exact point the cursor + is at. + + ``xormasks`` is a sequence of bytes containing the cursor xor data masks. + Lastly ``andmasks``, a sequence of bytes containing the cursor bitmask data. + To create these variables, we can make use of the + :func:`pygame.cursors.compile()` function. + + Width and height must be a multiple of 8, and the mask arrays must be the + correct size for the given width and height. Otherwise an exception is raised. + + .. method:: copy + + | :sl:`copy the current cursor` + | :sg:`copy() -> Cursor` + + Returns a new Cursor object with the same data and hotspot as the original. + .. ## pygame.cursors.Cursor.copy ## + + + .. attribute:: type + + | :sl:`Gets the cursor type` + | :sg:`type -> string` + + The type will be ``"system"``, ``"bitmap"``, or ``"color"``. + + .. ## pygame.cursors.Cursor.type ## + + .. attribute:: data + + | :sl:`Gets the cursor data` + | :sg:`data -> tuple` + + Returns the data that was used to create this cursor object, wrapped up in a tuple. + + .. ## pygame.cursors.Cursor.data ## + + .. versionadded:: 2.0.1 + + .. ## pygame.cursors.Cursor ## + +.. ## pygame.cursors ## + +Example code for creating and settings cursors. (Click the mouse to switch cursor) + +.. literalinclude:: code_examples/cursors_module_example.py diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/display.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/display.rst.txt new file mode 100644 index 00000000..c669eabc --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/display.rst.txt @@ -0,0 +1,737 @@ +.. include:: common.txt + +:mod:`pygame.display` +===================== + +.. module:: pygame.display + :synopsis: pygame module to control the display window and screen + +| :sl:`pygame module to control the display window and screen` + +This module offers control over the pygame display. Pygame has a single display +Surface that is either contained in a window or runs full screen. Once you +create the display you treat it as a regular Surface. Changes are not +immediately visible onscreen; you must choose one of the two flipping functions +to update the actual display. + +The origin of the display, where x = 0 and y = 0, is the top left of the +screen. Both axes increase positively towards the bottom right of the screen. + +The pygame display can actually be initialized in one of several modes. By +default, the display is a basic software driven framebuffer. You can request +special modules like automatic scaling or OpenGL support. These are +controlled by flags passed to ``pygame.display.set_mode()``. + +Pygame can only have a single display active at any time. Creating a new one +with ``pygame.display.set_mode()`` will close the previous display. To detect +the number and size of attached screens, you can use +``pygame.display.get_desktop_sizes`` and then select appropriate window size +and display index to pass to ``pygame.display.set_mode()``. + +For backward compatibility ``pygame.display`` allows precise control over +the pixel format or display resolutions. This used to be necessary with old +graphics cards and CRT screens, but is usually not needed any more. Use the +functions ``pygame.display.mode_ok()``, ``pygame.display.list_modes()``, and +``pygame.display.Info()`` to query detailed information about the display. + +Once the display Surface is created, the functions from this module affect the +single existing display. The Surface becomes invalid if the module is +uninitialized. If a new display mode is set, the existing Surface will +automatically switch to operate on the new display. + +When the display mode is set, several events are placed on the pygame event +queue. ``pygame.QUIT`` is sent when the user has requested the program to +shut down. The window will receive ``pygame.ACTIVEEVENT`` events as the display +gains and loses input focus. If the display is set with the +``pygame.RESIZABLE`` flag, ``pygame.VIDEORESIZE`` events will be sent when the +user adjusts the window dimensions. Hardware displays that draw direct to the +screen will get ``pygame.VIDEOEXPOSE`` events when portions of the window must +be redrawn. + +A new windowevent API was introduced in pygame 2.0.1. Check event module docs +for more information on that + +Some display environments have an option for automatically stretching all +windows. When this option is enabled, this automatic stretching distorts the +appearance of the pygame window. In the pygame examples directory, there is +example code (prevent_display_stretching.py) which shows how to disable this +automatic stretching of the pygame display on Microsoft Windows (Vista or newer +required). + +.. function:: init + + | :sl:`Initialize the display module` + | :sg:`init() -> None` + + Initializes the pygame display module. The display module cannot do anything + until it is initialized. This is usually handled for you automatically when + you call the higher level ``pygame.init()``. + + Pygame will select from one of several internal display backends when it is + initialized. The display mode will be chosen depending on the platform and + permissions of current user. Before the display module is initialized the + environment variable ``SDL_VIDEODRIVER`` can be set to control which backend + is used. The systems with multiple choices are listed here. + + :: + + Windows : windib, directx + Unix : x11, dga, fbcon, directfb, ggi, vgl, svgalib, aalib + + On some platforms it is possible to embed the pygame display into an already + existing window. To do this, the environment variable ``SDL_WINDOWID`` must + be set to a string containing the window id or handle. The environment + variable is checked when the pygame display is initialized. Be aware that + there can be many strange side effects when running in an embedded display. + + It is harmless to call this more than once, repeated calls have no effect. + + .. ## pygame.display.init ## + +.. function:: quit + + | :sl:`Uninitialize the display module` + | :sg:`quit() -> None` + + This will shut down the entire display module. This means any active + displays will be closed. This will also be handled automatically when the + program exits. + + It is harmless to call this more than once, repeated calls have no effect. + + .. ## pygame.display.quit ## + +.. function:: get_init + + | :sl:`Returns True if the display module has been initialized` + | :sg:`get_init() -> bool` + + Returns True if the :mod:`pygame.display` module is currently initialized. + + .. ## pygame.display.get_init ## + +.. function:: set_mode + + | :sl:`Initialize a window or screen for display` + | :sg:`set_mode(size=(0, 0), flags=0, depth=0, display=0, vsync=0) -> Surface` + + This function will create a display Surface. The arguments passed in are + requests for a display type. The actual created display will be the best + possible match supported by the system. + + Note that calling this function implicitly initializes ``pygame.display``, if + it was not initialized before. + + The size argument is a pair of numbers representing the width and + height. The flags argument is a collection of additional options. The depth + argument represents the number of bits to use for color. + + The Surface that gets returned can be drawn to like a regular Surface but + changes will eventually be seen on the monitor. + + If no size is passed or is set to ``(0, 0)`` and pygame uses ``SDL`` + version 1.2.10 or above, the created Surface will have the same size as the + current screen resolution. If only the width or height are set to ``0``, the + Surface will have the same width or height as the screen resolution. Using a + ``SDL`` version prior to 1.2.10 will raise an exception. + + It is usually best to not pass the depth argument. It will default to the + best and fastest color depth for the system. If your game requires a + specific color format you can control the depth with this argument. Pygame + will emulate an unavailable color depth which can be slow. + + When requesting fullscreen display modes, sometimes an exact match for the + requested size cannot be made. In these situations pygame will select + the closest compatible match. The returned surface will still always match + the requested size. + + On high resolution displays(4k, 1080p) and tiny graphics games (640x480) + show up very small so that they are unplayable. SCALED scales up the window + for you. The game thinks it's a 640x480 window, but really it can be bigger. + Mouse events are scaled for you, so your game doesn't need to do it. Note + that SCALED is considered an experimental API and may change in future + releases. + + The flags argument controls which type of display you want. There are + several to choose from, and you can even combine multiple types using the + bitwise or operator, (the pipe "|" character). Here are the display + flags you will want to choose from: + + :: + + pygame.FULLSCREEN create a fullscreen display + pygame.DOUBLEBUF only applicable with OPENGL + pygame.HWSURFACE (obsolete in pygame 2) hardware accelerated, only in FULLSCREEN + pygame.OPENGL create an OpenGL-renderable display + pygame.RESIZABLE display window should be sizeable + pygame.NOFRAME display window will have no border or controls + pygame.SCALED resolution depends on desktop size and scale graphics + pygame.SHOWN window is opened in visible mode (default) + pygame.HIDDEN window is opened in hidden mode + + + .. versionadded:: 2.0.0 ``SCALED``, ``SHOWN`` and ``HIDDEN`` + + By setting the ``vsync`` parameter to ``1``, it is possible to get a display + with vertical sync, but you are not guaranteed to get one. The request only + works at all for calls to ``set_mode()`` with the ``pygame.OPENGL`` or + ``pygame.SCALED`` flags set, and is still not guaranteed even with one of + those set. What you get depends on the hardware and driver configuration + of the system pygame is running on. Here is an example usage of a call + to ``set_mode()`` that may give you a display with vsync: + + :: + + flags = pygame.OPENGL | pygame.FULLSCREEN + window_surface = pygame.display.set_mode((1920, 1080), flags, vsync=1) + + Vsync behaviour is considered experimental, and may change in future releases. + + .. versionadded:: 2.0.0 ``vsync`` + + Basic example: + + :: + + # Open a window on the screen + screen_width=700 + screen_height=400 + screen=pygame.display.set_mode([screen_width, screen_height]) + + The display index ``0`` means the default display is used. If no display + index argument is provided, the default display can be overridden with an + environment variable. + + + .. versionchanged:: 1.9.5 ``display`` argument added + + .. versionchanged:: 2.1.3 + pygame now ensures that subsequent calls to this function clears the + window to black. On older versions, this was an implementation detail + on the major platforms this function was tested with. + + .. ## pygame.display.set_mode ## + +.. function:: get_surface + + | :sl:`Get a reference to the currently set display surface` + | :sg:`get_surface() -> Surface` + + Return a reference to the currently set display Surface. If no display mode + has been set this will return None. + + .. ## pygame.display.get_surface ## + +.. function:: flip + + | :sl:`Update the full display Surface to the screen` + | :sg:`flip() -> None` + + This will update the contents of the entire display. If your display mode is + using the flags ``pygame.HWSURFACE`` and ``pygame.DOUBLEBUF`` on pygame 1, + this will wait for a vertical retrace and swap the surfaces. + + When using an ``pygame.OPENGL`` display mode this will perform a gl buffer + swap. + + .. ## pygame.display.flip ## + +.. function:: update + + | :sl:`Update portions of the screen for software displays` + | :sg:`update(rectangle=None) -> None` + | :sg:`update(rectangle_list) -> None` + + This function is like an optimized version of ``pygame.display.flip()`` for + software displays. It allows only a portion of the screen to be updated, + instead of the entire area. If no argument is passed it updates the entire + Surface area like ``pygame.display.flip()``. + + Note that calling ``display.update(None)`` means no part of the window is + updated. Whereas ``display.update()`` means the whole window is updated. + + You can pass the function a single rectangle, or a sequence of rectangles. + It is more efficient to pass many rectangles at once than to call update + multiple times with single or a partial list of rectangles. If passing a + sequence of rectangles it is safe to include None values in the list, which + will be skipped. + + This call cannot be used on ``pygame.OPENGL`` displays and will generate an + exception. + + .. ## pygame.display.update ## + +.. function:: get_driver + + | :sl:`Get the name of the pygame display backend` + | :sg:`get_driver() -> name` + + Pygame chooses one of many available display backends when it is + initialized. This returns the internal name used for the display backend. + This can be used to provide limited information about what display + capabilities might be accelerated. See the ``SDL_VIDEODRIVER`` flags in + ``pygame.display.set_mode()`` to see some of the common options. + + .. ## pygame.display.get_driver ## + +.. function:: Info + + | :sl:`Create a video display information object` + | :sg:`Info() -> VideoInfo` + + Creates a simple object containing several attributes to describe the + current graphics environment. If this is called before + ``pygame.display.set_mode()`` some platforms can provide information about + the default display mode. This can also be called after setting the display + mode to verify specific display options were satisfied. The VidInfo object + has several attributes: + + :: + + hw: 1 if the display is hardware accelerated + wm: 1 if windowed display modes can be used + video_mem: The megabytes of video memory on the display. This is 0 if + unknown + bitsize: Number of bits used to store each pixel + bytesize: Number of bytes used to store each pixel + masks: Four values used to pack RGBA values into pixels + shifts: Four values used to pack RGBA values into pixels + losses: Four values used to pack RGBA values into pixels + blit_hw: 1 if hardware Surface blitting is accelerated + blit_hw_CC: 1 if hardware Surface colorkey blitting is accelerated + blit_hw_A: 1 if hardware Surface pixel alpha blitting is accelerated + blit_sw: 1 if software Surface blitting is accelerated + blit_sw_CC: 1 if software Surface colorkey blitting is accelerated + blit_sw_A: 1 if software Surface pixel alpha blitting is accelerated + current_h, current_w: Height and width of the current video mode, or + of the desktop mode if called before the display.set_mode + is called. (current_h, current_w are available since + SDL 1.2.10, and pygame 1.8.0). They are -1 on error, or if + an old SDL is being used. + + .. ## pygame.display.Info ## + +.. function:: get_wm_info + + | :sl:`Get information about the current windowing system` + | :sg:`get_wm_info() -> dict` + + Creates a dictionary filled with string keys. The strings and values are + arbitrarily created by the system. Some systems may have no information and + an empty dictionary will be returned. Most platforms will return a "window" + key with the value set to the system id for the current display. + + .. versionadded:: 1.7.1 + + .. ## pygame.display.get_wm_info ## + +.. function:: get_desktop_sizes + + | :sl:`Get sizes of active desktops` + | :sg:`get_desktop_sizes() -> list` + + This function returns the sizes of the currently configured + virtual desktops as a list of (x, y) tuples of integers. + + The length of the list is not the same as the number of attached monitors, + as a desktop can be mirrored across multiple monitors. The desktop sizes + do not indicate the maximum monitor resolutions supported by the hardware, + but the desktop size configured in the operating system. + + In order to fit windows into the desktop as it is currently configured, and + to respect the resolution configured by the operating system in fullscreen + mode, this function *should* be used to replace many use cases of + ``pygame.display.list_modes()`` whenever applicable. + + .. versionadded:: 2.0.0 + +.. function:: list_modes + + | :sl:`Get list of available fullscreen modes` + | :sg:`list_modes(depth=0, flags=pygame.FULLSCREEN, display=0) -> list` + + This function returns a list of possible sizes for a specified color + depth. The return value will be an empty list if no display modes are + available with the given arguments. A return value of ``-1`` means that + any requested size should work (this is likely the case for windowed + modes). Mode sizes are sorted from biggest to smallest. + + If depth is ``0``, the current/best color depth for the display is used. + The flags defaults to ``pygame.FULLSCREEN``, but you may need to add + additional flags for specific fullscreen modes. + + The display index ``0`` means the default display is used. + + Since pygame 2.0, ``pygame.display.get_desktop_sizes()`` has taken over + some use cases from ``pygame.display.list_modes()``: + + To find a suitable size for non-fullscreen windows, it is preferable to + use ``pygame.display.get_desktop_sizes()`` to get the size of the *current* + desktop, and to then choose a smaller window size. This way, the window is + guaranteed to fit, even when the monitor is configured to a lower resolution + than the maximum supported by the hardware. + + To avoid changing the physical monitor resolution, it is also preferable to + use ``pygame.display.get_desktop_sizes()`` to determine the fullscreen + resolution. Developers are strongly advised to default to the current + physical monitor resolution unless the user explicitly requests a different + one (e.g. in an options menu or configuration file). + + .. versionchanged:: 1.9.5 ``display`` argument added + + .. ## pygame.display.list_modes ## + +.. function:: mode_ok + + | :sl:`Pick the best color depth for a display mode` + | :sg:`mode_ok(size, flags=0, depth=0, display=0) -> depth` + + This function uses the same arguments as ``pygame.display.set_mode()``. It + is used to determine if a requested display mode is available. It will + return ``0`` if the display mode cannot be set. Otherwise it will return a + pixel depth that best matches the display asked for. + + Usually the depth argument is not passed, but some platforms can support + multiple display depths. If passed it will hint to which depth is a better + match. + + The function will return ``0`` if the passed display flags cannot be set. + + The display index ``0`` means the default display is used. + + .. versionchanged:: 1.9.5 ``display`` argument added + + .. ## pygame.display.mode_ok ## + +.. function:: gl_get_attribute + + | :sl:`Get the value for an OpenGL flag for the current display` + | :sg:`gl_get_attribute(flag) -> value` + + After calling ``pygame.display.set_mode()`` with the ``pygame.OPENGL`` flag, + it is a good idea to check the value of any requested OpenGL attributes. See + ``pygame.display.gl_set_attribute()`` for a list of valid flags. + + .. versionchanged:: 2.5.0 Added support for keyword arguments. + + .. ## pygame.display.gl_get_attribute ## + +.. function:: gl_set_attribute + + | :sl:`Request an OpenGL display attribute for the display mode` + | :sg:`gl_set_attribute(flag, value) -> None` + + When calling ``pygame.display.set_mode()`` with the ``pygame.OPENGL`` flag, + Pygame automatically handles setting the OpenGL attributes like color and + double-buffering. OpenGL offers several other attributes you may want control + over. Pass one of these attributes as the flag, and its appropriate value. + This must be called before ``pygame.display.set_mode()``. + + Many settings are the requested minimum. Creating a window with an OpenGL context + will fail if OpenGL cannot provide the requested attribute, but it may for example + give you a stencil buffer even if you request none, or it may give you a larger + one than requested. + + The ``OPENGL`` flags are: + + :: + + GL_ALPHA_SIZE, GL_DEPTH_SIZE, GL_STENCIL_SIZE, GL_ACCUM_RED_SIZE, + GL_ACCUM_GREEN_SIZE, GL_ACCUM_BLUE_SIZE, GL_ACCUM_ALPHA_SIZE, + GL_MULTISAMPLEBUFFERS, GL_MULTISAMPLESAMPLES, GL_STEREO + + :const:`GL_MULTISAMPLEBUFFERS` + + Whether to enable multisampling anti-aliasing. + Defaults to 0 (disabled). + + Set ``GL_MULTISAMPLESAMPLES`` to a value + above 0 to control the amount of anti-aliasing. + A typical value is 2 or 3. + + :const:`GL_STENCIL_SIZE` + + Minimum bit size of the stencil buffer. Defaults to 0. + + :const:`GL_DEPTH_SIZE` + + Minimum bit size of the depth buffer. Defaults to 16. + + :const:`GL_STEREO` + + 1 enables stereo 3D. Defaults to 0. + + :const:`GL_BUFFER_SIZE` + + Minimum bit size of the frame buffer. Defaults to 0. + + .. versionchanged:: 2.5.0 Added support for keyword arguments. + + .. versionadded:: 2.0.0 Additional attributes: + + :: + + GL_ACCELERATED_VISUAL, + GL_CONTEXT_MAJOR_VERSION, GL_CONTEXT_MINOR_VERSION, + GL_CONTEXT_FLAGS, GL_CONTEXT_PROFILE_MASK, + GL_SHARE_WITH_CURRENT_CONTEXT, + GL_CONTEXT_RELEASE_BEHAVIOR, + GL_FRAMEBUFFER_SRGB_CAPABLE + + :const:`GL_CONTEXT_PROFILE_MASK` + + Sets the OpenGL profile to one of these values: + + :: + + GL_CONTEXT_PROFILE_CORE disable deprecated features + GL_CONTEXT_PROFILE_COMPATIBILITY allow deprecated features + GL_CONTEXT_PROFILE_ES allow only the ES feature + subset of OpenGL + + :const:`GL_ACCELERATED_VISUAL` + + Set to 1 to require hardware acceleration, or 0 to force software render. + By default, both are allowed. + + .. ## pygame.display.gl_set_attribute ## + +.. function:: get_active + + | :sl:`Returns True when the display is active on the screen` + | :sg:`get_active() -> bool` + + Returns True when the display Surface is considered actively + renderable on the screen and may be visible to the user. This is + the default state immediately after ``pygame.display.set_mode()``. + This method may return True even if the application is fully hidden + behind another application window. + + This will return False if the display Surface has been iconified or + minimized (either via ``pygame.display.iconify()`` or via an OS + specific method such as the minimize-icon available on most + desktops). + + The method can also return False for other reasons without the + application being explicitly iconified or minimized by the user. A + notable example being if the user has multiple virtual desktops and + the display Surface is not on the active virtual desktop. + + .. note:: This function returning True is unrelated to whether the + application has input focus. Please see + ``pygame.key.get_focused()`` and ``pygame.mouse.get_focused()`` + for APIs related to input focus. + + .. ## pygame.display.get_active ## + +.. function:: iconify + + | :sl:`Iconify the display surface` + | :sg:`iconify() -> bool` + + Request the window for the display surface be iconified or hidden. Not all + systems and displays support an iconified display. The function will return + True if successful. + + When the display is iconified ``pygame.display.get_active()`` will return + ``False``. The event queue should receive an ``ACTIVEEVENT`` event when the + window has been iconified. Additionally, the event queue also receives a + ``WINDOWEVENT_MINIMIZED`` event when the window has been iconified on pygame 2. + + .. ## pygame.display.iconify ## + +.. function:: toggle_fullscreen + + | :sl:`Switch between fullscreen and windowed displays` + | :sg:`toggle_fullscreen() -> int` + + Switches the display window between windowed and fullscreen modes. + Display driver support is not great when using pygame 1, but with + pygame 2 it is the most reliable method to switch to and from fullscreen. + + Supported display drivers in pygame 1: + + * x11 (Linux/Unix) + * wayland (Linux/Unix) + + Supported display drivers in pygame 2: + + * windows (Windows) + * x11 (Linux/Unix) + * wayland (Linux/Unix) + * cocoa (OSX/Mac) + + .. Note:: :func:`toggle_fullscreen` doesn't work on Windows + unless the window size is in :func:`pygame.display.list_modes()` or + the window is created with the flag ``pygame.SCALED``. + See `issue #2380 `_. + + .. ## pygame.display.toggle_fullscreen ## + +.. function:: set_gamma + + | :sl:`Change the hardware gamma ramps` + | :sg:`set_gamma(red, green=None, blue=None) -> bool` + + DEPRECATED: This functionality will go away in SDL3. + + Set the red, green, and blue gamma values on the display hardware. If the + green and blue arguments are not passed, they will both be the same as red. + Not all systems and hardware support gamma ramps, if the function succeeds + it will return ``True``. + + A gamma value of ``1.0`` creates a linear color table. Lower values will + darken the display and higher values will brighten. + + .. deprecated:: 2.2.0 + + .. ## pygame.display.set_gamma ## + +.. function:: set_gamma_ramp + + | :sl:`Change the hardware gamma ramps with a custom lookup` + | :sg:`set_gamma_ramp(red, green, blue) -> bool` + + DEPRECATED: This functionality will go away in SDL3. + + Set the red, green, and blue gamma ramps with an explicit lookup table. Each + argument should be sequence of 256 integers. The integers should range + between ``0`` and ``0xffff``. Not all systems and hardware support gamma + ramps, if the function succeeds it will return ``True``. + + .. deprecated:: 2.2.0 + + .. ## pygame.display.set_gamma_ramp ## + +.. function:: set_icon + + | :sl:`Change the system image for the display window` + | :sg:`set_icon(Surface) -> None` + + Sets the runtime icon the system will use to represent the display window. + All windows default to a simple pygame logo for the window icon. + + Note that calling this function implicitly initializes ``pygame.display``, if + it was not initialized before. + + You can pass any surface, but most systems want a smaller image around + 32x32. The image can have colorkey transparency which will be passed to the + system. + + Some systems do not allow the window icon to change after it has been shown. + This function can be called before ``pygame.display.set_mode()`` to create + the icon before the display mode is set. + + .. ## pygame.display.set_icon ## + +.. function:: set_caption + + | :sl:`Set the current window caption` + | :sg:`set_caption(title, icontitle=None) -> None` + + If the display has a window title, this function will change the name on the + window. In pygame 1.x, some systems supported an alternate shorter title to + be used for minimized displays, but in pygame 2 ``icontitle`` does nothing. + + .. versionchanged:: 2.5.0 Added support for keyword arguments. + + .. ## pygame.display.set_caption ## + +.. function:: get_caption + + | :sl:`Get the current window caption` + | :sg:`get_caption() -> (title, icontitle)` + + Returns the title and icontitle for the display window. In pygame 2.x + these will always be the same value. + + .. ## pygame.display.get_caption ## + +.. function:: set_palette + + | :sl:`Set the display color palette for indexed displays` + | :sg:`set_palette(palette=None) -> None` + + This will change the video display color palette for 8-bit displays. This + does not change the palette for the actual display Surface, only the palette + that is used to display the Surface. If no palette argument is passed, the + system default palette will be restored. The palette is a sequence of + ``RGB`` triplets. + + .. versionchanged:: 2.5.0 Added support for keyword arguments. + + .. ## pygame.display.set_palette ## + +.. function:: get_num_displays + + | :sl:`Return the number of displays` + | :sg:`get_num_displays() -> int` + + Returns the number of available displays. This is always 1 if + :func:`pygame.get_sdl_version()` returns a major version number below 2. + + .. versionadded:: 1.9.5 + + .. ## pygame.display.get_num_displays ## + +.. function:: get_window_size + + | :sl:`Return the size of the window or screen` + | :sg:`get_window_size() -> tuple` + + Returns the size of the window initialized with :func:`pygame.display.set_mode()`. + This may differ from the size of the display surface if ``SCALED`` is used. + + .. versionadded:: 2.0.0 + + .. ## pygame.display.get_window_size ## + +.. function:: get_allow_screensaver + + | :sl:`Return whether the screensaver is allowed to run.` + | :sg:`get_allow_screensaver() -> bool` + + Return whether screensaver is allowed to run whilst the app is running. + Default is ``False``. + By default pygame does not allow the screensaver during game play. + + .. note:: Some platforms do not have a screensaver or support + disabling the screensaver. Please see + :func:`pygame.display.set_allow_screensaver()` for + caveats with screensaver support. + + .. versionadded:: 2.0.0 + + .. ## pygame.display.get_allow_screensaver ## + +.. function:: set_allow_screensaver + + | :sl:`Set whether the screensaver may run` + | :sg:`set_allow_screensaver(bool) -> None` + + Change whether screensavers should be allowed whilst the app is running. + The default value of the argument to the function is True. + By default pygame does not allow the screensaver during game play. + + If the screensaver has been disallowed due to this function, it will automatically + be allowed to run when :func:`pygame.quit()` is called. + + It is possible to influence the default value via the environment variable + ``SDL_HINT_VIDEO_ALLOW_SCREENSAVER``, which can be set to either ``0`` (disable) + or ``1`` (enable). + + .. note:: Disabling screensaver is subject to platform support. + When platform support is absent, this function will + silently appear to work even though the screensaver state + is unchanged. The lack of feedback is due to SDL not + providing any supported method for determining whether + it supports changing the screensaver state. + ``SDL_HINT_VIDEO_ALLOW_SCREENSAVER`` is available in SDL 2.0.2 or later. + SDL1.2 does not implement this. + + .. versionadded:: 2.0.0 + + + .. ## pygame.display.set_allow_screensaver ## + +.. ## pygame.display ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/draw.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/draw.rst.txt new file mode 100644 index 00000000..a7598605 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/draw.rst.txt @@ -0,0 +1,557 @@ +.. include:: common.txt + +:mod:`pygame.draw` +================== + +.. module:: pygame.draw + :synopsis: pygame module for drawing shapes + +| :sl:`pygame module for drawing shapes` + +Draw several simple shapes to a surface. These functions will work for +rendering to any format of surface. + +Most of the functions take a width argument to represent the size of stroke +(thickness) around the edge of the shape. If a width of 0 is passed the shape +will be filled (solid). + +All the drawing functions respect the clip area for the surface and will be +constrained to that area. The functions return a rectangle representing the +bounding area of changed pixels. This bounding rectangle is the 'minimum' +bounding box that encloses the affected area. + +All the drawing functions accept a color argument that can be one of the +following formats: + + - a :mod:`pygame.Color` object + - an ``(RGB)`` triplet (tuple/list) + - an ``(RGBA)`` quadruplet (tuple/list) + - an integer value that has been mapped to the surface's pixel format + (see :func:`pygame.Surface.map_rgb` and :func:`pygame.Surface.unmap_rgb`) + +A color's alpha value will be written directly into the surface (if the +surface contains pixel alphas), but the draw function will not draw +transparently. + +These functions temporarily lock the surface they are operating on. Many +sequential drawing calls can be sped up by locking and unlocking the surface +object around the draw calls (see :func:`pygame.Surface.lock` and +:func:`pygame.Surface.unlock`). + +.. note :: + See the :mod:`pygame.gfxdraw` module for alternative draw methods. + + +.. function:: rect + + | :sl:`draw a rectangle` + | :sg:`rect(surface, color, rect) -> Rect` + | :sg:`rect(surface, color, rect, width=0, border_radius=0, border_top_left_radius=-1, border_top_right_radius=-1, border_bottom_left_radius=-1, border_bottom_right_radius=-1) -> Rect` + + Draws a rectangle on the given surface. + + :param Surface surface: surface to draw on + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or int or tuple(int, int, int, [int]) + :param Rect rect: rectangle to draw, position and dimensions + :param int width: (optional) used for line thickness or to indicate that + the rectangle is to be filled (not to be confused with the width value + of the ``rect`` parameter) + + | if ``width == 0``, (default) fill the rectangle + | if ``width > 0``, used for line thickness + | if ``width < 0``, nothing will be drawn + | + + .. versionchanged:: 2.1.1 + Drawing rects with width now draws the width correctly inside the + rect's area, rather than using an internal call to draw.lines(), + which had half the width spill outside the rect area. + + :param int border_radius: (optional) used for drawing rectangle with rounded corners. + The supported range is [0, min(height, width) / 2], with 0 representing a rectangle + without rounded corners. + :param int border_top_left_radius: (optional) used for setting the value of top left + border. If you don't set this value, it will use the border_radius value. + :param int border_top_right_radius: (optional) used for setting the value of top right + border. If you don't set this value, it will use the border_radius value. + :param int border_bottom_left_radius: (optional) used for setting the value of bottom left + border. If you don't set this value, it will use the border_radius value. + :param int border_bottom_right_radius: (optional) used for setting the value of bottom right + border. If you don't set this value, it will use the border_radius value. + + | if ``border_radius < 1`` it will draw rectangle without rounded corners + | if any of border radii has the value ``< 0`` it will use value of the border_radius + | If sum of radii on the same side of the rectangle is greater than the rect size the radii + | will get scaled + + :returns: a rect bounding the changed pixels, if nothing is drawn the + bounding rect's position will be the position of the given ``rect`` + parameter and its width and height will be 0 + :rtype: Rect + + .. note:: + The :func:`pygame.Surface.fill()` method works just as well for drawing + filled rectangles and can be hardware accelerated on some platforms. + + .. versionchanged:: 2.0.0 Added support for keyword arguments. + .. versionchanged:: 2.0.0.dev8 Added support for border radius. + + .. ## pygame.draw.rect ## + +.. function:: polygon + + | :sl:`draw a polygon` + | :sg:`polygon(surface, color, points) -> Rect` + | :sg:`polygon(surface, color, points, width=0) -> Rect` + + Draws a polygon on the given surface. + + :param Surface surface: surface to draw on + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or int or tuple(int, int, int, [int]) + :param points: a sequence of 3 or more (x, y) coordinates that make up the + vertices of the polygon, each *coordinate* in the sequence must be a + tuple/list/:class:`pygame.math.Vector2` of 2 ints/floats, + e.g. ``[(x1, y1), (x2, y2), (x3, y3)]`` + :type points: tuple(coordinate) or list(coordinate) + :param int width: (optional) used for line thickness or to indicate that + the polygon is to be filled + + | if width == 0, (default) fill the polygon + | if width > 0, used for line thickness + | if width < 0, nothing will be drawn + | + + .. note:: + When using ``width`` values ``> 1``, the edge lines will grow + outside the original boundary of the polygon. For more details on + how the thickness for edge lines grow, refer to the ``width`` notes + of the :func:`pygame.draw.line` function. + + :returns: a rect bounding the changed pixels, if nothing is drawn the + bounding rect's position will be the position of the first point in the + ``points`` parameter (float values will be truncated) and its width and + height will be 0 + :rtype: Rect + + :raises ValueError: if ``len(points) < 3`` (must have at least 3 points) + :raises TypeError: if ``points`` is not a sequence or ``points`` does not + contain number pairs + + .. note:: + For an aapolygon, use :func:`aalines()` with ``closed=True``. + + .. versionchanged:: 2.0.0 Added support for keyword arguments. + + .. ## pygame.draw.polygon ## + +.. function:: circle + + | :sl:`draw a circle` + | :sg:`circle(surface, color, center, radius) -> Rect` + | :sg:`circle(surface, color, center, radius, width=0, draw_top_right=None, draw_top_left=None, draw_bottom_left=None, draw_bottom_right=None) -> Rect` + + Draws a circle on the given surface. + + :param Surface surface: surface to draw on + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or int or tuple(int, int, int, [int]) + :param center: center point of the circle as a sequence of 2 ints/floats, + e.g. ``(x, y)`` + :type center: tuple(int or float, int or float) or + list(int or float, int or float) or Vector2(int or float, int or float) + :param radius: radius of the circle, measured from the ``center`` parameter, + nothing will be drawn if the ``radius`` is less than 1 + :type radius: int or float + :param int width: (optional) used for line thickness or to indicate that + the circle is to be filled + + | if ``width == 0``, (default) fill the circle + | if ``width > 0``, used for line thickness + | if ``width < 0``, nothing will be drawn + | + + .. note:: + When using ``width`` values ``> 1``, the edge lines will only grow + inward. + :param bool draw_top_right: (optional) if this is set to True then the top right corner + of the circle will be drawn + :param bool draw_top_left: (optional) if this is set to True then the top left corner + of the circle will be drawn + :param bool draw_bottom_left: (optional) if this is set to True then the bottom left corner + of the circle will be drawn + :param bool draw_bottom_right: (optional) if this is set to True then the bottom right corner + of the circle will be drawn + + | if any of the draw_circle_part is True then it will draw all circle parts that have the True + | value, otherwise it will draw the entire circle. + + :returns: a rect bounding the changed pixels, if nothing is drawn the + bounding rect's position will be the ``center`` parameter value (float + values will be truncated) and its width and height will be 0 + :rtype: Rect + + :raises TypeError: if ``center`` is not a sequence of two numbers + :raises TypeError: if ``radius`` is not a number + + .. versionchanged:: 2.0.0 Added support for keyword arguments. + Nothing is drawn when the radius is 0 (a pixel at the ``center`` coordinates + used to be drawn when the radius equaled 0). + Floats, and Vector2 are accepted for the ``center`` param. + The drawing algorithm was improved to look more like a circle. + .. versionchanged:: 2.0.0.dev8 Added support for drawing circle quadrants. + + .. ## pygame.draw.circle ## + +.. function:: ellipse + + | :sl:`draw an ellipse` + | :sg:`ellipse(surface, color, rect) -> Rect` + | :sg:`ellipse(surface, color, rect, width=0) -> Rect` + + Draws an ellipse on the given surface. + + :param Surface surface: surface to draw on + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or int or tuple(int, int, int, [int]) + :param Rect rect: rectangle to indicate the position and dimensions of the + ellipse, the ellipse will be centered inside the rectangle and bounded + by it + :param int width: (optional) used for line thickness or to indicate that + the ellipse is to be filled (not to be confused with the width value + of the ``rect`` parameter) + + | if ``width == 0``, (default) fill the ellipse + | if ``width > 0``, used for line thickness + | if ``width < 0``, nothing will be drawn + | + + .. note:: + When using ``width`` values ``> 1``, the edge lines will only grow + inward from the original boundary of the ``rect`` parameter. + + :returns: a rect bounding the changed pixels, if nothing is drawn the + bounding rect's position will be the position of the given ``rect`` + parameter and its width and height will be 0 + :rtype: Rect + + .. versionchanged:: 2.0.0 Added support for keyword arguments. + + .. ## pygame.draw.ellipse ## + +.. function:: arc + + | :sl:`draw an elliptical arc` + | :sg:`arc(surface, color, rect, start_angle, stop_angle) -> Rect` + | :sg:`arc(surface, color, rect, start_angle, stop_angle, width=1) -> Rect` + + Draws an elliptical arc on the given surface. + + The two angle arguments are given in radians and indicate the start and stop + positions of the arc. The arc is drawn in a counterclockwise direction from + the ``start_angle`` to the ``stop_angle``. + + :param Surface surface: surface to draw on + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or int or tuple(int, int, int, [int]) + :param Rect rect: rectangle to indicate the position and dimensions of the + ellipse which the arc will be based on, the ellipse will be centered + inside the rectangle + :param float start_angle: start angle of the arc in radians + :param float stop_angle: stop angle of the arc in + radians + + | if ``start_angle < stop_angle``, the arc is drawn in a + counterclockwise direction from the ``start_angle`` to the + ``stop_angle`` + | if ``start_angle > stop_angle``, tau (tau == 2 * pi) will be added + to the ``stop_angle``, if the resulting stop angle value is greater + than the ``start_angle`` the above ``start_angle < stop_angle`` case + applies, otherwise nothing will be drawn + | if ``start_angle == stop_angle``, nothing will be drawn + | + + :param int width: (optional) used for line thickness (not to be confused + with the width value of the ``rect`` parameter) + + | if ``width == 0``, nothing will be drawn + | if ``width > 0``, (default is 1) used for line thickness + | if ``width < 0``, same as ``width == 0`` + + .. note:: + When using ``width`` values ``> 1``, the edge lines will only grow + inward from the original boundary of the ``rect`` parameter. + + :returns: a rect bounding the changed pixels, if nothing is drawn the + bounding rect's position will be the position of the given ``rect`` + parameter and its width and height will be 0 + :rtype: Rect + + .. versionchanged:: 2.0.0 Added support for keyword arguments. + + .. ## pygame.draw.arc ## + +.. function:: line + + | :sl:`draw a straight line` + | :sg:`line(surface, color, start_pos, end_pos) -> Rect` + | :sg:`line(surface, color, start_pos, end_pos, width=1) -> Rect` + + Draws a straight line on the given surface. There are no endcaps. For thick + lines the ends are squared off. + + :param Surface surface: surface to draw on + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or int or tuple(int, int, int, [int]) + :param start_pos: start position of the line, (x, y) + :type start_pos: tuple(int or float, int or float) or + list(int or float, int or float) or Vector2(int or float, int or float) + :param end_pos: end position of the line, (x, y) + :type end_pos: tuple(int or float, int or float) or + list(int or float, int or float) or Vector2(int or float, int or float) + :param int width: (optional) used for line thickness + + | if width >= 1, used for line thickness (default is 1) + | if width < 1, nothing will be drawn + | + + .. note:: + When using ``width`` values ``> 1``, lines will grow as follows. + + For odd ``width`` values, the thickness of each line grows with the + original line being in the center. + + For even ``width`` values, the thickness of each line grows with the + original line being offset from the center (as there is no exact + center line drawn). As a result, lines with a slope < 1 + (horizontal-ish) will have 1 more pixel of thickness below the + original line (in the y direction). Lines with a slope >= 1 + (vertical-ish) will have 1 more pixel of thickness to the right of + the original line (in the x direction). + + :returns: a rect bounding the changed pixels, if nothing is drawn the + bounding rect's position will be the ``start_pos`` parameter value (float + values will be truncated) and its width and height will be 0 + :rtype: Rect + + :raises TypeError: if ``start_pos`` or ``end_pos`` is not a sequence of + two numbers + + .. versionchanged:: 2.0.0 Added support for keyword arguments. + + .. ## pygame.draw.line ## + +.. function:: lines + + | :sl:`draw multiple contiguous straight line segments` + | :sg:`lines(surface, color, closed, points) -> Rect` + | :sg:`lines(surface, color, closed, points, width=1) -> Rect` + + Draws a sequence of contiguous straight lines on the given surface. There are + no endcaps or miter joints. For thick lines the ends are squared off. + Drawing thick lines with sharp corners can have undesired looking results. + + :param Surface surface: surface to draw on + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or int or tuple(int, int, int, [int]) + :param bool closed: if ``True`` an additional line segment is drawn between + the first and last points in the ``points`` sequence + :param points: a sequence of 2 or more (x, y) coordinates, where each + *coordinate* in the sequence must be a + tuple/list/:class:`pygame.math.Vector2` of 2 ints/floats and adjacent + coordinates will be connected by a line segment, e.g. for the + points ``[(x1, y1), (x2, y2), (x3, y3)]`` a line segment will be drawn + from ``(x1, y1)`` to ``(x2, y2)`` and from ``(x2, y2)`` to ``(x3, y3)``, + additionally if the ``closed`` parameter is ``True`` another line segment + will be drawn from ``(x3, y3)`` to ``(x1, y1)`` + :type points: tuple(coordinate) or list(coordinate) + :param int width: (optional) used for line thickness + + | if width >= 1, used for line thickness (default is 1) + | if width < 1, nothing will be drawn + | + + .. note:: + When using ``width`` values ``> 1`` refer to the ``width`` notes + of :func:`line` for details on how thick lines grow. + + :returns: a rect bounding the changed pixels, if nothing is drawn the + bounding rect's position will be the position of the first point in the + ``points`` parameter (float values will be truncated) and its width and + height will be 0 + :rtype: Rect + + :raises ValueError: if ``len(points) < 2`` (must have at least 2 points) + :raises TypeError: if ``points`` is not a sequence or ``points`` does not + contain number pairs + + .. versionchanged:: 2.0.0 Added support for keyword arguments. + + .. ## pygame.draw.lines ## + +.. function:: aaline + + | :sl:`draw a straight antialiased line` + | :sg:`aaline(surface, color, start_pos, end_pos) -> Rect` + | :sg:`aaline(surface, color, start_pos, end_pos, blend=1) -> Rect` + + Draws a straight antialiased line on the given surface. + + The line has a thickness of one pixel and the endpoints have a height and + width of one pixel each. + + The way a line and its endpoints are drawn: + If both endpoints are equal, only a single pixel is drawn (after + rounding floats to nearest integer). + + Otherwise if the line is not steep (i.e. if the length along the x-axis + is greater than the height along the y-axis): + + For each endpoint: + + If ``x``, the endpoint's x-coordinate, is a whole number find + which pixels would be covered by it and draw them. + + Otherwise: + + Calculate the position of the nearest point with a whole number + for its x-coordinate, when extending the line past the + endpoint. + + Find which pixels would be covered and how much by that point. + + If the endpoint is the left one, multiply the coverage by (1 - + the decimal part of ``x``). + + Otherwise multiply the coverage by the decimal part of ``x``. + + Then draw those pixels. + + *e.g.:* + | The left endpoint of the line ``((1, 1.3), (5, 3))`` would + cover 70% of the pixel ``(1, 1)`` and 30% of the pixel + ``(1, 2)`` while the right one would cover 100% of the + pixel ``(5, 3)``. + | The left endpoint of the line ``((1.2, 1.4), (4.6, 3.1))`` + would cover 56% *(i.e. 0.8 * 70%)* of the pixel ``(1, 1)`` + and 24% *(i.e. 0.8 * 30%)* of the pixel ``(1, 2)`` while + the right one would cover 42% *(i.e. 0.6 * 70%)* of the + pixel ``(5, 3)`` and 18% *(i.e. 0.6 * 30%)* of the pixel + ``(5, 4)`` while the right + + Then for each point between the endpoints, along the line, whose + x-coordinate is a whole number: + + Find which pixels would be covered and how much by that point and + draw them. + + *e.g.:* + | The points along the line ``((1, 1), (4, 2.5))`` would be + ``(2, 1.5)`` and ``(3, 2)`` and would cover 50% of the pixel + ``(2, 1)``, 50% of the pixel ``(2, 2)`` and 100% of the pixel + ``(3, 2)``. + | The points along the line ``((1.2, 1.4), (4.6, 3.1))`` would + be ``(2, 1.8)`` (covering 20% of the pixel ``(2, 1)`` and 80% + of the pixel ``(2, 2)``), ``(3, 2.3)`` (covering 70% of the + pixel ``(3, 2)`` and 30% of the pixel ``(3, 3)``) and ``(4, + 2.8)`` (covering 20% of the pixel ``(2, 1)`` and 80% of the + pixel ``(2, 2)``) + + Otherwise do the same for steep lines as for non-steep lines except + along the y-axis instead of the x-axis (using ``y`` instead of ``x``, + top instead of left and bottom instead of right). + + .. note:: + Regarding float values for coordinates, a point with coordinate + consisting of two whole numbers is considered being right in the center + of said pixel (and having a height and width of 1 pixel would therefore + completely cover it), while a point with coordinate where one (or both) + of the numbers have non-zero decimal parts would be partially covering + two (or four if both numbers have decimal parts) adjacent pixels, *e.g.* + the point ``(1.4, 2)`` covers 60% of the pixel ``(1, 2)`` and 40% of the + pixel ``(2,2)``. + + :param Surface surface: surface to draw on + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or int or tuple(int, int, int, [int]) + :param start_pos: start position of the line, (x, y) + :type start_pos: tuple(int or float, int or float) or + list(int or float, int or float) or Vector2(int or float, int or float) + :param end_pos: end position of the line, (x, y) + :type end_pos: tuple(int or float, int or float) or + list(int or float, int or float) or Vector2(int or float, int or float) + :param int blend: (optional) (deprecated) if non-zero (default) the line will be blended + with the surface's existing pixel shades, otherwise it will overwrite them + + :returns: a rect bounding the changed pixels, if nothing is drawn the + bounding rect's position will be the ``start_pos`` parameter value (float + values will be truncated) and its width and height will be 0 + :rtype: Rect + + :raises TypeError: if ``start_pos`` or ``end_pos`` is not a sequence of + two numbers + + .. versionchanged:: 2.0.0 Added support for keyword arguments. + + .. ## pygame.draw.aaline ## + +.. function:: aalines + + | :sl:`draw multiple contiguous straight antialiased line segments` + | :sg:`aalines(surface, color, closed, points) -> Rect` + | :sg:`aalines(surface, color, closed, points, blend=1) -> Rect` + + Draws a sequence of contiguous straight antialiased lines on the given + surface. + + :param Surface surface: surface to draw on + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or int or tuple(int, int, int, [int]) + :param bool closed: if ``True`` an additional line segment is drawn between + the first and last points in the ``points`` sequence + :param points: a sequence of 2 or more (x, y) coordinates, where each + *coordinate* in the sequence must be a + tuple/list/:class:`pygame.math.Vector2` of 2 ints/floats and adjacent + coordinates will be connected by a line segment, e.g. for the + points ``[(x1, y1), (x2, y2), (x3, y3)]`` a line segment will be drawn + from ``(x1, y1)`` to ``(x2, y2)`` and from ``(x2, y2)`` to ``(x3, y3)``, + additionally if the ``closed`` parameter is ``True`` another line segment + will be drawn from ``(x3, y3)`` to ``(x1, y1)`` + :type points: tuple(coordinate) or list(coordinate) + :param int blend: (optional) (deprecated) if non-zero (default) each line will be blended + with the surface's existing pixel shades, otherwise the pixels will be + overwritten + + :returns: a rect bounding the changed pixels, if nothing is drawn the + bounding rect's position will be the position of the first point in the + ``points`` parameter (float values will be truncated) and its width and + height will be 0 + :rtype: Rect + + :raises ValueError: if ``len(points) < 2`` (must have at least 2 points) + :raises TypeError: if ``points`` is not a sequence or ``points`` does not + contain number pairs + + .. versionchanged:: 2.0.0 Added support for keyword arguments. + + .. ## pygame.draw.aalines ## + +.. ## pygame.draw ## + +.. figure:: code_examples/draw_module_example.png + :alt: draw module example + + Example code for draw module. + +.. literalinclude:: code_examples/draw_module_example.py + diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/event.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/event.rst.txt new file mode 100644 index 00000000..0e4db222 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/event.rst.txt @@ -0,0 +1,565 @@ +.. include:: common.txt + +:mod:`pygame.event` +=================== + +.. module:: pygame.event + :synopsis: pygame module for interacting with events and queues + +| :sl:`pygame module for interacting with events and queues` + +Pygame handles all its event messaging through an event queue. The routines in +this module help you manage that event queue. The input queue is heavily +dependent on the :mod:`pygame.display` module. If the display has not been +initialized and a video mode not set, the event queue may not work properly. + +The event queue has an upper limit on the number of events it can hold. When +the queue becomes full new events are quietly dropped. To prevent lost events, +especially input events which signal a quit command, your program must handle +events every frame (with ``pygame.event.get()``, ``pygame.event.pump()``, +``pygame.event.wait()``, ``pygame.event.peek()`` or ``pygame.event.clear()``) +and process them. Not handling events may cause your system to decide your +program has locked up. To speed up queue processing use +:func:`pygame.event.set_blocked()` to limit which events get queued. + +To get the state of various input devices, you can forego the event queue and +access the input devices directly with their appropriate modules: +:mod:`pygame.mouse`, :mod:`pygame.key`, and :mod:`pygame.joystick`. If you use +this method, remember that pygame requires some form of communication with the +system window manager and other parts of the platform. To keep pygame in sync +with the system, you will need to call :func:`pygame.event.pump()` to keep +everything current. Usually, this should be called once per game loop. +Note: Joysticks will not send any events until the device has been initialized. + +The event queue contains :class:`pygame.event.Event` event objects. +There are a variety of ways to access the queued events, from simply +checking for the existence of events, to grabbing them directly off the stack. +The event queue also offers some simple filtering which can slightly help +performance by blocking certain event types from the queue. Use +:func:`pygame.event.set_allowed()` and :func:`pygame.event.set_blocked()` to +change this filtering. By default, all event types can be placed on the queue. + +All :class:`pygame.event.Event` instances contain an event type identifier +and attributes specific to that event type. The event type identifier is +accessible as the :attr:`pygame.event.Event.type` property. Any of the +event specific attributes can be accessed through the +:attr:`pygame.event.Event.__dict__` attribute or directly as an attribute +of the event object (as member lookups are passed through to the object's +dictionary values). The event object has no method functions. Users can create +their own new events with the :func:`pygame.event.Event()` function. + +The event type identifier is in between the values of ``NOEVENT`` and +``NUMEVENTS``. User defined events should have a value in the inclusive range +of ``USEREVENT`` to ``NUMEVENTS - 1``. User defined events can get a custom +event number with :func:`pygame.event.custom_type()`. +It is recommended all user events follow this system. + +Events support equality and inequality comparisons. Two events are equal if +they are the same type and have identical attribute values. + +While debugging and experimenting, you can print an event object for a quick +display of its type and members. The function :func:`pygame.event.event_name()` +can be used to get a string representing the name of the event type. + +Events that come from the system will have a guaranteed set of member +attributes based on the type. The following is a list event types with their +specific attributes. + +:: + + QUIT none + ACTIVEEVENT gain, state + KEYDOWN key, mod, unicode, scancode + KEYUP key, mod, unicode, scancode + MOUSEMOTION pos, rel, buttons, touch + MOUSEBUTTONUP pos, button, touch + MOUSEBUTTONDOWN pos, button, touch + JOYAXISMOTION joy (deprecated), instance_id, axis, value + JOYBALLMOTION joy (deprecated), instance_id, ball, rel + JOYHATMOTION joy (deprecated), instance_id, hat, value + JOYBUTTONUP joy (deprecated), instance_id, button + JOYBUTTONDOWN joy (deprecated), instance_id, button + VIDEORESIZE size, w, h + VIDEOEXPOSE none + USEREVENT code + +.. versionchanged:: 2.0.0 The ``joy`` attribute was deprecated, ``instance_id`` was added. + +.. versionchanged:: 2.0.1 The ``unicode`` attribute was added to ``KEYUP`` event. + +Note that ``ACTIVEEVENT``, ``VIDEORESIZE`` and ``VIDEOEXPOSE`` are considered +as "legacy" events, the use of pygame2 ``WINDOWEVENT`` API is recommended over +the use of this older API. + +You can also find a list of constants for keyboard keys +:ref:`here `. + +A keyboard event occurs when a key is pressed (``KEYDOWN``) and when a key is released (``KEYUP``) +The ``key`` attribute of keyboard events contains the value of what key was pressed or released. +The ``mod`` attribute contains information about the state of keyboard modifiers (SHIFT, CTRL, ALT, etc.). +The ``unicode`` attribute stores the 16-bit unicode value of the key that was pressed or released. +The ``scancode`` attribute represents the physical location of a key on the keyboard. + +The ``ACTIVEEVENT`` contains information about the application gaining or losing focus. The ``gain`` attribute +will be 1 if the mouse enters the window, otherwise ``gain`` will be 0. The ``state`` attribute will have a +value of ``SDL_APPMOUSEFOCUS`` if mouse focus was gained/lost, ``SDL_APPINPUTFOCUS`` if the application loses +or gains keyboard focus, or ``SDL_APPACTIVE`` if the application is minimized (``gain`` will be 0) or restored. + +| + +When compiled with SDL2, pygame has these additional events and their +attributes. + +:: + + AUDIODEVICEADDED which, iscapture (SDL backend >= 2.0.4) + AUDIODEVICEREMOVED which, iscapture (SDL backend >= 2.0.4) + FINGERMOTION touch_id, finger_id, x, y, dx, dy + FINGERDOWN touch_id, finger_id, x, y, dx, dy + FINGERUP touch_id, finger_id, x, y, dx, dy + MOUSEWHEEL which, flipped, x, y, touch, precise_x, precise_y + MULTIGESTURE touch_id, x, y, pinched, rotated, num_fingers + TEXTEDITING text, start, length + TEXTINPUT text + +.. versionadded:: 1.9.5 + +.. versionchanged:: 2.0.2 Fixed amount horizontal scroll (x, positive to the right and negative to the left). + +.. versionchanged:: 2.0.2 The ``touch`` attribute was added to all the ``MOUSE`` events. + +The ``touch`` attribute of ``MOUSE`` events indicates whether or not the events were generated +by a touch input device, and not a real mouse. You might want to ignore such events, if your application +already handles ``FINGERMOTION``, ``FINGERDOWN`` and ``FINGERUP`` events. + +.. versionadded:: 2.1.3 Added ``precise_x`` and ``precise_y`` to ``MOUSEWHEEL`` events + +``MOUSEWHEEL`` event occurs whenever the mouse wheel is moved. +The ``which`` attribute determines if the event was generated from a touch input device vs an actual +mousewheel. +The ``preciseX`` attribute contains a float with the amount scrolled horizontally (positive to the right, +negative to the left). +The ``preciseY`` attribute contains a float with the amount scrolled vertically (positive away from user, +negative towards user). +The ``flipped`` attribute determines if the values in x and y will be opposite or not. If ``SDL_MOUSEWHEEL_FLIPPED`` +is defined, the direction of x and y will be opposite. + +``TEXTEDITING`` event is triggered when a user activates an input method via hotkey or selecting an +input method in a GUI and starts typing + +The ``which`` attribute for ``AUDIODEVICE*`` events is an integer representing the index for new audio +devices that are added. ``AUDIODEVICE*`` events are used to update audio settings or device list. + +| + +Many new events were introduced in pygame 2. + +pygame can recognize text or files dropped in its window. If a file +is dropped, ``DROPFILE`` event will be sent, ``file`` will be its path. +The ``DROPTEXT`` event is only supported on X11. + +``MIDIIN`` and ``MIDIOUT`` are events reserved for :mod:`pygame.midi` use. +``MIDI*`` events differ from ``AUDIODEVICE*`` events in that AUDIODEVICE +events are triggered when there is a state change related to an audio +input/output device. + +pygame 2 also supports controller hot-plugging + +:: + + Event name Attributes and notes + + DROPFILE file + DROPBEGIN (SDL backend >= 2.0.5) + DROPCOMPLETE (SDL backend >= 2.0.5) + DROPTEXT text (SDL backend >= 2.0.5) + MIDIIN + MIDIOUT + CONTROLLERDEVICEADDED device_index + JOYDEVICEADDED device_index + CONTROLLERDEVICEREMOVED instance_id + JOYDEVICEREMOVED instance_id + CONTROLLERDEVICEREMAPPED instance_id + KEYMAPCHANGED (SDL backend >= 2.0.4) + CLIPBOARDUPDATE + RENDER_TARGETS_RESET (SDL backend >= 2.0.2) + RENDER_DEVICE_RESET (SDL backend >= 2.0.4) + LOCALECHANGED (SDL backend >= 2.0.14) + +Also in this version, ``instance_id`` attributes were added to joystick events, +and the ``joy`` attribute was deprecated. + +``KEYMAPCHANGED`` is a type of an event sent when keymap changes due to a +system event such as an input language or keyboard layout change. + +``CLIPBOARDUPDATE`` is an event sent when clipboard changes. This can still +be considered as an experimental feature, some kinds of clipboard changes might +not trigger this event. + +``LOCALECHANGED`` is an event sent when user locale changes + +.. versionadded:: 2.0.0 + +.. versionadded:: 2.1.3 ``KEYMAPCHANGED``, ``CLIPBOARDUPDATE``, + ``RENDER_TARGETS_RESET``, ``RENDER_DEVICE_RESET`` and ``LOCALECHANGED`` + +| + +Since pygame 2.0.1, there are a new set of events, called window events. +Here is a list of all window events, along with a short description + +:: + + Event type Short description + + WINDOWSHOWN Window became shown + WINDOWHIDDEN Window became hidden + WINDOWEXPOSED Window got updated by some external event + WINDOWMOVED Window got moved + WINDOWRESIZED Window got resized + WINDOWSIZECHANGED Window changed its size + WINDOWMINIMIZED Window was minimized + WINDOWMAXIMIZED Window was maximized + WINDOWRESTORED Window was restored + WINDOWENTER Mouse entered the window + WINDOWLEAVE Mouse left the window + WINDOWFOCUSGAINED Window gained focus + WINDOWFOCUSLOST Window lost focus + WINDOWCLOSE Window was closed + WINDOWTAKEFOCUS Window was offered focus (SDL backend >= 2.0.5) + WINDOWHITTEST Window has a special hit test (SDL backend >= 2.0.5) + WINDOWICCPROFCHANGED Window ICC profile changed (SDL backend >= 2.0.18) + WINDOWDISPLAYCHANGED Window moved on a new display (SDL backend >= 2.0.18) + + +``WINDOWMOVED``, ``WINDOWRESIZED`` and ``WINDOWSIZECHANGED`` have ``x`` and +``y`` attributes, ``WINDOWDISPLAYCHANGED`` has a ``display_index`` attribute. +All windowevents have a ``window`` attribute. + +.. versionadded:: 2.0.1 + +.. versionadded:: 2.1.3 ``WINDOWICCPROFCHANGED`` and ``WINDOWDISPLAYCHANGED`` + +| + +On Android, the following events can be generated + +:: + + Event type Short description + + APP_TERMINATING OS is terminating the application + APP_LOWMEMORY OS is low on memory, try to free memory if possible + APP_WILLENTERBACKGROUND Application is entering background + APP_DIDENTERBACKGROUND Application entered background + APP_WILLENTERFOREGROUND Application is entering foreground + APP_DIDENTERFOREGROUND Application entered foreground + +.. versionadded:: 2.1.3 + +| + +.. function:: pump + + | :sl:`internally process pygame event handlers` + | :sg:`pump() -> None` + + For each frame of your game, you will need to make some sort of call to the + event queue. This ensures your program can internally interact with the rest + of the operating system. If you are not using other event functions in your + game, you should call ``pygame.event.pump()`` to allow pygame to handle + internal actions. + + This function is not necessary if your program is consistently processing + events on the queue through the other :mod:`pygame.event` functions. + + There are important things that must be dealt with internally in the event + queue. The main window may need to be repainted or respond to the system. If + you fail to make a call to the event queue for too long, the system may + decide your program has locked up. + + .. caution:: + This function should only be called in the thread that initialized :mod:`pygame.display`. + + .. ## pygame.event.pump ## + +.. function:: get + + | :sl:`get events from the queue` + | :sg:`get(eventtype=None) -> Eventlist` + | :sg:`get(eventtype=None, pump=True) -> Eventlist` + | :sg:`get(eventtype=None, pump=True, exclude=None) -> Eventlist` + + This will get all the messages and remove them from the queue. If a type or + sequence of types is given only those messages will be removed from the + queue and returned. + + If a type or sequence of types is passed in the ``exclude`` argument + instead, then all only *other* messages will be removed from the queue. If + an ``exclude`` parameter is passed, the ``eventtype`` parameter *must* be + None. + + If you are only taking specific events from the queue, be aware that the + queue could eventually fill up with the events you are not interested. + + If ``pump`` is ``True`` (the default), then :func:`pygame.event.pump()` will be called. + + .. versionchanged:: 1.9.5 Added ``pump`` argument + .. versionchanged:: 2.0.2 Added ``exclude`` argument + + .. ## pygame.event.get ## + +.. function:: poll + + | :sl:`get a single event from the queue` + | :sg:`poll() -> Event instance` + + Returns a single event from the queue. If the event queue is empty an event + of type ``pygame.NOEVENT`` will be returned immediately. The returned event + is removed from the queue. + + .. caution:: + This function should only be called in the thread that initialized :mod:`pygame.display`. + + .. ## pygame.event.poll ## + +.. function:: wait + + | :sl:`wait for a single event from the queue` + | :sg:`wait() -> Event instance` + | :sg:`wait(timeout) -> Event instance` + + Returns a single event from the queue. If the queue is empty this function + will wait until one is created. From pygame 2.0.0, if a ``timeout`` argument + is given, the function will return an event of type ``pygame.NOEVENT`` + if no events enter the queue in ``timeout`` milliseconds. The event is removed + from the queue once it has been returned. While the program is waiting it will + sleep in an idle state. This is important for programs that want to share the + system with other applications. + + .. versionchanged:: 2.0.0.dev13 Added ``timeout`` argument + + .. caution:: + This function should only be called in the thread that initialized :mod:`pygame.display`. + + .. ## pygame.event.wait ## + +.. function:: peek + + | :sl:`test if event types are waiting on the queue` + | :sg:`peek(eventtype=None) -> bool` + | :sg:`peek(eventtype=None, pump=True) -> bool` + + Returns ``True`` if there are any events of the given type waiting on the + queue. If a sequence of event types is passed, this will return ``True`` if + any of those events are on the queue. + + If ``pump`` is ``True`` (the default), then :func:`pygame.event.pump()` will be called. + + .. versionchanged:: 1.9.5 Added ``pump`` argument + + .. ## pygame.event.peek ## + +.. function:: clear + + | :sl:`remove all events from the queue` + | :sg:`clear(eventtype=None) -> None` + | :sg:`clear(eventtype=None, pump=True) -> None` + + Removes all events from the queue. If ``eventtype`` is given, removes the given event + or sequence of events. This has the same effect as :func:`pygame.event.get()` except ``None`` + is returned. It can be slightly more efficient when clearing a full event queue. + + If ``pump`` is ``True`` (the default), then :func:`pygame.event.pump()` will be called. + + .. versionchanged:: 1.9.5 Added ``pump`` argument + + .. ## pygame.event.clear ## + +.. function:: event_name + + | :sl:`get the string name from an event id` + | :sg:`event_name(type) -> string` + + Returns a string representing the name (in CapWords style) of the given + event type. + + "UserEvent" is returned for all values in the user event id range. + "Unknown" is returned when the event type does not exist. + + .. versionchanged:: 2.5.0 Added support for keyword arguments. + .. ## pygame.event.event_name ## + + +.. function:: set_blocked + + | :sl:`control which events are allowed on the queue` + | :sg:`set_blocked(type) -> None` + | :sg:`set_blocked(typelist) -> None` + | :sg:`set_blocked(None) -> None` + + The given event types are not allowed to appear on the event queue. By + default all events can be placed on the queue. It is safe to disable an + event type multiple times. + + If ``None`` is passed as the argument, ALL of the event types are blocked + from being placed on the queue. + + .. ## pygame.event.set_blocked ## + +.. function:: set_allowed + + | :sl:`control which events are allowed on the queue` + | :sg:`set_allowed(type) -> None` + | :sg:`set_allowed(typelist) -> None` + | :sg:`set_allowed(None) -> None` + + The given event types are allowed to appear on the event queue. By default, + all event types can be placed on the queue. It is safe to enable an event + type multiple times. + + If ``None`` is passed as the argument, ALL of the event types are allowed + to be placed on the queue. + + .. ## pygame.event.set_allowed ## + +.. function:: get_blocked + + | :sl:`test if a type of event is blocked from the queue` + | :sg:`get_blocked(type) -> bool` + | :sg:`get_blocked(typelist) -> bool` + + Returns ``True`` if the given event type is blocked from the queue. If a + sequence of event types is passed, this will return ``True`` if any of those + event types are blocked. + + .. ## pygame.event.get_blocked ## + +.. function:: set_grab + + | :sl:`control the sharing of input devices with other applications` + | :sg:`set_grab(bool) -> None` + + When your program runs in a windowed environment, it will share the mouse + and keyboard devices with other applications that have focus. If your + program sets the event grab to ``True``, it will lock all input into your + program. + + It is best to not always grab the input, since it prevents the user from + doing other things on their system. + + .. ## pygame.event.set_grab ## + +.. function:: get_grab + + | :sl:`test if the program is sharing input devices` + | :sg:`get_grab() -> bool` + + Returns ``True`` when the input events are grabbed for this application. + + .. ## pygame.event.get_grab ## + +.. function:: set_keyboard_grab + + | :sl:`grab enables capture of system keyboard shortcuts like Alt+Tab or the Meta/Super key.` + | :sg:`set_keyboard_grab(bool) -> None` + + Keyboard grab enables capture of system keyboard shortcuts like Alt+Tab or the Meta/Super key. + Note that not all system keyboard shortcuts can be captured by applications (one example is Ctrl+Alt+Del on Windows). + This is primarily intended for specialized applications such as VNC clients or VM frontends. Normal games should not use keyboard grab. + + .. versionadded:: 2.5.0 + + .. ## pygame.event.set_keyboard_grab ## + +.. function:: get_keyboard_grab + + | :sl:`get the current keyboard grab state` + | :sg:`get_keyboard_grab() -> bool` + + Returns ``True`` when keyboard grab is enabled. + + .. versionadded:: 2.5.0 + + .. ## pygame.event.get_keyboard_grab ## + +.. function:: post + + | :sl:`place a new event on the queue` + | :sg:`post(Event) -> bool` + + Places the given event at the end of the event queue. + + This is usually used for placing custom events on the event queue. + Any type of event can be posted, and the events posted can have any attributes. + + This returns a boolean on whether the event was posted or not. Blocked events + cannot be posted, and this function returns ``False`` if you try to post them. + + .. versionchanged:: 2.0.1 returns a boolean, previously returned ``None`` + + .. ## pygame.event.post ## + +.. function:: custom_type + + | :sl:`make custom user event type` + | :sg:`custom_type() -> int` + + Reserves a ``pygame.USEREVENT`` for a custom use. + + If too many events are made a :exc:`pygame.error` is raised. + + .. versionadded:: 2.0.0.dev3 + + .. ## pygame.event.custom_type ## + +.. class:: Event + + | :sl:`pygame object for representing events` + | :sg:`Event(type, dict) -> Event` + | :sg:`Event(type, \**attributes) -> Event` + + A pygame object used for representing an event. ``Event`` instances + support attribute assignment and deletion. + + When creating the object, the attributes may come from a dictionary + argument with string keys or from keyword arguments. + + .. note:: + From version 2.1.3 ``EventType`` is an alias for ``Event``. Beforehand, + ``Event`` was a function that returned ``EventType`` instances. Use of + ``Event`` is preferred over ``EventType`` wherever it is possible, as + the latter could be deprecated in a future version. + + .. attribute:: type + + | :sl:`event type identifier.` + | :sg:`type -> int` + + Read-only. The event type identifier. For user created event + objects, this is the ``type`` argument passed to + :func:`pygame.event.Event()`. + + For example, some predefined event identifiers are ``QUIT`` and + ``MOUSEMOTION``. + + .. ## pygame.event.Event.type ## + + .. attribute:: __dict__ + + | :sl:`event attribute dictionary` + | :sg:`__dict__ -> dict` + + Read-only. The event type specific attributes of an event. The + ``dict`` attribute is a synonym for backward compatibility. + + For example, the attributes of a ``KEYDOWN`` event would be ``unicode``, + ``key``, and ``mod`` + + .. ## pygame.event.Event.__dict__ ## + + .. versionadded:: 1.9.2 Mutable attributes. + + .. ## pygame.event.Event ## + +.. ## pygame.event ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/examples.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/examples.rst.txt new file mode 100644 index 00000000..000ea76c --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/examples.rst.txt @@ -0,0 +1,451 @@ +.. include:: common.txt + +:mod:`pygame.examples` +====================== + +.. module:: pygame.examples + :synopsis: module of example programs + +| :sl:`module of example programs` + +These examples should help get you started with pygame. Here is a brief rundown +of what you get. The source code for these examples is in the public domain. +Feel free to use for your own projects. + +There are several ways to run the examples. First they can be run as +stand-alone programs. Second they can be imported and their ``main()`` methods +called (see below). Finally, the easiest way is to use the python -m option: + +:: + + python -m pygame.examples. + +eg: + +:: + + python -m pygame.examples.scaletest someimage.png + +Resources such as images and sounds for the examples are found in the +pygame/examples/data subdirectory. + +You can find where the example files are installed by using the following +commands inside the python interpreter. + +:: + + >>> import pygame.examples.scaletest + >>> pygame.examples.scaletest.__file__ + '/usr/lib/python2.6/site-packages/pygame/examples/scaletest.py' + +On each OS and version of Python the location will be slightly different. +For example on Windows it might be in 'C:/Python26/Lib/site-packages/pygame/examples/' +On Mac OS X it might be in '/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/pygame/examples/' + + +You can also run the examples in the python interpreter by calling each modules main() function. + +:: + + >>> import pygame.examples.scaletest + >>> pygame.examples.scaletest.main() + + +We're always on the lookout for more examples and/or example requests. Code +like this is probably the best way to start getting involved with python +gaming. + +examples as a package is new to pygame 1.9.0. But most of the examples came with +pygame much earlier. + +.. function:: aliens.main + + | :sl:`play the full aliens example` + | :sg:`aliens.main() -> None` + + This started off as a port of the ``SDL`` demonstration, Aliens. Now it has + evolved into something sort of resembling fun. This demonstrates a lot of + different uses of sprites and optimized blitting. Also transparency, + colorkeys, fonts, sound, music, joystick, and more. (PS, my high score is + 117! goodluck) + + .. ## pygame.examples.aliens.main ## + +.. function:: stars.main + + | :sl:`run a simple starfield example` + | :sg:`stars.main() -> None` + + A simple starfield example. You can change the center of perspective by + leftclicking the mouse on the screen. + + .. ## pygame.examples.stars.main ## + +.. function:: chimp.main + + | :sl:`hit the moving chimp` + | :sg:`chimp.main() -> None` + + This simple example is derived from the line-by-line tutorial that comes + with pygame. It is based on a 'popular' web banner. Note there are comments + here, but for the full explanation, follow along in the tutorial. + + .. ## pygame.examples.chimp.main ## + +.. function:: moveit.main + + | :sl:`display animated objects on the screen` + | :sg:`moveit.main() -> None` + + This is the full and final example from the Pygame Tutorial, "How Do I Make + It Move". It creates 10 objects and animates them on the screen. + + Note it's a bit scant on error checking, but it's easy to read. :] + Fortunately, this is python, and we needn't wrestle with a pile of error + codes. + + .. ## pygame.examples.moveit.main ## + +.. function:: fonty.main + + | :sl:`run a font rendering example` + | :sg:`fonty.main() -> None` + + Super quick, super simple application demonstrating the different ways to + render fonts with the font module + + .. ## pygame.examples.fonty.main ## + +.. function:: freetype_misc.main + + | :sl:`run a FreeType rendering example` + | :sg:`freetype_misc.main() -> None` + + A showcase of rendering features the :class:`pygame.freetype.Font` + class provides in addition to those available with :class:`pygame.font.Font`. + It is a demonstration of direct to surface rendering, with vertical text + and rotated text, opaque text and semi transparent text, horizontally + stretched text and vertically stretched text. + + .. ## pygame.examples.fonty.main ## + +.. function:: vgrade.main + + | :sl:`display a vertical gradient` + | :sg:`vgrade.main() -> None` + + Demonstrates creating a vertical gradient with pixelcopy and NumPy python. + The app will create a new gradient every half second and report the time + needed to create and display the image. If you're not prepared to start + working with the NumPy arrays, don't worry about the source for this one :] + + .. ## pygame.examples.vgrade.main ## + +.. function:: eventlist.main + + | :sl:`display pygame events` + | :sg:`eventlist.main() -> None` + + Eventlist is a sloppy style of pygame, but is a handy tool for learning + about pygame events and input. At the top of the screen are the state of + several device values, and a scrolling list of events are displayed on the + bottom. + + This is not quality 'ui' code at all, but you can see how to implement very + non-interactive status displays, or even a crude text output control. + + .. ## pygame.examples.eventlist.main ## + +.. function:: arraydemo.main + + | :sl:`show various surfarray effects` + | :sg:`arraydemo.main(arraytype=None) -> None` + + Another example filled with various surfarray effects. It requires the + surfarray and image modules to be installed. This little demo can also make + a good starting point for any of your own tests with surfarray + + The ``arraytype`` parameter is deprecated; passing any value besides 'numpy' + will raise ValueError. + + .. ## pygame.examples.arraydemo.main ## + +.. function:: sound.main + + | :sl:`load and play a sound` + | :sg:`sound.main(file_path=None) -> None` + + Extremely basic testing of the mixer module. Load a sound and play it. All + from the command shell, no graphics. + + If provided, use the audio file 'file_path', otherwise use a default file. + + ``sound.py`` optional command line argument: an audio file + + .. ## pygame.examples.sound.main ## + +.. function:: sound_array_demos.main + + | :sl:`play various sndarray effects` + | :sg:`sound_array_demos.main(arraytype=None) -> None` + + + Uses sndarray and NumPy to create offset faded copies of the + original sound. Currently it just uses hardcoded values for the number of + echoes and the delay. Easy for you to recreate as needed. + + The ``arraytype`` parameter is deprecated; passing any value besides 'numpy' + will raise ValueError. + + .. ## pygame.examples.sound_array_demos.main ## + +.. function:: liquid.main + + | :sl:`display an animated liquid effect` + | :sg:`liquid.main() -> None` + + This example was created in a quick comparison with the BlitzBasic gaming + language. Nonetheless, it demonstrates a quick 8-bit setup (with colormap). + + .. ## pygame.examples.liquid.main ## + +.. function:: glcube.main + + | :sl:`display an animated 3D cube using OpenGL` + | :sg:`glcube.main() -> None` + + Using PyOpenGL and pygame, this creates a spinning 3D multicolored cube. + + .. ## pygame.examples.glcube.main ## + +.. function:: scrap_clipboard.main + + | :sl:`access the clipboard` + | :sg:`scrap_clipboard.main() -> None` + + A simple demonstration example for the clipboard support. + + .. ## pygame.examples.scrap_clipboard.main ## + +.. function:: mask.main + + | :sl:`display multiple images bounce off each other using collision detection` + | :sg:`mask.main(*args) -> None` + + Positional arguments: + + :: + + one or more image file names. + + This ``pygame.masks`` demo will display multiple moving sprites bouncing off + each other. More than one sprite image can be provided. + + If run as a program then ``mask.py`` takes one or more image files as + command line arguments. + + .. ## pygame.examples.mask.main ## + +.. function:: testsprite.main + + | :sl:`show lots of sprites moving around` + | :sg:`testsprite.main(update_rects = True, use_static = False, use_FastRenderGroup = False, screen_dims = [640, 480], use_alpha = False, flags = 0) -> None` + + Optional keyword arguments: + + :: + + update_rects - use the RenderUpdate sprite group class + use_static - include non-moving images + use_FastRenderGroup - Use the FastRenderGroup sprite group + screen_dims - pygame window dimensions + use_alpha - use alpha blending + flags - additional display mode flags + + Like the ``testsprite.c`` that comes with SDL, this pygame version shows + lots of sprites moving around. + + If run as a stand-alone program then no command line arguments are taken. + + .. ## pygame.examples.testsprite.main ## + +.. function:: headless_no_windows_needed.main + + | :sl:`write an image file that is smoothscaled copy of an input file` + | :sg:`headless_no_windows_needed.main(fin, fout, w, h) -> None` + + arguments: + + :: + + fin - name of an input image file + fout - name of the output file to create/overwrite + w, h - size of the rescaled image, as integer width and height + + How to use pygame with no windowing system, like on headless servers. + + Thumbnail generation with scaling is an example of what you can do with + pygame. + + ``NOTE``: the pygame scale function uses MMX/SSE if available, and can be + run in multiple threads. + + If ``headless_no_windows_needed.py`` is run as a program it takes the + following command line arguments: + + :: + + -scale inputimage outputimage new_width new_height + eg. -scale in.png outpng 50 50 + + .. ## pygame.examples.headless_no_windows_needed.main ## + +.. function:: joystick.main + + | :sl:`demonstrate joystick functionality` + | :sg:`joystick.main() -> None` + + A demo showing full joystick support. + + .. versionadded:: 2.0.2 + + .. ## pygame.examples.joystick.main ## + +.. function:: blend_fill.main + + | :sl:`demonstrate the various surface.fill method blend options` + | :sg:`blend_fill.main() -> None` + + A interactive demo that lets one choose which BLEND_xxx option to apply to a + surface. + + .. ## pygame.examples.blend_fill.main ## + +.. function:: blit_blends.main + + | :sl:`uses alternative additive fill to that of surface.fill` + | :sg:`blit_blends.main() -> None` + + Fake additive blending. Using NumPy. it doesn't clamp. Press r,g,b Somewhat + like blend_fill. + + .. ## pygame.examples.blit_blends.main ## + +.. function:: cursors.main + + | :sl:`display two different custom cursors` + | :sg:`cursors.main() -> None` + + Display an arrow or circle with crossbar cursor. + + .. ## pygame.examples.cursors.main ## + +.. function:: pixelarray.main + + | :sl:`display various pixelarray generated effects` + | :sg:`pixelarray.main() -> None` + + Display various pixelarray generated effects. + + .. ## pygame.examples.pixelarray.main ## + +.. function:: scaletest.main + + | :sl:`interactively scale an image using smoothscale` + | :sg:`scaletest.main(imagefile, convert_alpha=False, run_speed_test=True) -> None` + + arguments: + + :: + + imagefile - file name of source image (required) + convert_alpha - use convert_alpha() on the surf (default False) + run_speed_test - (default False) + + A smoothscale example that resized an image on the screen. Vertical and + horizontal arrow keys are used to change the width and height of the + displayed image. If the convert_alpha option is True then the source image + is forced to have source alpha, whether or not the original images does. If + run_speed_test is True then a background timing test is performed instead of + the interactive scaler. + + If ``scaletest.py`` is run as a program then the command line options are: + + :: + + ImageFile [-t] [-convert_alpha] + [-t] = Run Speed Test + [-convert_alpha] = Use convert_alpha() on the surf. + + .. ## pygame.examples.scaletest.main ## + +.. function:: midi.main + + | :sl:`run a midi example` + | :sg:`midi.main(mode='output', device_id=None) -> None` + + Arguments: + + :: + + mode - if 'output' run a midi keyboard output example + 'input' run a midi event logger input example + 'list' list available midi devices + (default 'output') + device_id - midi device number; if None then use the default midi input or + output device for the system + + The output example shows how to translate mouse clicks or computer keyboard + events into midi notes. It implements a rudimentary button widget and state + machine. + + The input example shows how to translate midi input to pygame events. + + With the use of a virtual midi patch cord the output and input examples can + be run as separate processes and connected so the keyboard output is + displayed on a console. + + new to pygame 1.9.0 + + .. ## pygame.examples.midi.main ## + +.. function:: scroll.main + + | :sl:`run a Surface.scroll example that shows a magnified image` + | :sg:`scroll.main(image_file=None) -> None` + + This example shows a scrollable image that has a zoom factor of eight. It + uses the :meth:`Surface.scroll() ` + function to shift the image on the display surface. + A clip rectangle protects a margin area. If called as a function, + the example accepts an optional image file path. If run as a program it + takes an optional file path command line argument. If no file is provided a + default image file is used. + + When running click on a black triangle to move one pixel in the direction + the triangle points. Or use the arrow keys. Close the window or press + ``ESC`` to quit. + + .. ## pygame.examples.scroll.main ## + +.. function:: camera.main + + | :sl:`display video captured live from an attached camera` + | :sg:`camera.main() -> None` + + A simple live video player, it uses the first available camera it finds on + the system. + + .. ## pygame.examples.camera.main ## + +.. function:: playmus.main + + | :sl:`play an audio file` + | :sg:`playmus.main(file_path) -> None` + + A simple music player with window and keyboard playback control. Playback can + be paused and rewound to the beginning. + + .. ## pygame.examples.playmus.main ## + +.. ## pygame.examples ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/fastevent.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/fastevent.rst.txt new file mode 100644 index 00000000..a2efe5f3 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/fastevent.rst.txt @@ -0,0 +1,109 @@ +.. include:: common.txt + +:mod:`pygame.fastevent` +======================= + +.. module:: pygame.fastevent + :synopsis: pygame module for interacting with events and queues from multiple + threads. + +| :sl:`pygame module for interacting with events and queues` + +IMPORTANT NOTE: THIS MODULE IS DEPRECATED IN PYGAME 2.2 + +In older pygame versions before pygame 2, :mod:`pygame.event` was not well +suited for posting events from different threads. This module served as a +replacement (with less features) for multithreaded use. Now, the usage of this +module is highly discouraged in favour of use of the main :mod:`pygame.event` +module. This module will be removed in a future pygame version. + +Below, the legacy docs of the module is provided + +.. function:: init + + | :sl:`initialize pygame.fastevent` + | :sg:`init() -> None` + + Initialize the pygame.fastevent module. + + .. ## pygame.fastevent.init ## + +.. function:: get_init + + | :sl:`returns True if the fastevent module is currently initialized` + | :sg:`get_init() -> bool` + + Returns True if the pygame.fastevent module is currently initialized. + + .. ## pygame.fastevent.get_init ## + +.. function:: pump + + | :sl:`internally process pygame event handlers` + | :sg:`pump() -> None` + + For each frame of your game, you will need to make some sort of call to the + event queue. This ensures your program can internally interact with the rest + of the operating system. + + This function is not necessary if your program is consistently processing + events on the queue through the other :mod:`pygame.fastevent` functions. + + There are important things that must be dealt with internally in the event + queue. The main window may need to be repainted or respond to the system. If + you fail to make a call to the event queue for too long, the system may + decide your program has locked up. + + .. ## pygame.fastevent.pump ## + +.. function:: wait + + | :sl:`wait for an event` + | :sg:`wait() -> Event` + + Returns the current event on the queue. If there are no messages + waiting on the queue, this will not return until one is available. + Sometimes it is important to use this wait to get events from the queue, + it will allow your application to idle when the user isn't doing anything + with it. + + .. ## pygame.fastevent.wait ## + +.. function:: poll + + | :sl:`get an available event` + | :sg:`poll() -> Event` + + Returns next event on queue. If there is no event waiting on the queue, + this will return an event with type NOEVENT. + + .. ## pygame.fastevent.poll ## + +.. function:: get + + | :sl:`get all events from the queue` + | :sg:`get() -> list of Events` + + This will get all the messages and remove them from the queue. + + .. ## pygame.fastevent.get ## + +.. function:: post + + | :sl:`place an event on the queue` + | :sg:`post(Event) -> None` + + This will post your own event objects onto the event queue. You can post + any event type you want, but some care must be taken. For example, if you + post a MOUSEBUTTONDOWN event to the queue, it is likely any code receiving + the event will expect the standard MOUSEBUTTONDOWN attributes to be + available, like 'pos' and 'button'. + + Because pygame.fastevent.post() may have to wait for the queue to empty, + you can get into a dead lock if you try to append an event on to a full + queue from the thread that processes events. For that reason I do not + recommend using this function in the main thread of an SDL program. + + .. ## pygame.fastevent.post ## + +.. ## pygame.fastevent ## \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/font.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/font.rst.txt new file mode 100644 index 00000000..9f09a8b3 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/font.rst.txt @@ -0,0 +1,499 @@ +.. include:: common.txt + +:mod:`pygame.font` +================== + +.. module:: pygame.font + :synopsis: pygame module for loading and rendering fonts + +| :sl:`pygame module for loading and rendering fonts` + +The font module allows for rendering TrueType fonts into Surface objects. +This module is built on top of the SDL_ttf library, which comes with all +normal pygame installations. + +Most of the work done with fonts are done by using the actual Font objects. +The module by itself only has routines to support the creation of Font objects +with :func:`pygame.font.Font`. + +You can load fonts from the system by using the :func:`pygame.font.SysFont` +function. There are a few other functions to help look up the system fonts. + +Pygame comes with a builtin default font, freesansbold. This can always be +accessed by passing ``None`` as the font name. + +Before pygame 2.0.3, pygame.font accepts any UCS-2 / UTF-16 character +('\\u0001' to '\\uFFFF'). After 2.0.3, pygame.font built with SDL_ttf +2.0.15 accepts any valid UCS-4 / UTF-32 character +(like emojis, if the font has them) ('\\U00000001' to '\\U0010FFFF')). +More about this in :func:`Font.render`. + +Before pygame 2.0.3, this character space restriction can be avoided by +using the :mod:`pygame.freetype` based ``pygame.ftfont`` to emulate the Font +module. This can be used by defining the environment variable PYGAME_FREETYPE +before the first import of :mod:`pygame`. Since the problem ``pygame.ftfont`` +solves no longer exists, it will likely be removed in the future. + +.. function:: init + + | :sl:`initialize the font module` + | :sg:`init() -> None` + + This method is called automatically by ``pygame.init()``. It initializes the + font module. The module must be initialized before any other functions will + work. + + It is safe to call this function more than once. + + .. ## pygame.font.init ## + +.. function:: quit + + | :sl:`uninitialize the font module` + | :sg:`quit() -> None` + + Manually uninitialize SDL_ttf's font system. This is called automatically by + ``pygame.quit()``. + + It is safe to call this function even if font is currently not initialized. + + .. ## pygame.font.quit ## + +.. function:: get_init + + | :sl:`true if the font module is initialized` + | :sg:`get_init() -> bool` + + Test if the font module is initialized or not. + + .. ## pygame.font.get_init ## + +.. function:: get_default_font + + | :sl:`get the filename of the default font` + | :sg:`get_default_font() -> string` + + Return the filename of the system font. This is not the full path to the + file. This file can usually be found in the same directory as the font + module, but it can also be bundled in separate archives. + + .. ## pygame.font.get_default_font ## + +.. function:: get_sdl_ttf_version + + | :sl:`gets SDL_ttf version` + | :sg:`get_sdl_ttf_version(linked=True) -> (major, minor, patch)` + + **Experimental:** feature still in development available for testing and feedback. It may change. + `Please leave get_sdl_ttf_version feedback with authors `_ + + Returns a tuple of integers that identify SDL_ttf's version. + SDL_ttf is the underlying font rendering library, written in C, + on which pygame's font module depends. If 'linked' is True (the default), + the function returns the version of the linked TTF library. + Otherwise this function returns the version of TTF pygame was compiled with + + .. versionadded:: 2.1.3 + + .. ## pygame.font.get_sdl_ttf_version ## + +.. function:: get_fonts + + | :sl:`get all available fonts` + | :sg:`get_fonts() -> list of strings` + + Returns a list of all the fonts available on the system. The names of the + fonts will be set to lowercase with all spaces and punctuation removed. This + works on most systems, but some will return an empty list if they cannot + find fonts. + + .. versionchanged:: 2.1.3 Checks through user fonts instead of just global fonts for Windows. + + .. ## pygame.font.get_fonts ## + +.. function:: match_font + + | :sl:`find a specific font on the system` + | :sg:`match_font(name, bold=False, italic=False) -> path` + + Returns the full path to a font file on the system. If bold or italic are + set to true, this will attempt to find the correct family of font. + + The font name can also be an iterable of font names, a string of + comma-separated font names, or a bytes of comma-separated font names, in + which case the set of names will be searched in order. + If none of the given names are found, None is returned. + + .. versionadded:: 2.0.1 Accept an iterable of font names. + + .. versionchanged:: 2.1.3 Checks through user fonts instead of just global fonts for Windows. + + Example: + + :: + + print pygame.font.match_font('bitstreamverasans') + # output is: /usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf + # (but only if you have Vera on your system) + + .. ## pygame.font.match_font ## + +.. function:: SysFont + + | :sl:`create a Font object from the system fonts` + | :sg:`SysFont(name, size, bold=False, italic=False) -> Font` + + Return a new Font object that is loaded from the system fonts. The font will + match the requested bold and italic flags. Pygame uses a small set of common + font aliases. If the specific font you ask for is not available, a reasonable + alternative may be used. If a suitable system font is not found this will + fall back on loading the default pygame font. + + The font name can also be an iterable of font names, a string of + comma-separated font names, or a bytes of comma-separated font names, in + which case the set of names will be searched in order. + + .. versionadded:: 2.0.1 Accept an iterable of font names. + + .. versionchanged:: 2.1.3 Checks through user fonts instead of just global fonts for Windows. + + .. ## pygame.font.SysFont ## + +.. class:: Font + + | :sl:`create a new Font object from a file` + | :sg:`Font(file_path=None, size=12) -> Font` + | :sg:`Font(file_path, size) -> Font` + | :sg:`Font(pathlib.Path, size) -> Font` + | :sg:`Font(object, size) -> Font` + + Load a new font from a given filename or a python file object. The size is + the height of the font in pixels. If the filename is ``None`` the pygame + default font will be loaded. If a font cannot be loaded from the arguments + given an exception will be raised. Once the font is created the size cannot + be changed. If no arguments are given then the default font will be used and + a font size of 12 is used. + + Font objects are mainly used to render text into new Surface objects. The + render can emulate bold or italic features, but it is better to load from a + font with actual italic or bold glyphs. + + .. attribute:: bold + + | :sl:`Gets or sets whether the font should be rendered in (faked) bold.` + | :sg:`bold -> bool` + + Whether the font should be rendered in bold. + + When set to True, this enables the bold rendering of text. This + is a fake stretching of the font that doesn't look good on many + font types. If possible load the font from a real bold font + file. While bold, the font will have a different width than when + normal. This can be mixed with the italic, underline and + strikethrough modes. + + .. versionadded:: 2.0.0 + + .. ## Font.bold ## + + .. attribute:: italic + + | :sl:`Gets or sets whether the font should be rendered in (faked) italics.` + | :sg:`italic -> bool` + + Whether the font should be rendered in italic. + + When set to True, this enables fake rendering of italic + text. This is a fake skewing of the font that doesn't look good + on many font types. If possible load the font from a real italic + font file. While italic the font will have a different width + than when normal. This can be mixed with the bold, underline and + strikethrough modes. + + .. versionadded:: 2.0.0 + + .. ## Font.italic ## + + .. attribute:: underline + + | :sl:`Gets or sets whether the font should be rendered with an underline.` + | :sg:`underline -> bool` + + Whether the font should be rendered in underline. + + When set to True, all rendered fonts will include an + underline. The underline is always one pixel thick, regardless + of font size. This can be mixed with the bold, italic and + strikethrough modes. + + .. versionadded:: 2.0.0 + + .. ## Font.underline ## + + .. attribute:: strikethrough + + | :sl:`Gets or sets whether the font should be rendered with a strikethrough.` + | :sg:`strikethrough -> bool` + + Whether the font should be rendered with a strikethrough. + + When set to True, all rendered fonts will include an + strikethrough. The strikethrough is always one pixel thick, + regardless of font size. This can be mixed with the bold, + italic and underline modes. + + .. versionadded:: 2.1.3 + + .. ## Font.strikethrough ## + + .. method:: render + + | :sl:`draw text on a new Surface` + | :sg:`render(text, antialias, color, background=None) -> Surface` + + This creates a new Surface with the specified text rendered on it. + :mod:`pygame.font` provides no way to directly draw text on an existing + Surface: instead you must use :func:`Font.render` to create an image + (Surface) of the text, then blit this image onto another Surface. + + The text can only be a single line: newline characters are not rendered. + Null characters ('\x00') raise a TypeError. Both Unicode and char (byte) + strings are accepted. For Unicode strings only UCS-2 characters + ('\\u0001' to '\\uFFFF') were previously supported and any greater + unicode codepoint would raise a UnicodeError. Now, characters in the + UCS-4 range are supported. For char strings a ``LATIN1`` encoding is + assumed. The antialias argument is a boolean: if True the characters + will have smooth edges. The color argument is the color of the text + [e.g.: (0,0,255) for blue]. The optional background argument is a color + to use for the text background. If no background is passed the area + outside the text will be transparent. + + The Surface returned will be of the dimensions required to hold the text. + (the same as those returned by :func:`Font.size`). If an empty string is passed + for the text, a blank surface will be returned that is zero pixel wide and + the height of the font. + + Depending on the type of background and antialiasing used, this returns + different types of Surfaces. For performance reasons, it is good to know + what type of image will be used. If antialiasing is not used, the return + image will always be an 8-bit image with a two-color palette. If the + background is transparent a colorkey will be set. Antialiased images are + rendered to 24-bit ``RGB`` images. If the background is transparent a + pixel alpha will be included. + + Optimization: if you know that the final destination for the text (on the + screen) will always have a solid background, and the text is antialiased, + you can improve performance by specifying the background color. This will + cause the resulting image to maintain transparency information by + colorkey rather than (much less efficient) alpha values. + + If you render '\\n' an unknown char will be rendered. Usually a + rectangle. Instead you need to handle newlines yourself. + + Font rendering is not thread safe: only a single thread can render text + at any time. + + .. versionchanged:: 2.0.3 Rendering UCS4 unicode works and does not + raise an exception. Use `if hasattr(pygame.font, "UCS4"):` to see if + pygame supports rendering UCS4 unicode including more languages and + emoji. + + .. ## Font.render ## + + .. method:: size + + | :sl:`determine the amount of space needed to render text` + | :sg:`size(text) -> (width, height)` + + Returns the dimensions needed to render the text. This can be used to + help determine the positioning needed for text before it is rendered. It + can also be used for word wrapping and other layout effects. + + Be aware that most fonts use kerning which adjusts the widths for + specific letter pairs. For example, the width for "ae" will not always + match the width for "a" + "e". + + .. ## Font.size ## + + .. method:: set_underline + + | :sl:`control if text is rendered with an underline` + | :sg:`set_underline(bool) -> None` + + When enabled, all rendered fonts will include an underline. The underline + is always one pixel thick, regardless of font size. This can be mixed + with the bold, italic and strikethrough modes. + + .. note:: This is the same as the :attr:`underline` attribute. + + .. ## Font.set_underline ## + + .. method:: get_underline + + | :sl:`check if text will be rendered with an underline` + | :sg:`get_underline() -> bool` + + Return True when the font underline is enabled. + + .. note:: This is the same as the :attr:`underline` attribute. + + .. ## Font.get_underline ## + + .. method:: set_strikethrough + + | :sl:`control if text is rendered with a strikethrough` + | :sg:`set_strikethrough(bool) -> None` + + When enabled, all rendered fonts will include a strikethrough. The + strikethrough is always one pixel thick, regardless of font size. + This can be mixed with the bold, italic and underline modes. + + .. note:: This is the same as the :attr:`strikethrough` attribute. + + .. versionadded:: 2.1.3 + + .. ## Font.set_strikethrough ## + + .. method:: get_strikethrough + + | :sl:`check if text will be rendered with a strikethrough` + | :sg:`get_strikethrough() -> bool` + + Return True when the font strikethrough is enabled. + + .. note:: This is the same as the :attr:`strikethrough` attribute. + + .. versionadded:: 2.1.3 + + .. ## Font.get_strikethrough ## + + .. method:: set_bold + + | :sl:`enable fake rendering of bold text` + | :sg:`set_bold(bool) -> None` + + Enables the bold rendering of text. This is a fake stretching of the font + that doesn't look good on many font types. If possible load the font from + a real bold font file. While bold, the font will have a different width + than when normal. This can be mixed with the italic, underline and + strikethrough modes. + + .. note:: This is the same as the :attr:`bold` attribute. + + .. ## Font.set_bold ## + + .. method:: get_bold + + | :sl:`check if text will be rendered bold` + | :sg:`get_bold() -> bool` + + Return True when the font bold rendering mode is enabled. + + .. note:: This is the same as the :attr:`bold` attribute. + + .. ## Font.get_bold ## + + .. method:: set_italic + + | :sl:`enable fake rendering of italic text` + | :sg:`set_italic(bool) -> None` + + Enables fake rendering of italic text. This is a fake skewing of the font + that doesn't look good on many font types. If possible load the font from + a real italic font file. While italic the font will have a different + width than when normal. This can be mixed with the bold, underline and + strikethrough modes. + + .. note:: This is the same as the :attr:`italic` attribute. + + .. ## Font.set_italic ## + + .. method:: metrics + + | :sl:`gets the metrics for each character in the passed string` + | :sg:`metrics(text) -> list` + + The list contains tuples for each character, which contain the minimum + ``X`` offset, the maximum ``X`` offset, the minimum ``Y`` offset, the + maximum ``Y`` offset and the advance offset (bearing plus width) of the + character. [(minx, maxx, miny, maxy, advance), (minx, maxx, miny, maxy, + advance), ...]. None is entered in the list for each unrecognized + character. + + .. ## Font.metrics ## + + .. method:: get_italic + + | :sl:`check if the text will be rendered italic` + | :sg:`get_italic() -> bool` + + Return True when the font italic rendering mode is enabled. + + .. note:: This is the same as the :attr:`italic` attribute. + + .. ## Font.get_italic ## + + .. method:: get_linesize + + | :sl:`get the line space of the font text` + | :sg:`get_linesize() -> int` + + Return the height in pixels for a line of text with the font. When + rendering multiple lines of text this is the recommended amount of space + between lines. + + .. ## Font.get_linesize ## + + .. method:: get_height + + | :sl:`get the height of the font` + | :sg:`get_height() -> int` + + Return the height in pixels of the actual rendered text. This is the + average size for each glyph in the font. + + .. ## Font.get_height ## + + .. method:: get_ascent + + | :sl:`get the ascent of the font` + | :sg:`get_ascent() -> int` + + Return the height in pixels for the font ascent. The ascent is the number + of pixels from the font baseline to the top of the font. + + .. ## Font.get_ascent ## + + .. method:: get_descent + + | :sl:`get the descent of the font` + | :sg:`get_descent() -> int` + + Return the height in pixels for the font descent. The descent is the + number of pixels from the font baseline to the bottom of the font. + + .. ## Font.get_descent ## + + .. method:: set_script + + | :sl:`set the script code for text shaping` + | :sg:`set_script(str) -> None` + + **Experimental:** feature still in development available for testing and feedback. It may change. + `Please leave feedback with authors `_ + + Sets the script used by harfbuzz text shaping, taking a 4 character + script code as input. For example, Hindi is written in the Devanagari + script, for which the script code is `"Deva"`. See the full list of + script codes in `ISO 15924 `_. + + This method requires pygame built with SDL_ttf 2.20.0 or above. Otherwise the + method will raise a pygame.error. + + .. versionadded:: 2.2.0 + + .. ## Font.set_script ## + + .. ## pygame.font.Font ## + +.. ## pygame.font ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/freetype.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/freetype.rst.txt new file mode 100644 index 00000000..0f282acb --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/freetype.rst.txt @@ -0,0 +1,770 @@ +.. include:: common.txt + +:mod:`pygame.freetype` +====================== + +.. module:: pygame.freetype + :synopsis: Enhanced pygame module for loading and rendering computer fonts + +| :sl:`Enhanced pygame module for loading and rendering computer fonts` + +The ``pygame.freetype`` module is a replacement for :mod:`pygame.font`. +It has all of the functionality of the original, plus many new features. +Yet is has absolutely no dependencies on the SDL_ttf library. +It is implemented directly on the FreeType 2 library. +The ``pygame.freetype`` module is not itself backward compatible with +:mod:`pygame.font`. +Instead, use the ``pygame.ftfont`` module as a drop-in replacement +for :mod:`pygame.font`. + +All font file formats supported by FreeType can be rendered by +``pygame.freetype``, namely ``TTF``, Type1, ``CFF``, OpenType, +``SFNT``, ``PCF``, ``FNT``, ``BDF``, ``PFR`` and Type42 fonts. +All glyphs having UTF-32 code points are accessible +(see :attr:`Font.ucs4`). + +Most work on fonts is done using :class:`Font` instances. +The module itself only has routines for initialization and creation +of :class:`Font` objects. +You can load fonts from the system using the :func:`SysFont` function. + +Extra support of bitmap fonts is available. Available bitmap sizes can +be listed (see :meth:`Font.get_sizes`). For bitmap only fonts :class:`Font` +can set the size for you (see the :attr:`Font.size` property). + +For now undefined character codes are replaced with the ``.notdef`` +(not defined) character. +How undefined codes are handled may become configurable in a future release. + +Pygame comes with a built-in default font. This can always be accessed by +passing None as the font name to the :class:`Font` constructor. + +Extra rendering features available to :class:`pygame.freetype.Font` +are direct to surface rendering (see :meth:`Font.render_to`), character kerning +(see :attr:`Font.kerning`), vertical layout (see :attr:`Font.vertical`), +rotation of rendered text (see :attr:`Font.rotation`), +and the strong style (see :attr:`Font.strong`). +Some properties are configurable, such as +strong style strength (see :attr:`Font.strength`) and underline positioning +(see :attr:`Font.underline_adjustment`). Text can be positioned by the upper +right corner of the text box or by the text baseline (see :attr:`Font.origin`). +Finally, a font's vertical and horizontal size can be adjusted separately +(see :attr:`Font.size`). +The :any:`pygame.examples.freetype_misc ` +example shows these features in use. + +The pygame package does not import ``freetype`` automatically when +loaded. This module must be imported explicitly to be used. :: + + import pygame + import pygame.freetype + +.. versionadded:: 1.9.2 :mod:`freetype` + + +.. function:: get_error + + | :sl:`Return the latest FreeType error` + | :sg:`get_error() -> str` + | :sg:`get_error() -> None` + + Return a description of the last error which occurred in the FreeType2 + library, or ``None`` if no errors have occurred. + +.. function:: get_version + + | :sl:`Return the FreeType version` + | :sg:`get_version(linked=True) -> (int, int, int)` + + Returns the version of the FreeType library in use by this module. ``linked=True`` + is the default behavior and returns the linked version of FreeType and ``linked=False`` + returns the compiled version of FreeType. + + Note that the ``freetype`` module depends on the FreeType 2 library. + It will not compile with the original FreeType 1.0. Hence, the first element + of the tuple will always be "2". + + .. versionchanged:: 2.2.0 ``linked`` keyword argument added and default behavior changed from returning compiled version to returning linked version + +.. function:: init + + | :sl:`Initialize the underlying FreeType library.` + | :sg:`init(cache_size=64, resolution=72) -> None` + + This function initializes the underlying FreeType library and must be + called before trying to use any of the functionality of the ``freetype`` + module. + + However, :func:`pygame.init()` will automatically call this function + if the ``freetype`` module is already imported. It is safe to call this + function more than once. + + Optionally, you may specify a default *cache_size* for the Glyph cache: the + maximum number of glyphs that will be cached at any given time by the + module. Exceedingly small values will be automatically tuned for + performance. Also a default pixel *resolution*, in dots per inch, can + be given to adjust font scaling. + +.. function:: quit + + | :sl:`Shut down the underlying FreeType library.` + | :sg:`quit() -> None` + + This function closes the ``freetype`` module. After calling this + function, you should not invoke any class, method or function related to the + ``freetype`` module as they are likely to fail or might give unpredictable + results. It is safe to call this function even if the module hasn't been + initialized yet. + +.. function:: get_init + + | :sl:`Returns True if the FreeType module is currently initialized.` + | :sg:`get_init() -> bool` + + Returns ``True`` if the ``pygame.freetype`` module is currently initialized. + + .. versionadded:: 1.9.5 + +.. function:: was_init + + | :sl:`DEPRECATED: Use get_init() instead.` + | :sg:`was_init() -> bool` + + DEPRECATED: Returns ``True`` if the ``pygame.freetype`` module is currently + initialized. Use ``get_init()`` instead. + +.. function:: get_cache_size + + | :sl:`Return the glyph case size` + | :sg:`get_cache_size() -> long` + + See :func:`pygame.freetype.init()`. + +.. function:: get_default_resolution + + | :sl:`Return the default pixel size in dots per inch` + | :sg:`get_default_resolution() -> long` + + Returns the default pixel size, in dots per inch, for the module. + The default is 72 DPI. + +.. function:: set_default_resolution + + | :sl:`Set the default pixel size in dots per inch for the module` + | :sg:`set_default_resolution([resolution])` + + Set the default pixel size, in dots per inch, for the module. If the + optional argument is omitted or zero the resolution is reset to 72 DPI. + +.. function:: SysFont + + | :sl:`create a Font object from the system fonts` + | :sg:`SysFont(name, size, bold=False, italic=False) -> Font` + + Return a new Font object that is loaded from the system fonts. The font will + match the requested *bold* and *italic* flags. Pygame uses a small set of + common font aliases. If the specific font you ask for is not available, a + reasonable alternative may be used. If a suitable system font is not found + this will fall back on loading the default pygame font. + + The font *name* can also be an iterable of font names, a string of + comma-separated font names, or a bytes of comma-separated font names, in + which case the set of names will be searched in order. + + .. versionadded:: 2.0.1 Accept an iterable of font names. + +.. function:: get_default_font + + | :sl:`Get the filename of the default font` + | :sg:`get_default_font() -> string` + + Return the filename of the default pygame font. This is not the full path + to the file. The file is usually in the same directory as the font module, + but can also be bundled in a separate archive. + +.. class:: Font + + | :sl:`Create a new Font instance from a supported font file.` + | :sg:`Font(file, size=0, font_index=0, resolution=0, ucs4=False) -> Font` + | :sg:`Font(pathlib.Path) -> Font` + + Argument *file* can be either a string representing the font's filename, a + file-like object containing the font, or None; if None, a default, + Pygame, font is used. + + .. _freetype-font-size-argument: + + Optionally, a *size* argument may be specified to set the default size in + points, which determines the size of the rendered characters. + The size can also be passed explicitly to each method call. + Because of the way the caching system works, specifying a default size on + the constructor doesn't imply a performance gain over manually passing + the size on each function call. If the font is bitmap and no *size* + is given, the default size is set to the first available size for the font. + + If the font file has more than one font, the font to load can be chosen with + the *index* argument. An exception is raised for an out-of-range font index + value. + + The optional *resolution* argument sets the pixel size, in dots per inch, + for use in scaling glyphs for this Font instance. If 0 then the default + module value, set by :func:`init`, is used. The Font object's + resolution can only be changed by re-initializing the Font instance. + + The optional *ucs4* argument, an integer, sets the default text translation + mode: 0 (False) recognize UTF-16 surrogate pairs, any other value (True), + to treat Unicode text as UCS-4, with no surrogate pairs. See + :attr:`Font.ucs4`. + + .. attribute:: name + + | :sl:`Proper font name.` + | :sg:`name -> string` + + Read only. Returns the real (long) name of the font, as + recorded in the font file. + + .. attribute:: path + + | :sl:`Font file path` + | :sg:`path -> unicode` + + Read only. Returns the path of the loaded font file + + .. attribute:: size + + | :sl:`The default point size used in rendering` + | :sg:`size -> float` + | :sg:`size -> (float, float)` + + Get or set the default size for text metrics and rendering. It can be + a single point size, given as a Python ``int`` or ``float``, or a + font ppem (width, height) ``tuple``. Size values are non-negative. + A zero size or width represents an undefined size. In this case + the size must be given as a method argument, or an exception is + raised. A zero width but non-zero height is a ValueError. + + For a scalable font, a single number value is equivalent to a tuple + with width equal height. A font can be stretched vertically with + height set greater than width, or horizontally with width set + greater than height. For embedded bitmaps, as listed by :meth:`get_sizes`, + use the nominal width and height to select an available size. + + Font size differs for a non-scalable, bitmap, font. During a + method call it must match one of the available sizes returned by + method :meth:`get_sizes`. If not, an exception is raised. + If the size is a single number, the size is first matched against the + point size value. If no match, then the available size with the + same nominal width and height is chosen. + + .. method:: get_rect + + | :sl:`Return the size and offset of rendered text` + | :sg:`get_rect(text, style=STYLE_DEFAULT, rotation=0, size=0) -> rect` + + Gets the final dimensions and origin, in pixels, of *text* using the + optional *size* in points, *style*, and *rotation*. For other + relevant render properties, and for any optional argument not given, + the default values set for the :class:`Font` instance are used. + + Returns a :class:`Rect ` instance containing the + width and height of the text's bounding box and the position of the + text's origin. + The origin is useful in aligning separately rendered pieces of text. + It gives the baseline position and bearing at the start of the text. + See the :meth:`render_to` method for an example. + + If *text* is a char (byte) string, its encoding is assumed to be + ``LATIN1``. + + Optionally, *text* can be ``None``, which will return the bounding + rectangle for the text passed to a previous :meth:`get_rect`, + :meth:`render`, :meth:`render_to`, :meth:`render_raw`, or + :meth:`render_raw_to` call. See :meth:`render_to` for more + details. + + .. method:: get_metrics + + | :sl:`Return the glyph metrics for the given text` + | :sg:`get_metrics(text, size=0) -> [(...), ...]` + + Returns the glyph metrics for each character in *text*. + + The glyph metrics are returned as a list of tuples. Each tuple gives + metrics of a single character glyph. The glyph metrics are: + + :: + + (min_x, max_x, min_y, max_y, horizontal_advance_x, horizontal_advance_y) + + The bounding box min_x, max_x, min_y, and max_y values are returned as + grid-fitted pixel coordinates of type int. The advance values are + float values. + + The calculations are done using the font's default size in points. + Optionally you may specify another point size with the *size* argument. + + The metrics are adjusted for the current rotation, strong, and oblique + settings. + + If text is a char (byte) string, then its encoding is assumed to be + ``LATIN1``. + + .. attribute:: height + + | :sl:`The unscaled height of the font in font units` + | :sg:`height -> int` + + Read only. Gets the height of the font. This is the average value of all + glyphs in the font. + + .. attribute:: ascender + + | :sl:`The unscaled ascent of the font in font units` + | :sg:`ascender -> int` + + Read only. Return the number of units from the font's baseline to + the top of the bounding box. + + .. attribute:: descender + + | :sl:`The unscaled descent of the font in font units` + | :sg:`descender -> int` + + Read only. Return the height in font units for the font descent. + The descent is the number of units from the font's baseline to the + bottom of the bounding box. + + .. method:: get_sized_ascender + + | :sl:`The scaled ascent of the font in pixels` + | :sg:`get_sized_ascender(=0) -> int` + + Return the number of units from the font's baseline to the top of the + bounding box. It is not adjusted for strong or rotation. + + .. method:: get_sized_descender + + | :sl:`The scaled descent of the font in pixels` + | :sg:`get_sized_descender(=0) -> int` + + Return the number of pixels from the font's baseline to the top of the + bounding box. It is not adjusted for strong or rotation. + + .. method:: get_sized_height + + | :sl:`The scaled height of the font in pixels` + | :sg:`get_sized_height(=0) -> int` + + Returns the height of the font. This is the average value of all + glyphs in the font. It is not adjusted for strong or rotation. + + .. method:: get_sized_glyph_height + + | :sl:`The scaled bounding box height of the font in pixels` + | :sg:`get_sized_glyph_height(=0) -> int` + + Return the glyph bounding box height of the font in pixels. + This is the average value of all glyphs in the font. + It is not adjusted for strong or rotation. + + .. method:: get_sizes + + | :sl:`return the available sizes of embedded bitmaps` + | :sg:`get_sizes() -> [(int, int, int, float, float), ...]` + | :sg:`get_sizes() -> []` + + Returns a list of tuple records, one for each point size + supported. Each tuple containing the point size, the height in pixels, + width in pixels, horizontal ppem (nominal width) in fractional pixels, + and vertical ppem (nominal height) in fractional pixels. + + .. method:: render + + | :sl:`Return rendered text as a surface` + | :sg:`render(text, fgcolor=None, bgcolor=None, style=STYLE_DEFAULT, rotation=0, size=0) -> (Surface, Rect)` + + Returns a new :class:`Surface `, + with the text rendered to it + in the color given by 'fgcolor'. If no foreground color is given, + the default foreground color, :attr:`fgcolor ` is used. + If ``bgcolor`` is given, the surface + will be filled with this color. When no background color is given, + the surface background is transparent, zero alpha. Normally the returned + surface has a 32 bit pixel size. However, if ``bgcolor`` is ``None`` + and anti-aliasing is disabled a monochrome 8 bit colorkey surface, + with colorkey set for the background color, is returned. + + The return value is a tuple: the new surface and the bounding + rectangle giving the size and origin of the rendered text. + + If an empty string is passed for text then the returned Rect is zero + width and the height of the font. + + Optional *fgcolor*, *style*, *rotation*, and *size* arguments override + the default values set for the :class:`Font` instance. + + If *text* is a char (byte) string, then its encoding is assumed to be + ``LATIN1``. + + Optionally, *text* can be ``None``, which will render the text + passed to a previous :meth:`get_rect`, :meth:`render`, :meth:`render_to`, + :meth:`render_raw`, or :meth:`render_raw_to` call. + See :meth:`render_to` for details. + + .. method:: render_to + + | :sl:`Render text onto an existing surface` + | :sg:`render_to(surf, dest, text, fgcolor=None, bgcolor=None, style=STYLE_DEFAULT, rotation=0, size=0) -> Rect` + + Renders the string *text* to the :mod:`pygame.Surface` *surf*, + at position *dest*, a (x, y) surface coordinate pair. + If either x or y is not an integer it is converted to one if possible. + Any sequence where the first two items are x and y positional elements + is accepted, including a :class:`Rect ` instance. + As with :meth:`render`, + optional *fgcolor*, *style*, *rotation*, and *size* argument are + available. + + If a background color *bgcolor* is given, the text bounding box is + first filled with that color. The text is blitted next. + Both the background fill and text rendering involve full alpha blits. + That is, the alpha values of the foreground, background, and destination + target surface all affect the blit. + + The return value is a rectangle giving the size and position of the + rendered text within the surface. + + If an empty string is passed for text then the returned + :class:`Rect ` is zero width and the height of the font. + The rect will test False. + + Optionally, *text* can be set ``None``, which will re-render text + passed to a previous :meth:`render_to`, :meth:`get_rect`, :meth:`render`, + :meth:`render_raw`, or :meth:`render_raw_to` call. Primarily, this + feature is an aid to using :meth:`render_to` in combination with + :meth:`get_rect`. An example: :: + + def word_wrap(surf, text, font, color=(0, 0, 0)): + font.origin = True + words = text.split(' ') + width, height = surf.get_size() + line_spacing = font.get_sized_height() + 2 + x, y = 0, line_spacing + space = font.get_rect(' ') + for word in words: + bounds = font.get_rect(word) + if x + bounds.width + bounds.x >= width: + x, y = 0, y + line_spacing + if x + bounds.width + bounds.x >= width: + raise ValueError("word too wide for the surface") + if y + bounds.height - bounds.y >= height: + raise ValueError("text to long for the surface") + font.render_to(surf, (x, y), None, color) + x += bounds.width + space.width + return x, y + + When :meth:`render_to` is called with the same + font properties ― :attr:`size`, :attr:`style`, :attr:`strength`, + :attr:`wide`, :attr:`antialiased`, :attr:`vertical`, :attr:`rotation`, + :attr:`kerning`, and :attr:`use_bitmap_strikes` ― as :meth:`get_rect`, + :meth:`render_to` will use the layout calculated by :meth:`get_rect`. + Otherwise, :meth:`render_to` will recalculate the layout if called + with a text string or one of the above properties has changed + after the :meth:`get_rect` call. + + If *text* is a char (byte) string, then its encoding is assumed to be + ``LATIN1``. + + .. method:: render_raw + + | :sl:`Return rendered text as a string of bytes` + | :sg:`render_raw(text, style=STYLE_DEFAULT, rotation=0, size=0, invert=False) -> (bytes, (int, int))` + + Like :meth:`render` but with the pixels returned as a byte string + of 8-bit gray-scale values. The foreground color is 255, the + background 0, useful as an alpha mask for a foreground pattern. + + .. method:: render_raw_to + + | :sl:`Render text into an array of ints` + | :sg:`render_raw_to(array, text, dest=None, style=STYLE_DEFAULT, rotation=0, size=0, invert=False) -> Rect` + + Render to an array object exposing an array struct interface. The array + must be two dimensional with integer items. The default *dest* value, + ``None``, is equivalent to position (0, 0). See :meth:`render_to`. + As with the other render methods, *text* can be ``None`` to + render a text string passed previously to another method. + + The return value is a :func:`pygame.Rect` giving the size and position of + the rendered text. + + .. attribute:: style + + | :sl:`The font's style flags` + | :sg:`style -> int` + + Gets or sets the default style of the Font. This default style will be + used for all text rendering and size calculations unless overridden + specifically a render or :meth:`get_rect` call. + The style value may be a bit-wise OR of one or more of the following + constants: + + :: + + STYLE_NORMAL + STYLE_UNDERLINE + STYLE_OBLIQUE + STYLE_STRONG + STYLE_WIDE + STYLE_DEFAULT + + These constants may be found on the FreeType constants module. + Optionally, the default style can be modified or obtained accessing the + individual style attributes (underline, oblique, strong). + + The ``STYLE_OBLIQUE`` and ``STYLE_STRONG`` styles are for + scalable fonts only. An attempt to set either for a bitmap font raises + an AttributeError. An attempt to set either for an inactive font, + as returned by ``Font.__new__()``, raises a RuntimeError. + + Assigning ``STYLE_DEFAULT`` to the :attr:`style` property leaves + the property unchanged, as this property defines the default. + The :attr:`style` property will never return ``STYLE_DEFAULT``. + + .. attribute:: underline + + | :sl:`The state of the font's underline style flag` + | :sg:`underline -> bool` + + Gets or sets whether the font will be underlined when drawing text. This + default style value will be used for all text rendering and size + calculations unless overridden specifically in a render or + :meth:`get_rect` call, via the 'style' parameter. + + .. attribute:: strong + + | :sl:`The state of the font's strong style flag` + | :sg:`strong -> bool` + + Gets or sets whether the font will be bold when drawing text. This + default style value will be used for all text rendering and size + calculations unless overridden specifically in a render or + :meth:`get_rect` call, via the 'style' parameter. + + .. attribute:: oblique + + | :sl:`The state of the font's oblique style flag` + | :sg:`oblique -> bool` + + Gets or sets whether the font will be rendered as oblique. This + default style value will be used for all text rendering and size + calculations unless overridden specifically in a render or + :meth:`get_rect` call, via the *style* parameter. + + The oblique style is only supported for scalable (outline) fonts. + An attempt to set this style on a bitmap font will raise an + AttributeError. If the font object is inactive, as returned by + ``Font.__new__()``, setting this property raises a RuntimeError. + + .. attribute:: wide + + | :sl:`The state of the font's wide style flag` + | :sg:`wide -> bool` + + Gets or sets whether the font will be stretched horizontally + when drawing text. It produces a result similar to + :class:`pygame.font.Font`'s bold. This style not available for + rotated text. + + .. attribute:: strength + + | :sl:`The strength associated with the strong or wide font styles` + | :sg:`strength -> float` + + The amount by which a font glyph's size is enlarged for the + strong or wide transformations, as a fraction of the untransformed + size. For the wide style only the horizontal dimension is + increased. For strong text both the horizontal and vertical + dimensions are enlarged. A wide style of strength 0.08333 ( 1/12 ) is + equivalent to the :class:`pygame.font.Font` bold style. + The default is 0.02778 ( 1/36 ). + + The strength style is only supported for scalable (outline) fonts. + An attempt to set this property on a bitmap font will raise an + AttributeError. If the font object is inactive, as returned by + ``Font.__new__()``, assignment to this property raises a RuntimeError. + + .. attribute:: underline_adjustment + + | :sl:`Adjustment factor for the underline position` + | :sg:`underline_adjustment -> float` + + Gets or sets a factor which, when positive, is multiplied with the + font's underline offset to adjust the underline position. A negative + value turns an underline into a strike-through or overline. It is + multiplied with the ascender. Accepted values range between -2.0 and 2.0 + inclusive. A value of 0.5 closely matches Tango underlining. A value of + 1.0 mimics :class:`pygame.font.Font` underlining. + + .. attribute:: fixed_width + + | :sl:`Gets whether the font is fixed-width` + | :sg:`fixed_width -> bool` + + Read only. Returns ``True`` if the font contains fixed-width + characters (for example Courier, Bitstream Vera Sans Mono, Andale Mono). + + .. attribute:: fixed_sizes + + | :sl:`the number of available bitmap sizes for the font` + | :sg:`fixed_sizes -> int` + + Read only. Returns the number of point sizes for which the font contains + bitmap character images. If zero then the font is not a bitmap font. + A scalable font may contain pre-rendered point sizes as strikes. + + .. attribute:: scalable + + | :sl:`Gets whether the font is scalable` + | :sg:`scalable -> bool` + + Read only. Returns ``True`` if the font contains outline glyphs. + If so, the point size is not limited to available bitmap sizes. + + .. attribute:: use_bitmap_strikes + + | :sl:`allow the use of embedded bitmaps in an outline font file` + | :sg:`use_bitmap_strikes -> bool` + + Some scalable fonts include embedded bitmaps for particular point + sizes. This property controls whether or not those bitmap strikes + are used. Set it ``False`` to disable the loading of any bitmap + strike. Set it ``True``, the default, to permit bitmap strikes + for a non-rotated render with no style other than :attr:`wide` or + :attr:`underline`. This property is ignored for bitmap fonts. + + See also :attr:`fixed_sizes` and :meth:`get_sizes`. + + .. attribute:: antialiased + + | :sl:`Font anti-aliasing mode` + | :sg:`antialiased -> bool` + + Gets or sets the font's anti-aliasing mode. This defaults to + ``True`` on all fonts, which are rendered with full 8 bit blending. + + Set to ``False`` to do monochrome rendering. This should + provide a small speed gain and reduce cache memory size. + + .. attribute:: kerning + + | :sl:`Character kerning mode` + | :sg:`kerning -> bool` + + Gets or sets the font's kerning mode. This defaults to ``False`` + on all fonts, which will be rendered without kerning. + + Set to ``True`` to add kerning between character pairs, if supported + by the font, when positioning glyphs. + + .. attribute:: vertical + + | :sl:`Font vertical mode` + | :sg:`vertical -> bool` + + Gets or sets whether the characters are laid out vertically rather + than horizontally. May be useful when rendering Kanji or some other + vertical script. + + Set to ``True`` to switch to a vertical text layout. The default + is ``False``, place horizontally. + + Note that the :class:`Font` class does not automatically determine + script orientation. Vertical layout must be selected explicitly. + + Also note that several font formats (especially bitmap based ones) don't + contain the necessary metrics to draw glyphs vertically, so drawing in + those cases will give unspecified results. + + .. attribute:: rotation + + | :sl:`text rotation in degrees counterclockwise` + | :sg:`rotation -> int` + + Gets or sets the baseline angle of the rendered text. The angle is + represented as integer degrees. The default angle is 0, with horizontal + text rendered along the X-axis, and vertical text along the Y-axis. + A positive value rotates these axes counterclockwise that many degrees. + A negative angle corresponds to a clockwise rotation. The rotation + value is normalized to a value within the range 0 to 359 inclusive + (eg. 390 -> 390 - 360 -> 30, -45 -> 360 + -45 -> 315, + 720 -> 720 - (2 * 360) -> 0). + + Only scalable (outline) fonts can be rotated. An attempt to change + the rotation of a bitmap font raises an AttributeError. + An attempt to change the rotation of an inactive font instance, as + returned by ``Font.__new__()``, raises a RuntimeError. + + .. attribute:: fgcolor + + | :sl:`default foreground color` + | :sg:`fgcolor -> Color` + + Gets or sets the default glyph rendering color. It is initially opaque + black ― (0, 0, 0, 255). Applies to :meth:`render` and :meth:`render_to`. + + .. attribute:: bgcolor + + | :sl:`default background color` + | :sg:`bgcolor -> Color` + + Gets or sets the default background rendering color. Initially it is + unset and text will render with a transparent background by default. + Applies to :meth:`render` and :meth:`render_to`. + + .. versionadded:: 2.0.0 + + .. attribute:: origin + + | :sl:`Font render to text origin mode` + | :sg:`origin -> bool` + + If set ``True``, :meth:`render_to` and :meth:`render_raw_to` will + take the *dest* position to be that of the text origin, as opposed to + the top-left corner of the bounding box. See :meth:`get_rect` for + details. + + .. attribute:: pad + + | :sl:`padded boundary mode` + | :sg:`pad -> bool` + + If set ``True``, then the text boundary rectangle will be inflated + to match that of :class:`font.Font `. + Otherwise, the boundary rectangle is just large enough for the text. + + .. attribute:: ucs4 + + | :sl:`Enable UCS-4 mode` + | :sg:`ucs4 -> bool` + + Gets or sets the decoding of Unicode text. By default, the + freetype module performs UTF-16 surrogate pair decoding on Unicode text. + This allows 32-bit escape sequences ('\Uxxxxxxxx') between 0x10000 and + 0x10FFFF to represent their corresponding UTF-32 code points on Python + interpreters built with a UCS-2 Unicode type (on Windows, for instance). + It also means character values within the UTF-16 surrogate area (0xD800 + to 0xDFFF) are considered part of a surrogate pair. A malformed surrogate + pair will raise a UnicodeEncodeError. Setting ucs4 ``True`` turns + surrogate pair decoding off, allowing access the full UCS-4 character + range to a Python interpreter built with four-byte Unicode character + support. + + .. attribute:: resolution + + | :sl:`Pixel resolution in dots per inch` + | :sg:`resolution -> int` + + Read only. Gets pixel size used in scaling font glyphs for this + :class:`Font` instance. diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/gfxdraw.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/gfxdraw.rst.txt new file mode 100644 index 00000000..28153a31 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/gfxdraw.rst.txt @@ -0,0 +1,628 @@ +.. include:: common.txt + +:mod:`pygame.gfxdraw` +===================== + +.. module:: pygame.gfxdraw + :synopsis: pygame module for drawing shapes + +| :sl:`pygame module for drawing shapes` + +**EXPERIMENTAL!**: This API may change or disappear in later pygame releases. If +you use this, your code may break with the next pygame release. + +The pygame package does not import gfxdraw automatically when loaded, so it +must imported explicitly to be used. + +:: + + import pygame + import pygame.gfxdraw + +For all functions the arguments are strictly positional and integers are +accepted for coordinates and radii. The ``color`` argument can be one of the +following formats: + + - a :mod:`pygame.Color` object + - an ``(RGB)`` triplet (tuple/list) + - an ``(RGBA)`` quadruplet (tuple/list) + +The functions :meth:`rectangle` and :meth:`box` will accept any ``(x, y, w, h)`` +sequence for their ``rect`` argument, though :mod:`pygame.Rect` instances are +preferred. + +To draw a filled antialiased shape, first use the antialiased (aa*) version +of the function, and then use the filled (filled_*) version. +For example: + +:: + + col = (255, 0, 0) + surf.fill((255, 255, 255)) + pygame.gfxdraw.aacircle(surf, x, y, 30, col) + pygame.gfxdraw.filled_circle(surf, x, y, 30, col) + + +.. note:: + For threading, each of the functions releases the GIL during the C part of + the call. + +.. note:: + See the :mod:`pygame.draw` module for alternative draw methods. + The ``pygame.gfxdraw`` module differs from the :mod:`pygame.draw` module in + the API it uses and the different draw functions available. + ``pygame.gfxdraw`` wraps the primitives from the library called SDL_gfx, + rather than using modified versions. + +.. versionadded:: 1.9.0 + + +.. function:: pixel + + | :sl:`draw a pixel` + | :sg:`pixel(surface, x, y, color) -> None` + + Draws a single pixel, at position (x ,y), on the given surface. + + :param Surface surface: surface to draw on + :param int x: x coordinate of the pixel + :param int y: y coordinate of the pixel + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or tuple(int, int, int, [int]) + + :returns: ``None`` + :rtype: NoneType + + .. ## pygame.gfxdraw.pixel ## + +.. function:: hline + + | :sl:`draw a horizontal line` + | :sg:`hline(surface, x1, x2, y, color) -> None` + + Draws a straight horizontal line (``(x1, y)`` to ``(x2, y)``) on the given + surface. There are no endcaps. + + :param Surface surface: surface to draw on + :param int x1: x coordinate of one end of the line + :param int x2: x coordinate of the other end of the line + :param int y: y coordinate of the line + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or tuple(int, int, int, [int]) + + :returns: ``None`` + :rtype: NoneType + + .. ## pygame.gfxdraw.hline ## + +.. function:: vline + + | :sl:`draw a vertical line` + | :sg:`vline(surface, x, y1, y2, color) -> None` + + Draws a straight vertical line (``(x, y1)`` to ``(x, y2)``) on the given + surface. There are no endcaps. + + :param Surface surface: surface to draw on + :param int x: x coordinate of the line + :param int y1: y coordinate of one end of the line + :param int y2: y coordinate of the other end of the line + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or tuple(int, int, int, [int]) + + :returns: ``None`` + :rtype: NoneType + + .. ## pygame.gfxdraw.vline ## + +.. function:: line + + | :sl:`draw a line` + | :sg:`line(surface, x1, y1, x2, y2, color) -> None` + + Draws a straight line (``(x1, y1)`` to ``(x2, y2)``) on the given surface. + There are no endcaps. + + :param Surface surface: surface to draw on + :param int x1: x coordinate of one end of the line + :param int y1: y coordinate of one end of the line + :param int x2: x coordinate of the other end of the line + :param int y2: y coordinate of the other end of the line + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or tuple(int, int, int, [int]) + + :returns: ``None`` + :rtype: NoneType + + .. ## pygame.gfxdraw.line ## + +.. function:: rectangle + + | :sl:`draw a rectangle` + | :sg:`rectangle(surface, rect, color) -> None` + + Draws an unfilled rectangle on the given surface. For a filled rectangle use + :meth:`box`. + + :param Surface surface: surface to draw on + :param Rect rect: rectangle to draw, position and dimensions + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or tuple(int, int, int, [int]) + + :returns: ``None`` + :rtype: NoneType + + .. note:: + The ``rect.bottom`` and ``rect.right`` attributes of a :mod:`pygame.Rect` + always lie one pixel outside of its actual border. Therefore, these + values will not be included as part of the drawing. + + .. ## pygame.gfxdraw.rectangle ## + +.. function:: box + + | :sl:`draw a filled rectangle` + | :sg:`box(surface, rect, color) -> None` + + Draws a filled rectangle on the given surface. For an unfilled rectangle use + :meth:`rectangle`. + + :param Surface surface: surface to draw on + :param Rect rect: rectangle to draw, position and dimensions + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or tuple(int, int, int, [int]) + + :returns: ``None`` + :rtype: NoneType + + .. note:: + The ``rect.bottom`` and ``rect.right`` attributes of a :mod:`pygame.Rect` + always lie one pixel outside of its actual border. Therefore, these + values will not be included as part of the drawing. + + .. note:: + The :func:`pygame.Surface.fill` method works just as well for drawing + filled rectangles. In fact :func:`pygame.Surface.fill` can be hardware + accelerated on some platforms with both software and hardware display + modes. + + .. ## pygame.gfxdraw.box ## + +.. function:: circle + + | :sl:`draw a circle` + | :sg:`circle(surface, x, y, r, color) -> None` + + Draws an unfilled circle on the given surface. For a filled circle use + :meth:`filled_circle`. + + :param Surface surface: surface to draw on + :param int x: x coordinate of the center of the circle + :param int y: y coordinate of the center of the circle + :param int r: radius of the circle + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or tuple(int, int, int, [int]) + + :returns: ``None`` + :rtype: NoneType + + .. ## pygame.gfxdraw.circle ## + +.. function:: aacircle + + | :sl:`draw an antialiased circle` + | :sg:`aacircle(surface, x, y, r, color) -> None` + + Draws an unfilled antialiased circle on the given surface. + + :param Surface surface: surface to draw on + :param int x: x coordinate of the center of the circle + :param int y: y coordinate of the center of the circle + :param int r: radius of the circle + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or tuple(int, int, int, [int]) + + :returns: ``None`` + :rtype: NoneType + + .. ## pygame.gfxdraw.aacircle ## + +.. function:: filled_circle + + | :sl:`draw a filled circle` + | :sg:`filled_circle(surface, x, y, r, color) -> None` + + Draws a filled circle on the given surface. For an unfilled circle use + :meth:`circle`. + + :param Surface surface: surface to draw on + :param int x: x coordinate of the center of the circle + :param int y: y coordinate of the center of the circle + :param int r: radius of the circle + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or tuple(int, int, int, [int]) + + :returns: ``None`` + :rtype: NoneType + + .. ## pygame.gfxdraw.filled_circle ## + +.. function:: ellipse + + | :sl:`draw an ellipse` + | :sg:`ellipse(surface, x, y, rx, ry, color) -> None` + + Draws an unfilled ellipse on the given surface. For a filled ellipse use + :meth:`filled_ellipse`. + + :param Surface surface: surface to draw on + :param int x: x coordinate of the center of the ellipse + :param int y: y coordinate of the center of the ellipse + :param int rx: horizontal radius of the ellipse + :param int ry: vertical radius of the ellipse + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or tuple(int, int, int, [int]) + + :returns: ``None`` + :rtype: NoneType + + .. ## pygame.gfxdraw.ellipse ## + +.. function:: aaellipse + + | :sl:`draw an antialiased ellipse` + | :sg:`aaellipse(surface, x, y, rx, ry, color) -> None` + + Draws an unfilled antialiased ellipse on the given surface. + + :param Surface surface: surface to draw on + :param int x: x coordinate of the center of the ellipse + :param int y: y coordinate of the center of the ellipse + :param int rx: horizontal radius of the ellipse + :param int ry: vertical radius of the ellipse + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or tuple(int, int, int, [int]) + + :returns: ``None`` + :rtype: NoneType + + .. ## pygame.gfxdraw.aaellipse ## + +.. function:: filled_ellipse + + | :sl:`draw a filled ellipse` + | :sg:`filled_ellipse(surface, x, y, rx, ry, color) -> None` + + Draws a filled ellipse on the given surface. For an unfilled ellipse use + :meth:`ellipse`. + + :param Surface surface: surface to draw on + :param int x: x coordinate of the center of the ellipse + :param int y: y coordinate of the center of the ellipse + :param int rx: horizontal radius of the ellipse + :param int ry: vertical radius of the ellipse + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or tuple(int, int, int, [int]) + + :returns: ``None`` + :rtype: NoneType + + .. ## pygame.gfxdraw.filled_ellipse ## + +.. function:: arc + + | :sl:`draw an arc` + | :sg:`arc(surface, x, y, r, start_angle, stop_angle, color) -> None` + + Draws an arc on the given surface. For an arc with its endpoints connected + to its center use :meth:`pie`. + + The two angle arguments are given in degrees and indicate the start and stop + positions of the arc. The arc is drawn in a clockwise direction from the + ``start_angle`` to the ``stop_angle``. If ``start_angle == stop_angle``, + nothing will be drawn + + :param Surface surface: surface to draw on + :param int x: x coordinate of the center of the arc + :param int y: y coordinate of the center of the arc + :param int r: radius of the arc + :param int start_angle: start angle in degrees + :param int stop_angle: stop angle in degrees + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or tuple(int, int, int, [int]) + + :returns: ``None`` + :rtype: NoneType + + .. note:: + This function uses *degrees* while the :func:`pygame.draw.arc` function + uses *radians*. + + .. ## pygame.gfxdraw.arc ## + +.. function:: pie + + | :sl:`draw a pie` + | :sg:`pie(surface, x, y, r, start_angle, stop_angle, color) -> None` + + Draws an unfilled pie on the given surface. A pie is an :meth:`arc` with its + endpoints connected to its center. + + The two angle arguments are given in degrees and indicate the start and stop + positions of the pie. The pie is drawn in a clockwise direction from the + ``start_angle`` to the ``stop_angle``. If ``start_angle == stop_angle``, + a straight line will be drawn from the center position at the given angle, + to a length of the radius. + + :param Surface surface: surface to draw on + :param int x: x coordinate of the center of the pie + :param int y: y coordinate of the center of the pie + :param int r: radius of the pie + :param int start_angle: start angle in degrees + :param int stop_angle: stop angle in degrees + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or tuple(int, int, int, [int]) + + :returns: ``None`` + :rtype: NoneType + + .. ## pygame.gfxdraw.pie ## + +.. function:: trigon + + | :sl:`draw a trigon/triangle` + | :sg:`trigon(surface, x1, y1, x2, y2, x3, y3, color) -> None` + + Draws an unfilled trigon (triangle) on the given surface. For a filled + trigon use :meth:`filled_trigon`. + + A trigon can also be drawn using :meth:`polygon` e.g. + ``polygon(surface, ((x1, y1), (x2, y2), (x3, y3)), color)`` + + :param Surface surface: surface to draw on + :param int x1: x coordinate of the first corner of the trigon + :param int y1: y coordinate of the first corner of the trigon + :param int x2: x coordinate of the second corner of the trigon + :param int y2: y coordinate of the second corner of the trigon + :param int x3: x coordinate of the third corner of the trigon + :param int y3: y coordinate of the third corner of the trigon + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or tuple(int, int, int, [int]) + + :returns: ``None`` + :rtype: NoneType + + .. ## pygame.gfxdraw.trigon ## + +.. function:: aatrigon + + | :sl:`draw an antialiased trigon/triangle` + | :sg:`aatrigon(surface, x1, y1, x2, y2, x3, y3, color) -> None` + + Draws an unfilled antialiased trigon (triangle) on the given surface. + + An aatrigon can also be drawn using :meth:`aapolygon` e.g. + ``aapolygon(surface, ((x1, y1), (x2, y2), (x3, y3)), color)`` + + :param Surface surface: surface to draw on + :param int x1: x coordinate of the first corner of the trigon + :param int y1: y coordinate of the first corner of the trigon + :param int x2: x coordinate of the second corner of the trigon + :param int y2: y coordinate of the second corner of the trigon + :param int x3: x coordinate of the third corner of the trigon + :param int y3: y coordinate of the third corner of the trigon + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or tuple(int, int, int, [int]) + + :returns: ``None`` + :rtype: NoneType + + .. ## pygame.gfxdraw.aatrigon ## + +.. function:: filled_trigon + + | :sl:`draw a filled trigon/triangle` + | :sg:`filled_trigon(surface, x1, y1, x2, y2, x3, y3, color) -> None` + + Draws a filled trigon (triangle) on the given surface. For an unfilled + trigon use :meth:`trigon`. + + A filled_trigon can also be drawn using :meth:`filled_polygon` e.g. + ``filled_polygon(surface, ((x1, y1), (x2, y2), (x3, y3)), color)`` + + :param Surface surface: surface to draw on + :param int x1: x coordinate of the first corner of the trigon + :param int y1: y coordinate of the first corner of the trigon + :param int x2: x coordinate of the second corner of the trigon + :param int y2: y coordinate of the second corner of the trigon + :param int x3: x coordinate of the third corner of the trigon + :param int y3: y coordinate of the third corner of the trigon + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or tuple(int, int, int, [int]) + + :returns: ``None`` + :rtype: NoneType + + .. ## pygame.gfxdraw.filled_trigon ## + +.. function:: polygon + + | :sl:`draw a polygon` + | :sg:`polygon(surface, points, color) -> None` + + Draws an unfilled polygon on the given surface. For a filled polygon use + :meth:`filled_polygon`. + + The adjacent coordinates in the ``points`` argument, as well as the first + and last points, will be connected by line segments. + e.g. For the points ``[(x1, y1), (x2, y2), (x3, y3)]`` a line segment will + be drawn from ``(x1, y1)`` to ``(x2, y2)``, from ``(x2, y2)`` to + ``(x3, y3)``, and from ``(x3, y3)`` to ``(x1, y1)``. + + :param Surface surface: surface to draw on + :param points: a sequence of 3 or more (x, y) coordinates, where each + *coordinate* in the sequence must be a + tuple/list/:class:`pygame.math.Vector2` of 2 ints/floats (float values + will be truncated) + :type points: tuple(coordinate) or list(coordinate) + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or tuple(int, int, int, [int]) + + :returns: ``None`` + :rtype: NoneType + + :raises ValueError: if ``len(points) < 3`` (must have at least 3 points) + :raises IndexError: if ``len(coordinate) < 2`` (each coordinate must have + at least 2 items) + + .. ## pygame.gfxdraw.polygon ## + +.. function:: aapolygon + + | :sl:`draw an antialiased polygon` + | :sg:`aapolygon(surface, points, color) -> None` + + Draws an unfilled antialiased polygon on the given surface. + + The adjacent coordinates in the ``points`` argument, as well as the first + and last points, will be connected by line segments. + e.g. For the points ``[(x1, y1), (x2, y2), (x3, y3)]`` a line segment will + be drawn from ``(x1, y1)`` to ``(x2, y2)``, from ``(x2, y2)`` to + ``(x3, y3)``, and from ``(x3, y3)`` to ``(x1, y1)``. + + :param Surface surface: surface to draw on + :param points: a sequence of 3 or more (x, y) coordinates, where each + *coordinate* in the sequence must be a + tuple/list/:class:`pygame.math.Vector2` of 2 ints/floats (float values + will be truncated) + :type points: tuple(coordinate) or list(coordinate) + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or tuple(int, int, int, [int]) + + :returns: ``None`` + :rtype: NoneType + + :raises ValueError: if ``len(points) < 3`` (must have at least 3 points) + :raises IndexError: if ``len(coordinate) < 2`` (each coordinate must have + at least 2 items) + + .. ## pygame.gfxdraw.aapolygon ## + +.. function:: filled_polygon + + | :sl:`draw a filled polygon` + | :sg:`filled_polygon(surface, points, color) -> None` + + Draws a filled polygon on the given surface. For an unfilled polygon use + :meth:`polygon`. + + The adjacent coordinates in the ``points`` argument, as well as the first + and last points, will be connected by line segments. + e.g. For the points ``[(x1, y1), (x2, y2), (x3, y3)]`` a line segment will + be drawn from ``(x1, y1)`` to ``(x2, y2)``, from ``(x2, y2)`` to + ``(x3, y3)``, and from ``(x3, y3)`` to ``(x1, y1)``. + + :param Surface surface: surface to draw on + :param points: a sequence of 3 or more (x, y) coordinates, where each + *coordinate* in the sequence must be a + tuple/list/:class:`pygame.math.Vector2` of 2 ints/floats (float values + will be truncated)` + :type points: tuple(coordinate) or list(coordinate) + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or tuple(int, int, int, [int]) + + :returns: ``None`` + :rtype: NoneType + + :raises ValueError: if ``len(points) < 3`` (must have at least 3 points) + :raises IndexError: if ``len(coordinate) < 2`` (each coordinate must have + at least 2 items) + + .. ## pygame.gfxdraw.filled_polygon ## + +.. function:: textured_polygon + + | :sl:`draw a textured polygon` + | :sg:`textured_polygon(surface, points, texture, tx, ty) -> None` + + Draws a textured polygon on the given surface. For better performance, the + surface and the texture should have the same format. + + A per-pixel alpha texture blit to a per-pixel alpha surface will differ from + a :func:`pygame.Surface.blit` blit. Also, a per-pixel alpha texture cannot be + used with an 8-bit per pixel destination. + + The adjacent coordinates in the ``points`` argument, as well as the first + and last points, will be connected by line segments. + e.g. For the points ``[(x1, y1), (x2, y2), (x3, y3)]`` a line segment will + be drawn from ``(x1, y1)`` to ``(x2, y2)``, from ``(x2, y2)`` to + ``(x3, y3)``, and from ``(x3, y3)`` to ``(x1, y1)``. + + :param Surface surface: surface to draw on + :param points: a sequence of 3 or more (x, y) coordinates, where each + *coordinate* in the sequence must be a + tuple/list/:class:`pygame.math.Vector2` of 2 ints/floats (float values + will be truncated) + :type points: tuple(coordinate) or list(coordinate) + :param Surface texture: texture to draw on the polygon + :param int tx: x offset of the texture + :param int ty: y offset of the texture + + :returns: ``None`` + :rtype: NoneType + + :raises ValueError: if ``len(points) < 3`` (must have at least 3 points) + :raises IndexError: if ``len(coordinate) < 2`` (each coordinate must have + at least 2 items) + + .. ## pygame.gfxdraw.textured_polygon ## + +.. function:: bezier + + | :sl:`draw a Bezier curve` + | :sg:`bezier(surface, points, steps, color) -> None` + + Draws a Bézier curve on the given surface. + + :param Surface surface: surface to draw on + :param points: a sequence of 3 or more (x, y) coordinates used to form a + curve, where each *coordinate* in the sequence must be a + tuple/list/:class:`pygame.math.Vector2` of 2 ints/floats (float values + will be truncated) + :type points: tuple(coordinate) or list(coordinate) + :param int steps: number of steps for the interpolation, the minimum is 2 + :param color: color to draw with, the alpha value is optional if using a + tuple ``(RGB[A])`` + :type color: Color or tuple(int, int, int, [int]) + + :returns: ``None`` + :rtype: NoneType + + :raises ValueError: if ``steps < 2`` + :raises ValueError: if ``len(points) < 3`` (must have at least 3 points) + :raises IndexError: if ``len(coordinate) < 2`` (each coordinate must have + at least 2 items) + + .. ## pygame.gfxdraw.bezier ## + +.. ## pygame.gfxdraw ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/image.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/image.rst.txt new file mode 100644 index 00000000..ec2e4fc4 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/image.rst.txt @@ -0,0 +1,375 @@ +.. include:: common.txt + +:mod:`pygame.image` +=================== + +.. module:: pygame.image + :synopsis: pygame module for loading and saving images + +| :sl:`pygame module for image transfer` + +The image module contains functions for loading and saving pictures, as well as +transferring Surfaces to formats usable by other packages. + +Note that there is no Image class; an image is loaded as a Surface object. The +Surface class allows manipulation (drawing lines, setting pixels, capturing +regions, etc.). + +In the vast majority of installations, pygame is built to support extended +formats, using the SDL_Image library behind the scenes. However, some +installations may only support uncompressed ``BMP`` images. With full image +support, the :func:`pygame.image.load()` function can load the following +formats. + + * ``BMP`` + + * ``GIF`` (non-animated) + + * ``JPEG`` + + * ``LBM`` (and ``PBM``, ``PGM``, ``PPM``) + + * ``PCX`` + + * ``PNG`` + + * ``PNM`` + + * ``SVG`` (limited support, using Nano SVG) + + * ``TGA`` (uncompressed) + + * ``TIFF`` + + * ``WEBP`` + + * ``XPM`` + + +.. versionadded:: 2.0 Loading SVG, WebP, PNM + +Saving images only supports a limited set of formats. You can save to the +following formats. + + * ``BMP`` + + * ``JPEG`` + + * ``PNG`` + + * ``TGA`` + + +``JPEG`` and ``JPG``, as well as ``TIF`` and ``TIFF`` refer to the same file format + +.. versionadded:: 1.8 Saving PNG and JPEG files. + + +.. function:: load + + | :sl:`load new image from a file (or file-like object)` + | :sg:`load(filename) -> Surface` + | :sg:`load(fileobj, namehint="") -> Surface` + + Load an image from a file source. You can pass either a filename, a Python + file-like object, or a pathlib.Path. + + Pygame will automatically determine the image type (e.g., ``GIF`` or bitmap) + and create a new Surface object from the data. In some cases it will need to + know the file extension (e.g., ``GIF`` images should end in ".gif"). If you + pass a raw file-like object, you may also want to pass the original filename + as the namehint argument. + + The returned Surface will contain the same color format, colorkey and alpha + transparency as the file it came from. You will often want to call + :func:`pygame.Surface.convert()` with no arguments, to create a copy that + will draw more quickly on the screen. + + For alpha transparency, like in .png images, use the + :func:`pygame.Surface.convert_alpha()` method after loading so that the + image has per pixel transparency. + + Pygame may not always be built to support all image formats. At minimum it + will support uncompressed ``BMP``. If :func:`pygame.image.get_extended()` + returns ``True``, you should be able to load most images (including PNG, JPG + and GIF). + + You should use :func:`os.path.join()` for compatibility. + + :: + + eg. asurf = pygame.image.load(os.path.join('data', 'bla.png')) + + .. ## pygame.image.load ## + +.. function:: save + + | :sl:`save an image to file (or file-like object)` + | :sg:`save(Surface, filename) -> None` + | :sg:`save(Surface, fileobj, namehint="") -> None` + + This will save your Surface as either a ``BMP``, ``TGA``, ``PNG``, or + ``JPEG`` image. If the filename extension is unrecognized it will default to + ``TGA``. Both ``TGA``, and ``BMP`` file formats create uncompressed files. + You can pass a filename, a pathlib.Path or a Python file-like object. + For file-like object, the image is saved to ``TGA`` format unless + a namehint with a recognizable extension is passed in. + + .. note:: When saving to a file-like object, it seems that for most formats, + the object needs to be flushed after saving to it to make loading + from it possible. + + .. versionchanged:: 1.8 Saving PNG and JPEG files. + .. versionchanged:: 2.0.0 + The ``namehint`` parameter was added to make it possible + to save other formats than ``TGA`` to a file-like object. + Saving to a file-like object with JPEG is possible. + + .. ## pygame.image.save ## + +.. function:: get_sdl_image_version + + | :sl:`get version number of the SDL_Image library being used` + | :sg:`get_sdl_image_version(linked=True) -> None` + | :sg:`get_sdl_image_version(linked=True) -> (major, minor, patch)` + + If pygame is built with extended image formats, then this function will + return the SDL_Image library's version number as a tuple of 3 integers + ``(major, minor, patch)``. If not, then it will return ``None``. + + ``linked=True`` is the default behavior and the function will return the + version of the library that Pygame is linked against, while ``linked=False`` + will return the version of the library that Pygame is compiled against. + + .. versionadded:: 2.0.0 + + .. versionchanged:: 2.2.0 ``linked`` keyword argument added and default behavior changed from returning compiled version to returning linked version + + .. ## pygame.image.get_sdl_image_version ## + +.. function:: get_extended + + | :sl:`test if extended image formats can be loaded` + | :sg:`get_extended() -> bool` + + If pygame is built with extended image formats this function will return + True. It is still not possible to determine which formats will be available, + but generally you will be able to load them all. + + .. ## pygame.image.get_extended ## + +.. function:: tostring + + | :sl:`transfer image to byte buffer` + | :sg:`tostring(Surface, format, flipped=False) -> bytes` + + Creates a string of bytes that can be transferred with the ``fromstring`` + or ``frombytes`` methods in other Python imaging packages. Some Python + image packages prefer their images in bottom-to-top format (PyOpenGL for + example). If you pass ``True`` for the flipped argument, the byte buffer + will be vertically flipped. + + The format argument is a string of one of the following values. Note that + only 8-bit Surfaces can use the "P" format. The other formats will work for + any Surface. Also note that other Python image packages support more formats + than pygame. + + * ``P``, 8-bit palettized Surfaces + + * ``RGB``, 24-bit image + + * ``RGBX``, 32-bit image with unused space + + * ``RGBA``, 32-bit image with an alpha channel + + * ``ARGB``, 32-bit image with alpha channel first + + * ``BGRA``, 32-bit image with alpha channel, red and blue channels swapped + + * ``RGBA_PREMULT``, 32-bit image with colors scaled by alpha channel + + * ``ARGB_PREMULT``, 32-bit image with colors scaled by alpha channel, alpha channel first + + .. note:: it is preferred to use :func:`tobytes` as of pygame 2.1.3 + + .. versionadded:: 2.1.3 BGRA format + .. ## pygame.image.tostring ## + +.. function:: tobytes + + | :sl:`transfer image to byte buffer` + | :sg:`tobytes(Surface, format, flipped=False) -> bytes` + + Creates a string of bytes that can be transferred with the ``fromstring`` + or ``frombytes`` methods in other Python imaging packages. Some Python + image packages prefer their images in bottom-to-top format (PyOpenGL for + example). If you pass ``True`` for the flipped argument, the byte buffer + will be vertically flipped. + + The format argument is a string of one of the following values. Note that + only 8-bit Surfaces can use the "P" format. The other formats will work for + any Surface. Also note that other Python image packages support more formats + than pygame. + + * ``P``, 8-bit palettized Surfaces + + * ``RGB``, 24-bit image + + * ``RGBX``, 32-bit image with unused space + + * ``RGBA``, 32-bit image with an alpha channel + + * ``ARGB``, 32-bit image with alpha channel first + + * ``BGRA``, 32-bit image with alpha channel, red and blue channels swapped + + * ``RGBA_PREMULT``, 32-bit image with colors scaled by alpha channel + + * ``ARGB_PREMULT``, 32-bit image with colors scaled by alpha channel, alpha channel first + + .. note:: this function is an alias for :func:`tostring`. The use of this + function is recommended over :func:`tostring` as of pygame 2.1.3. + This function was introduced so it matches nicely with other + libraries (PIL, numpy, etc), and with people's expectations. + + .. versionadded:: 2.1.3 + + .. ## pygame.image.tobytes ## + + +.. function:: fromstring + + | :sl:`create new Surface from a byte buffer` + | :sg:`fromstring(bytes, size, format, flipped=False) -> Surface` + + This function takes arguments similar to :func:`pygame.image.tostring()`. + The size argument is a pair of numbers representing the width and height. + Once the new Surface is created it is independent from the memory of the + bytes passed in. + + The bytes and format passed must compute to the exact size of image + specified. Otherwise a ``ValueError`` will be raised. + + See the :func:`pygame.image.frombuffer()` method for a potentially faster + way to transfer images into pygame. + + .. note:: it is preferred to use :func:`frombytes` as of pygame 2.1.3 + + .. ## pygame.image.fromstring ## + +.. function:: frombytes + + | :sl:`create new Surface from a byte buffer` + | :sg:`frombytes(bytes, size, format, flipped=False) -> Surface` + + This function takes arguments similar to :func:`pygame.image.tobytes()`. + The size argument is a pair of numbers representing the width and height. + Once the new Surface is created it is independent from the memory of the + bytes passed in. + + The bytes and format passed must compute to the exact size of image + specified. Otherwise a ``ValueError`` will be raised. + + See the :func:`pygame.image.frombuffer()` method for a potentially faster + way to transfer images into pygame. + + .. note:: this function is an alias for :func:`fromstring`. The use of this + function is recommended over :func:`fromstring` as of pygame 2.1.3. + This function was introduced so it matches nicely with other + libraries (PIL, numpy, etc), and with people's expectations. + + .. versionadded:: 2.1.3 + + .. ## pygame.image.frombytes ## + +.. function:: frombuffer + + | :sl:`create a new Surface that shares data inside a bytes buffer` + | :sg:`frombuffer(buffer, size, format) -> Surface` + + Create a new Surface that shares pixel data directly from a buffer. This + buffer can be bytes, a bytearray, a memoryview, a + :class:`pygame.BufferProxy`, or any object that supports the buffer protocol. + This method takes similar arguments to :func:`pygame.image.fromstring()`, but + is unable to vertically flip the source data. + + This will run much faster than :func:`pygame.image.fromstring`, since no + pixel data must be allocated and copied. + + It accepts the following 'format' arguments: + + * ``P``, 8-bit palettized Surfaces + + * ``RGB``, 24-bit image + + * ``BGR``, 24-bit image, red and blue channels swapped. + + * ``RGBX``, 32-bit image with unused space + + * ``RGBA``, 32-bit image with an alpha channel + + * ``ARGB``, 32-bit image with alpha channel first + + * ``BGRA``, 32-bit image with alpha channel, red and blue channels swapped + + .. versionadded:: 2.1.3 BGRA format + .. ## pygame.image.frombuffer ## + +.. function:: load_basic + + | :sl:`load new BMP image from a file (or file-like object)` + | :sg:`load_basic(file) -> Surface` + + Load an image from a file source. You can pass either a filename or a Python + file-like object, or a pathlib.Path. + + This function only supports loading "basic" image format, ie ``BMP`` + format. + This function is always available, no matter how pygame was built. + + .. ## pygame.image.load_basic ## + +.. function:: load_extended + + | :sl:`load an image from a file (or file-like object)` + | :sg:`load_extended(filename) -> Surface` + | :sg:`load_extended(fileobj, namehint="") -> Surface` + + This function is similar to :func:`pygame.image.load()`, except that this + function can only be used if pygame was built with extended image format + support. + + .. versionchanged:: 2.0.1 + This function is always available, but raises an + ``NotImplementedError`` if extended image formats are + not supported. + Previously, this function may or may not be + available, depending on the state of extended image + format support. + + .. ## pygame.image.load_extended ## + +.. function:: save_extended + + | :sl:`save a png/jpg image to file (or file-like object)` + | :sg:`save_extended(Surface, filename) -> None` + | :sg:`save_extended(Surface, fileobj, namehint="") -> None` + + This will save your Surface as either a ``PNG`` or ``JPEG`` image. + + In case the image is being saved to a file-like object, this function + uses the namehint argument to determine the format of the file being + saved. Saves to ``JPEG`` in case the namehint was not specified while + saving to a file-like object. + + .. versionchanged:: 2.0.1 + This function is always available, but raises an + ``NotImplementedError`` if extended image formats are + not supported. + Previously, this function may or may not be + available, depending on the state of extended image + format support. + + .. ## pygame.image.save_extended ## + +.. ## pygame.image ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/joystick.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/joystick.rst.txt new file mode 100644 index 00000000..dba5b20d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/joystick.rst.txt @@ -0,0 +1,697 @@ +.. include:: common.txt + +:mod:`pygame.joystick` +====================== + +.. module:: pygame.joystick + :synopsis: Pygame module for interacting with joysticks, gamepads, and trackballs. + +| :sl:`Pygame module for interacting with joysticks, gamepads, and trackballs.` + +The joystick module manages the joystick devices on a computer. +Joystick devices include trackballs and video-game-style +gamepads, and the module allows the use of multiple buttons and "hats". +Computers may manage multiple joysticks at a time. + +Each instance of the Joystick class represents one gaming device plugged +into the computer. If a gaming pad has multiple joysticks on it, then the +joystick object can actually represent multiple joysticks on that single +game device. + +For a quick way to initialise the joystick module and get a list of Joystick instances +use the following code:: + + pygame.joystick.init() + joysticks = [pygame.joystick.Joystick(x) for x in range(pygame.joystick.get_count())] + +The following event types will be generated by the joysticks :: + + JOYAXISMOTION JOYBALLMOTION JOYBUTTONDOWN JOYBUTTONUP JOYHATMOTION + +And in pygame 2, which supports hotplugging:: + + JOYDEVICEADDED JOYDEVICEREMOVED + +Note that in pygame 2, joysticks events use a unique "instance ID". The device index +passed in the constructor to a Joystick object is not unique after devices have +been added and removed. You must call :meth:`Joystick.get_instance_id()` to find +the instance ID that was assigned to a Joystick on opening. + +The event queue needs to be pumped frequently for some of the methods to work. +So call one of pygame.event.get, pygame.event.wait, or pygame.event.pump regularly. + +To be able to get joystick events and update the joystick objects while the window +is not in focus, you may set the ``SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS`` environment +variable. See :ref:`environment variables ` for more details. + + +.. function:: init + + | :sl:`Initialize the joystick module.` + | :sg:`init() -> None` + + This function is called automatically by ``pygame.init()``. + + It initializes the joystick module. The module must be initialized before any + other functions will work. + + It is safe to call this function more than once. + + .. ## pygame.joystick.init ## + +.. function:: quit + + | :sl:`Uninitialize the joystick module.` + | :sg:`quit() -> None` + + Uninitialize the joystick module. After you call this any existing joystick + objects will no longer work. + + It is safe to call this function more than once. + + .. ## pygame.joystick.quit ## + +.. function:: get_init + + | :sl:`Returns True if the joystick module is initialized.` + | :sg:`get_init() -> bool` + + Test if the ``pygame.joystick.init()`` function has been called. + + .. ## pygame.joystick.get_init ## + +.. function:: get_count + + | :sl:`Returns the number of joysticks.` + | :sg:`get_count() -> count` + + Return the number of joystick devices on the system. The count will be ``0`` + if there are no joysticks on the system. + + When you create Joystick objects using ``Joystick(id)``, you pass an integer + that must be lower than this count. + + .. ## pygame.joystick.get_count ## + +.. class:: Joystick + + | :sl:`Create a new Joystick object.` + | :sg:`Joystick(id) -> Joystick` + + Create a new joystick to access a physical device. The id argument must be a + value from ``0`` to ``pygame.joystick.get_count() - 1``. + + Joysticks are initialised on creation and are shut down when deallocated. + Once the device is initialized the pygame event queue will start receiving + events about its input. + + .. versionchanged:: 2.0.0 Joystick objects are now opened immediately on creation. + + .. method:: init + + | :sl:`initialize the Joystick` + | :sg:`init() -> None` + + Initialize the joystick, if it has been closed. It is safe to call this + even if the joystick is already initialized. + + .. deprecated:: 2.0.0 + + In future it will not be possible to reinitialise a closed Joystick + object. Will be removed in Pygame 2.1. + + .. ## Joystick.init ## + + .. method:: quit + + | :sl:`uninitialize the Joystick` + | :sg:`quit() -> None` + + Close a Joystick object. After this the pygame event queue will no longer + receive events from the device. + + It is safe to call this more than once. + + .. ## Joystick.quit ## + + .. method:: get_init + + | :sl:`check if the Joystick is initialized` + | :sg:`get_init() -> bool` + + Return True if the Joystick object is currently initialised. + + .. ## Joystick.get_init ## + + .. method:: get_id + + | :sl:`get the device index (deprecated)` + | :sg:`get_id() -> int` + + Returns the original device index for this device. This is the same + value that was passed to the ``Joystick()`` constructor. This method can + safely be called while the Joystick is not initialized. + + .. deprecated:: 2.0.0 + + The original device index is not useful in pygame 2. Use + :meth:`.get_instance_id` instead. Will be removed in Pygame 2.1. + + .. method:: get_instance_id() -> int + + | :sl:`get the joystick instance id` + | :sg:`get_instance_id() -> int` + + Get the joystick instance ID. This matches the ``instance_id`` field + that is given in joystick events. + + .. versionadded:: 2.0.0dev11 + + .. method:: get_guid() -> str + + | :sl:`get the joystick GUID` + | :sg:`get_guid() -> str` + + Get the GUID string. This identifies the exact hardware of the joystick + device. + + .. versionadded:: 2.0.0dev11 + + .. method:: get_power_level() -> str + + | :sl:`get the approximate power status of the device` + | :sg:`get_power_level() -> str` + + Get a string giving the power status of the device. + + One of: ``empty``, ``low``, ``medium``, ``full``, ``wired``, ``max``, or + ``unknown``. + + .. versionadded:: 2.0.0dev11 + + .. ## Joystick.get_id ## + + .. method:: get_name + + | :sl:`get the Joystick system name` + | :sg:`get_name() -> string` + + Returns the system name for this joystick device. It is unknown what name + the system will give to the Joystick, but it should be a unique name that + identifies the device. This method can safely be called while the + Joystick is not initialized. + + .. ## Joystick.get_name ## + + .. method:: get_numaxes + + | :sl:`get the number of axes on a Joystick` + | :sg:`get_numaxes() -> int` + + Returns the number of input axes are on a Joystick. There will usually be + two for the position. Controls like rudders and throttles are treated as + additional axes. + + The ``pygame.JOYAXISMOTION`` events will be in the range from ``-1.0`` + to ``1.0``. A value of ``0.0`` means the axis is centered. Gamepad devices + will usually be ``-1``, ``0``, or ``1`` with no values in between. Older + analog joystick axes will not always use the full ``-1`` to ``1`` range, + and the centered value will be some area around ``0``. + + Analog joysticks usually have a bit of noise in their axis, which will + generate a lot of rapid small motion events. + + .. ## Joystick.get_numaxes ## + + .. method:: get_axis + + | :sl:`get the current position of an axis` + | :sg:`get_axis(axis_number) -> float` + + Returns the current position of a joystick axis. The value will range + from ``-1`` to ``1`` with a value of ``0`` being centered. You may want + to take into account some tolerance to handle jitter, and joystick drift + may keep the joystick from centering at ``0`` or using the full range of + position values. + + The axis number must be an integer from ``0`` to ``get_numaxes() - 1``. + + When using gamepads both the control sticks and the analog triggers are + usually reported as axes. + + .. ## Joystick.get_axis ## + + .. method:: get_numballs + + | :sl:`get the number of trackballs on a Joystick` + | :sg:`get_numballs() -> int` + + Returns the number of trackball devices on a Joystick. These devices work + similar to a mouse but they have no absolute position; they only have + relative amounts of movement. + + The ``pygame.JOYBALLMOTION`` event will be sent when the trackball is + rolled. It will report the amount of movement on the trackball. + + .. ## Joystick.get_numballs ## + + .. method:: get_ball + + | :sl:`get the relative position of a trackball` + | :sg:`get_ball(ball_number) -> x, y` + + Returns the relative movement of a joystick button. The value is a ``x, y`` + pair holding the relative movement since the last call to get_ball. + + The ball number must be an integer from ``0`` to ``get_numballs() - 1``. + + .. ## Joystick.get_ball ## + + .. method:: get_numbuttons + + | :sl:`get the number of buttons on a Joystick` + | :sg:`get_numbuttons() -> int` + + Returns the number of pushable buttons on the joystick. These buttons + have a boolean (on or off) state. + + Buttons generate a ``pygame.JOYBUTTONDOWN`` and ``pygame.JOYBUTTONUP`` + event when they are pressed and released. + + .. ## Joystick.get_numbuttons ## + + .. method:: get_button + + | :sl:`get the current button state` + | :sg:`get_button(button) -> bool` + + Returns the current state of a joystick button. + + .. ## Joystick.get_button ## + + .. method:: get_numhats + + | :sl:`get the number of hat controls on a Joystick` + | :sg:`get_numhats() -> int` + + Returns the number of joystick hats on a Joystick. Hat devices are like + miniature digital joysticks on a joystick. Each hat has two axes of + input. + + The ``pygame.JOYHATMOTION`` event is generated when the hat changes + position. The ``position`` attribute for the event contains a pair of + values that are either ``-1``, ``0``, or ``1``. A position of ``(0, 0)`` + means the hat is centered. + + .. ## Joystick.get_numhats ## + + .. method:: get_hat + + | :sl:`get the position of a joystick hat` + | :sg:`get_hat(hat_number) -> x, y` + + Returns the current position of a position hat. The position is given as + two values representing the ``x`` and ``y`` position for the hat. ``(0, 0)`` + means centered. A value of ``-1`` means left/down and a value of ``1`` means + right/up: so ``(-1, 0)`` means left; ``(1, 0)`` means right; ``(0, 1)`` means + up; ``(1, 1)`` means upper-right; etc. + + This value is digital, ``i.e.``, each coordinate can be ``-1``, ``0`` or ``1`` + but never in-between. + + The hat number must be between ``0`` and ``get_numhats() - 1``. + + .. ## Joystick.get_hat ## + + .. method:: rumble + + | :sl:`Start a rumbling effect` + | :sg:`rumble(low_frequency, high_frequency, duration) -> bool` + + Start a rumble effect on the joystick, with the specified strength ranging + from 0 to 1. Duration is length of the effect, in ms. Setting the duration + to 0 will play the effect until another one overwrites it or + :meth:`Joystick.stop_rumble` is called. If an effect is already + playing, then it will be overwritten. + + Returns True if the rumble was played successfully or False if the + joystick does not support it or :meth:`pygame.version.SDL` is below 2.0.9. + + .. versionadded:: 2.0.2 + + .. ## Joystick.rumble ## + + .. method:: stop_rumble + + | :sl:`Stop any rumble effect playing` + | :sg:`stop_rumble() -> None` + + Stops any rumble effect playing on the joystick. See + :meth:`Joystick.rumble` for more information. + + .. versionadded:: 2.0.2 + + .. ## Joystick.stop_rumble ## + + .. ## pygame.joystick.Joystick ## + +.. ## pygame.joystick ## + +.. figure:: code_examples/joystick_calls.png + :scale: 100 % + :alt: joystick module example + + Example code for joystick module. + +.. literalinclude:: ../../../examples/joystick.py + +.. _controller-mappings: + + +Common Controller Axis Mappings +=============================== + +Controller mappings are drawn from the underlying SDL library which pygame uses and they differ +between pygame 1 and pygame 2. Below are a couple of mappings for three popular controllers. + +Axis and hat mappings are listed from -1 to +1. + + +Nintendo Switch Left Joy-Con (pygame 2.x) +***************************************** + +The Nintendo Switch Left Joy-Con has 4 axes, 11 buttons, and 0 hats. The values for the 4 axes never change. +The controller is recognized as "Wireless Gamepad" + + +* **Buttons**:: + + D-pad Up - Button 0 + D-pad Down - Button 1 + D-pad Left - Button 2 + D-pad Right - Button 3 + SL - Button 4 + SR - Button 5 + - - Button 8 + Stick In - Button 10 + Capture - Button 13 + L - Button 14 + ZL - Button 15 + +* **Hat/JoyStick**:: + + Down -> Up - Y Axis + Left -> Right - X Axis + + +Nintendo Switch Right Joy-Con (pygame 2.x) +****************************************** + +The Nintendo Switch Right Joy-Con has 4 axes, 11 buttons, and 0 hats. The values for the 4 axes never change. +The controller is recognized as "Wireless Gamepad" + + +* **Buttons**:: + + A Button - Button 0 + B Button - Button 1 + X Button - Button 2 + Y Button - Button 3 + SL - Button 4 + SR - Button 5 + + - Button 9 + Stick In - Button 11 + Home - Button 12 + R - Button 14 + ZR - Button 15 + +* **Hat/JoyStick**:: + + Down -> Up - Y Axis + Left -> Right - X Axis + + +Nintendo Switch Pro Controller (pygame 2.x) +******************************************* + +The Nintendo Switch Pro Controller has 6 axes, 16 buttons, and 0 hats. +The controller is recognized as "Nintendo Switch Pro Controller". + + +* **Left Stick**:: + + Left -> Right - Axis 0 + Up -> Down - Axis 1 + +* **Right Stick**:: + + Left -> Right - Axis 2 + Up -> Down - Axis 3 + +* **Left Trigger**:: + + Out -> In - Axis 4 + +* **Right Trigger**:: + + Out -> In - Axis 5 + +* **Buttons**:: + + A Button - Button 0 + B Button - Button 1 + X Button - Button 2 + Y Button - Button 3 + - Button - Button 4 + Home Button - Button 5 + + Button - Button 6 + L. Stick In - Button 7 + R. Stick In - Button 8 + Left Bumper - Button 9 + Right Bumper - Button 10 + D-pad Up - Button 11 + D-pad Down - Button 12 + D-pad Left - Button 13 + D-pad Right - Button 14 + Capture Button - Button 15 + + +XBox 360 Controller (pygame 2.x) +******************************** + +The Xbox 360 controller mapping has 6 axes, 11 buttons and 1 hat. +The controller is recognized as "Xbox 360 Controller". + +* **Left Stick**:: + + Left -> Right - Axis 0 + Up -> Down - Axis 1 + +* **Right Stick**:: + + Left -> Right - Axis 3 + Up -> Down - Axis 4 + +* **Left Trigger**:: + + Out -> In - Axis 2 + +* **Right Trigger**:: + + Out -> In - Axis 5 + +* **Buttons**:: + + A Button - Button 0 + B Button - Button 1 + X Button - Button 2 + Y Button - Button 3 + Left Bumper - Button 4 + Right Bumper - Button 5 + Back Button - Button 6 + Start Button - Button 7 + L. Stick In - Button 8 + R. Stick In - Button 9 + Guide Button - Button 10 + +* **Hat/D-pad**:: + + Down -> Up - Y Axis + Left -> Right - X Axis + + +Playstation 4 Controller (pygame 2.x) +************************************* + +The PlayStation 4 controller mapping has 6 axes and 16 buttons. +The controller is recognized as "PS4 Controller". + +* **Left Stick**:: + + Left -> Right - Axis 0 + Up -> Down - Axis 1 + +* **Right Stick**:: + + Left -> Right - Axis 2 + Up -> Down - Axis 3 + +* **Left Trigger**:: + + Out -> In - Axis 4 + +* **Right Trigger**:: + + Out -> In - Axis 5 + +* **Buttons**:: + + Cross Button - Button 0 + Circle Button - Button 1 + Square Button - Button 2 + Triangle Button - Button 3 + Share Button - Button 4 + PS Button - Button 5 + Options Button - Button 6 + L. Stick In - Button 7 + R. Stick In - Button 8 + Left Bumper - Button 9 + Right Bumper - Button 10 + D-pad Up - Button 11 + D-pad Down - Button 12 + D-pad Left - Button 13 + D-pad Right - Button 14 + Touch Pad Click - Button 15 + +Playstation 5 Controller (pygame 2.x) +************************************* + +The PlayStation 5 controller mapping has 6 axes, 13 buttons, and 1 hat. +The controller is recognized as "Sony Interactive Entertainment Wireless Controller". + +* **Left Stick**:: + + Left -> Right - Axis 0 + Up -> Down - Axis 1 + +* **Right Stick**:: + + Left -> Right - Axis 3 + Up -> Down - Axis 4 + +* **Left Trigger**:: + + Out -> In - Axis 2 + +* **Right Trigger**:: + + Out -> In - Axis 5 + +* **Buttons**:: + + Cross Button - Button 0 + Circle Button - Button 1 + Square Button - Button 2 + Triangle Button - Button 3 + Left Bumper - Button 4 + Right Bumper - Button 5 + Left Trigger - Button 6 + Right Trigger - Button 7 + Share Button - Button 8 + Options Button - Button 9 + PS Button - Button 10 + Left Stick in - Button 11 + Right Stick in - Button 12 + +* **Hat/D-pad**:: + + Down -> Up - Y Axis + Left -> Right - X Axis + + + +XBox 360 Controller (pygame 1.x) +******************************** + +The Xbox 360 controller mapping has 5 axes, 10 buttons, and 1 hat. +The controller is recognized as "Controller (XBOX 360 For Windows)". + +* **Left Stick**:: + + Left -> Right - Axis 0 + Up -> Down - Axis 1 + +* **Right Stick**:: + + Left -> Right - Axis 4 + Up -> Down - Axis 3 + +* **Left Trigger & Right Trigger**:: + + RT -> LT - Axis 2 + +* **Buttons**:: + + A Button - Button 0 + B Button - Button 1 + X Button - Button 2 + Y Button - Button 3 + Left Bumper - Button 4 + Right Bumper - Button 5 + Back Button - Button 6 + Start Button - Button 7 + L. Stick In - Button 8 + R. Stick In - Button 9 + +* **Hat/D-pad**:: + + Down -> Up - Y Axis + Left -> Right - X Axis + + +Playstation 4 Controller (pygame 1.x) +************************************* + +The PlayStation 4 controller mapping has 6 axes, 14 buttons, and 1 hat. +The controller is recognized as "Wireless Controller". + +* **Left Stick**:: + + Left -> Right - Axis 0 + Up -> Down - Axis 1 + +* **Right Stick**:: + + Left -> Right - Axis 2 + Up -> Down - Axis 3 + +* **Left Trigger**:: + + Out -> In - Axis 5 + +* **Right Trigger**:: + + Out -> In - Axis 4 + +* **Buttons**:: + + Cross Button - Button 0 + Circle Button - Button 1 + Square Button - Button 2 + Triangle Button - Button 3 + Left Bumper - Button 4 + Right Bumper - Button 5 + L. Trigger(Full)- Button 6 + R. Trigger(Full)- Button 7 + Share Button - Button 8 + Options Button - Button 9 + L. Stick In - Button 10 + R. Stick In - Button 11 + PS Button - Button 12 + Touch Pad Click - Button 13 + +* **Hat/D-pad**:: + + Down -> Up - Y Axis + Left -> Right - X Axis + diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/key.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/key.rst.txt new file mode 100644 index 00000000..134f2549 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/key.rst.txt @@ -0,0 +1,455 @@ +.. include:: common.txt + +:mod:`pygame.key` +================= + +.. module:: pygame.key + :synopsis: pygame module to work with the keyboard + +| :sl:`pygame module to work with the keyboard` + +This module contains functions for dealing with the keyboard. + +The :mod:`pygame.event` queue gets ``pygame.KEYDOWN`` and ``pygame.KEYUP`` +events when the keyboard buttons are pressed and released. Both events have +``key`` and ``mod`` attributes. + + * ``key``: an :ref:`integer ID ` representing every key + on the keyboard + * ``mod``: a bitmask of all the :ref:`modifier keys ` + that were in a pressed state when the event occurred + +The ``pygame.KEYDOWN`` event has the additional attributes ``unicode`` and +``scancode``. + + * ``unicode``: a single character string that is the fully translated + character entered, this takes into account the shift and composition keys + * ``scancode``: the platform-specific key code, which could be different from + keyboard to keyboard, but is useful for key selection of weird keys like + the multimedia keys + +.. versionadded:: 2.0.0 + The ``pygame.TEXTINPUT`` event is preferred to the ``unicode`` attribute + of ``pygame.KEYDOWN``. The attribute ``text`` contains the input. + + +.. _key-constants-label: + +The following is a list of all the constants (from :mod:`pygame.locals`) used to +represent keyboard keys. + +Portability note: The integers for key constants differ between pygame 1 and 2. +Always use key constants (``K_a``) rather than integers directly (``97``) so +that your key handling code works well on both pygame 1 and pygame 2. + + +:: + + pygame + Constant ASCII Description + --------------------------------- + K_BACKSPACE \b backspace + K_TAB \t tab + K_CLEAR clear + K_RETURN \r return + K_PAUSE pause + K_ESCAPE ^[ escape + K_SPACE space + K_EXCLAIM ! exclaim + K_QUOTEDBL " quotedbl + K_HASH # hash + K_DOLLAR $ dollar + K_AMPERSAND & ampersand + K_QUOTE quote + K_LEFTPAREN ( left parenthesis + K_RIGHTPAREN ) right parenthesis + K_ASTERISK * asterisk + K_PLUS + plus sign + K_COMMA , comma + K_MINUS - minus sign + K_PERIOD . period + K_SLASH / forward slash + K_0 0 0 + K_1 1 1 + K_2 2 2 + K_3 3 3 + K_4 4 4 + K_5 5 5 + K_6 6 6 + K_7 7 7 + K_8 8 8 + K_9 9 9 + K_COLON : colon + K_SEMICOLON ; semicolon + K_LESS < less-than sign + K_EQUALS = equals sign + K_GREATER > greater-than sign + K_QUESTION ? question mark + K_AT @ at + K_LEFTBRACKET [ left bracket + K_BACKSLASH \ backslash + K_RIGHTBRACKET ] right bracket + K_CARET ^ caret + K_UNDERSCORE _ underscore + K_BACKQUOTE ` grave + K_a a a + K_b b b + K_c c c + K_d d d + K_e e e + K_f f f + K_g g g + K_h h h + K_i i i + K_j j j + K_k k k + K_l l l + K_m m m + K_n n n + K_o o o + K_p p p + K_q q q + K_r r r + K_s s s + K_t t t + K_u u u + K_v v v + K_w w w + K_x x x + K_y y y + K_z z z + K_DELETE delete + K_KP0 keypad 0 + K_KP1 keypad 1 + K_KP2 keypad 2 + K_KP3 keypad 3 + K_KP4 keypad 4 + K_KP5 keypad 5 + K_KP6 keypad 6 + K_KP7 keypad 7 + K_KP8 keypad 8 + K_KP9 keypad 9 + K_KP_PERIOD . keypad period + K_KP_DIVIDE / keypad divide + K_KP_MULTIPLY * keypad multiply + K_KP_MINUS - keypad minus + K_KP_PLUS + keypad plus + K_KP_ENTER \r keypad enter + K_KP_EQUALS = keypad equals + K_UP up arrow + K_DOWN down arrow + K_RIGHT right arrow + K_LEFT left arrow + K_INSERT insert + K_HOME home + K_END end + K_PAGEUP page up + K_PAGEDOWN page down + K_F1 F1 + K_F2 F2 + K_F3 F3 + K_F4 F4 + K_F5 F5 + K_F6 F6 + K_F7 F7 + K_F8 F8 + K_F9 F9 + K_F10 F10 + K_F11 F11 + K_F12 F12 + K_F13 F13 + K_F14 F14 + K_F15 F15 + K_NUMLOCK numlock + K_CAPSLOCK capslock + K_SCROLLOCK scrollock + K_RSHIFT right shift + K_LSHIFT left shift + K_RCTRL right control + K_LCTRL left control + K_RALT right alt + K_LALT left alt + K_RMETA right meta + K_LMETA left meta + K_LSUPER left Windows key + K_RSUPER right Windows key + K_MODE mode shift + K_HELP help + K_PRINT print screen + K_SYSREQ sysrq + K_BREAK break + K_MENU menu + K_POWER power + K_EURO Euro + K_AC_BACK Android back button + + +.. _key-modifiers-label: + +The keyboard also has a list of modifier states (from :mod:`pygame.locals`) that +can be assembled by bitwise-ORing them together. + +:: + + pygame + Constant Description + ------------------------- + KMOD_NONE no modifier keys pressed + KMOD_LSHIFT left shift + KMOD_RSHIFT right shift + KMOD_SHIFT left shift or right shift or both + KMOD_LCTRL left control + KMOD_RCTRL right control + KMOD_CTRL left control or right control or both + KMOD_LALT left alt + KMOD_RALT right alt + KMOD_ALT left alt or right alt or both + KMOD_LMETA left meta + KMOD_RMETA right meta + KMOD_META left meta or right meta or both + KMOD_CAPS caps lock + KMOD_NUM num lock + KMOD_MODE AltGr + + +The modifier information is contained in the ``mod`` attribute of the +``pygame.KEYDOWN`` and ``pygame.KEYUP`` events. The ``mod`` attribute is a +bitmask of all the modifier keys that were in a pressed state when the event +occurred. The modifier information can be decoded using a bitwise AND (except +for ``KMOD_NONE``, which should be compared using equals ``==``). For example: + +:: + + for event in pygame.event.get(): + if event.type == pygame.KEYDOWN or event.type == pygame.KEYUP: + if event.mod == pygame.KMOD_NONE: + print('No modifier keys were in a pressed state when this ' + 'event occurred.') + else: + if event.mod & pygame.KMOD_LSHIFT: + print('Left shift was in a pressed state when this event ' + 'occurred.') + if event.mod & pygame.KMOD_RSHIFT: + print('Right shift was in a pressed state when this event ' + 'occurred.') + if event.mod & pygame.KMOD_SHIFT: + print('Left shift or right shift or both were in a ' + 'pressed state when this event occurred.') + + + +.. function:: get_focused + + | :sl:`true if the display is receiving keyboard input from the system` + | :sg:`get_focused() -> bool` + + Returns ``True`` when the display window has keyboard focus from the + system. If the display needs to ensure it does not lose keyboard focus, it + can use :func:`pygame.event.set_grab()` to grab all input. + + .. ## pygame.key.get_focused ## + +.. function:: get_pressed + + | :sl:`get the state of all keyboard buttons` + | :sg:`get_pressed() -> bools` + + Returns a sequence of boolean values representing the state of every key on + the keyboard. Use the key constant values to index the array. A ``True`` + value means that the button is pressed. + + .. note:: + Getting the list of pushed buttons with this function is not the proper + way to handle text entry from the user. There is no way to know the order + of keys pressed, and rapidly pushed keys can be completely unnoticed + between two calls to ``pygame.key.get_pressed()``. There is also no way to + translate these pushed keys into a fully translated character value. See + the ``pygame.KEYDOWN`` events on the :mod:`pygame.event` queue for this + functionality. + + .. versionadded:: 2.2.0 + The collection of bools returned by ``get_pressed`` can not be iterated + over because the indexes of the internal tuple does not correpsond to the + keycodes. + + .. versionadded:: 2.5.0 + Iteration over the collection of bools returned by ``get_pressed`` is now + restored. However it still does not make sense to iterate over it. Currently. + + .. ## pygame.key.get_pressed ## + +.. function:: get_mods + + | :sl:`determine which modifier keys are being held` + | :sg:`get_mods() -> int` + + Returns a single integer representing a bitmask of all the modifier keys + being held. Using bitwise operators you can test if specific + :ref:`modifier keys ` are pressed. + + .. ## pygame.key.get_mods ## + +.. function:: set_mods + + | :sl:`temporarily set which modifier keys are pressed` + | :sg:`set_mods(int) -> None` + + Create a bitmask of the :ref:`modifier key constants ` + you want to impose on your program. + + .. ## pygame.key.set_mods ## + +.. function:: set_repeat + + | :sl:`control how held keys are repeated` + | :sg:`set_repeat() -> None` + | :sg:`set_repeat(delay) -> None` + | :sg:`set_repeat(delay, interval) -> None` + + When the keyboard repeat is enabled, keys that are held down will generate + multiple ``pygame.KEYDOWN`` events. The ``delay`` parameter is the number of + milliseconds before the first repeated ``pygame.KEYDOWN`` event will be sent. + After that, another ``pygame.KEYDOWN`` event will be sent every ``interval`` + milliseconds. If a ``delay`` value is provided and an ``interval`` value is + not provided or is 0, then the ``interval`` will be set to the same value as + ``delay``. + + To disable key repeat call this function with no arguments or with ``delay`` + set to 0. + + When pygame is initialized the key repeat is disabled. + + :raises ValueError: if ``delay`` or ``interval`` is < 0 + + .. versionchanged:: 2.0.0 A ``ValueError`` is now raised (instead of a + ``pygame.error``) if ``delay`` or ``interval`` is < 0. + + .. ## pygame.key.set_repeat ## + +.. function:: get_repeat + + | :sl:`see how held keys are repeated` + | :sg:`get_repeat() -> (delay, interval)` + + Get the ``delay`` and ``interval`` keyboard repeat values. Refer to + :func:`pygame.key.set_repeat()` for a description of these values. + + .. versionadded:: 1.8 + + .. ## pygame.key.get_repeat ## + +.. function:: name + + | :sl:`get the name of a key identifier` + | :sg:`name(key, use_compat=True) -> str` + + Get the descriptive name of the button from a keyboard button id constant. + Returns an empty string (``""``) if the key is not found. + + If ``use_compat`` argument is ``True`` (which is the default), this function + returns the legacy name of a key where applicable. The return value is + expected to be the same across different pygame versions (provided the + corresponding key constant exists and is unique). If the return value is + passed to the ``key_code`` function, the original constant will be returned. + + **Experimental:** ``use_compat`` paramater still in development for testing and feedback. It may change. + `Please leave use_compat feedback with authors `_ + + If this argument is ``False``, the returned name may be prettier to display + and may cover a wider range of keys than with ``use_compat``, but there are + no guarantees that this name will be the same across different pygame + versions. If the name returned is passed to the ``key_code`` function, the + original constant is returned back (this is an implementation detail which + may change later, do not rely on this) + + .. versionchanged:: 2.1.3 Added ``use_compat`` argument and guaranteed API stability for it + + .. ## pygame.key.name ## + +.. function:: key_code + + | :sl:`get the key identifier from a key name` + | :sg:`key_code(name=string) -> int` + + Get the key identifier code from the descriptive name of the key. This + returns an integer matching one of the K_* keycodes. For example: + + :: + + >>> pygame.key.key_code("return") == pygame.K_RETURN + True + >>> pygame.key.key_code("0") == pygame.K_0 + True + >>> pygame.key.key_code("space") == pygame.K_SPACE + True + + :raises ValueError: if the key name is not known. + + .. versionadded:: 2.0.0 + + .. ## pygame.key.key_code ## + +.. function:: start_text_input + + | :sl:`start handling Unicode text input events` + | :sg:`start_text_input() -> None` + + Start receiving ``pygame.TEXTEDITING`` and ``pygame.TEXTINPUT`` + events. If applicable, show the on-screen keyboard or IME editor. + + For many languages, key presses will automatically generate a + corresponding ``pygame.TEXTINPUT`` event. Special keys like + escape or function keys, and certain key combinations will not + generate ``pygame.TEXTINPUT`` events. + + In other languages, entering a single symbol may require multiple + key presses, or a language-specific user interface. In this case, + ``pygame.TEXTINPUT`` events are preferable to ``pygame.KEYDOWN`` + events for text input. + + A ``pygame.TEXTEDITING`` event is received when an IME composition + is started or changed. It contains the composition ``text``, ``length``, + and editing ``start`` position within the composition (attributes + ``text``, ``length``, and ``start``, respectively). + When the composition is committed (or non-IME input is received), + a ``pygame.TEXTINPUT`` event is generated. + + Text input events handling is on by default. + + .. versionadded:: 2.0.0 + + .. ## pygame.key.start_text_input ## + +.. function:: stop_text_input + + | :sl:`stop handling Unicode text input events` + | :sg:`stop_text_input() -> None` + + Stop receiving ``pygame.TEXTEDITING`` and ``pygame.TEXTINPUT`` + events. If an on-screen keyboard or IME editor was shown with + ``pygame.key.start_text_input()``, hide it again. + + Text input events handling is on by default. + + To avoid triggering the IME editor or the on-screen keyboard + when the user is holding down a key during gameplay, text input + should be disabled once text entry is finished, or when the user + clicks outside of a text box. + + .. versionadded:: 2.0.0 + + .. ## pygame.key.stop_text_input ## + +.. function:: set_text_input_rect + + | :sl:`controls the position of the candidate list` + | :sg:`set_text_input_rect(Rect) -> None` + + This sets the rectangle used for typing with an IME. + It controls where the candidate list will open, if supported. + + .. versionadded:: 2.0.0 + + .. ## pygame.key.set_text_input_rect ## + +.. ## pygame.key ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/locals.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/locals.rst.txt new file mode 100644 index 00000000..091dbaab --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/locals.rst.txt @@ -0,0 +1,27 @@ +.. include:: common.txt + +:mod:`pygame.locals` +==================== + +.. module:: pygame.locals + :synopsis: pygame constants + +| :sl:`pygame constants` + +This module contains various constants used by pygame. Its contents are +automatically placed in the pygame module namespace. However, an application +can use ``pygame.locals`` to include only the pygame constants with a ``from +pygame.locals import *``. + +Detailed descriptions of the various constants can be found throughout the +pygame documentation. Here are the locations of some of them. + + - The :mod:`pygame.display` module contains flags like ``FULLSCREEN`` used + by :func:`pygame.display.set_mode`. + - The :mod:`pygame.event` module contains the various event types. + - The :mod:`pygame.key` module lists the keyboard constants and modifiers + (``K_``\* and ``MOD_``\*) relating to the ``key`` and ``mod`` attributes of + the ``KEYDOWN`` and ``KEYUP`` events. + - The :mod:`pygame.time` module defines ``TIMER_RESOLUTION``. + +.. ## pygame.locals ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/mask.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/mask.rst.txt new file mode 100644 index 00000000..f4365cfd --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/mask.rst.txt @@ -0,0 +1,642 @@ +.. include:: common.txt + +:mod:`pygame.mask` +================== + +.. module:: pygame.mask + :synopsis: pygame module for image masks. + +| :sl:`pygame module for image masks.` + +Useful for fast pixel perfect collision detection. A mask uses 1 bit per-pixel +to store which parts collide. + +.. versionadded:: 1.8 + +.. versionchanged:: 2.0.2 Mask functions now support keyword arguments. + +.. versionchanged:: 2.0.2 Mask functions that take positions or offsets now + support :class:`pygame.math.Vector2` arguments. + + +.. function:: from_surface + + | :sl:`Creates a Mask from the given surface` + | :sg:`from_surface(surface) -> Mask` + | :sg:`from_surface(surface, threshold=127) -> Mask` + + Creates a :class:`Mask` object from the given surface by setting all the + opaque pixels and not setting the transparent pixels. + + If the surface uses a color-key, then it is used to decide which bits in + the resulting mask are set. All the pixels that are **not** equal to the + color-key are **set** and the pixels equal to the color-key are not set. + + If a color-key is not used, then the alpha value of each pixel is used to + decide which bits in the resulting mask are set. All the pixels that have an + alpha value **greater than** the ``threshold`` parameter are **set** and the + pixels with an alpha value less than or equal to the ``threshold`` are + not set. + + :param Surface surface: the surface to create the mask from + :param int threshold: (optional) the alpha threshold (default is 127) to + compare with each surface pixel's alpha value, if the ``surface`` is + color-keyed this parameter is ignored + + :returns: a newly created :class:`Mask` object from the given surface + :rtype: Mask + + .. note:: + This function is used to create the masks for + :func:`pygame.sprite.collide_mask`. + + .. ## pygame.mask.from_surface ## + +.. function:: from_threshold + + | :sl:`Creates a mask by thresholding Surfaces` + | :sg:`from_threshold(surface, color) -> Mask` + | :sg:`from_threshold(surface, color, threshold=(0, 0, 0, 255), othersurface=None, palette_colors=1) -> Mask` + + This is a more featureful method of getting a :class:`Mask` from a surface. + + If the optional ``othersurface`` is not used, all the pixels **within** the + ``threshold`` of the ``color`` parameter are **set** in the resulting mask. + + If the optional ``othersurface`` is used, every pixel in the first surface + that is **within** the ``threshold`` of the corresponding pixel in + ``othersurface`` is **set** in the resulting mask. + + :param Surface surface: the surface to create the mask from + :param color: color used to check if the surface's pixels are within the + given ``threshold`` range, this parameter is ignored if the optional + ``othersurface`` parameter is supplied + :type color: Color or int or tuple(int, int, int, [int]) or list[int, int, int, [int]] + :param threshold: (optional) the threshold range used to check the difference + between two colors (default is ``(0, 0, 0, 255)``) + :type threshold: Color or int or tuple(int, int, int, [int]) or list[int, int, int, [int]] + :param Surface othersurface: (optional) used to check whether the pixels of + the first surface are within the given ``threshold`` range of the pixels + from this surface (default is ``None``) + :param int palette_colors: (optional) indicates whether to use the palette + colors or not, a nonzero value causes the palette colors to be used and a + 0 causes them not to be used (default is 1) + + :returns: a newly created :class:`Mask` object from the given surface + :rtype: Mask + + .. ## pygame.mask.from_threshold ## + +.. class:: Mask + + | :sl:`pygame object for representing 2D bitmasks` + | :sg:`Mask(size=(width, height)) -> Mask` + | :sg:`Mask(size=(width, height), fill=False) -> Mask` + + A ``Mask`` object is used to represent a 2D bitmask. Each bit in + the mask represents a pixel. 1 is used to indicate a set bit and 0 is used + to indicate an unset bit. Set bits in a mask can be used to detect collisions + with other masks and their set bits. + + A filled mask has all of its bits set to 1, conversely an + unfilled/cleared/empty mask has all of its bits set to 0. Masks can be + created unfilled (default) or filled by using the ``fill`` parameter. Masks + can also be cleared or filled using the :func:`pygame.mask.Mask.clear()` and + :func:`pygame.mask.Mask.fill()` methods respectively. + + A mask's coordinates start in the top left corner at ``(0, 0)`` just like + :mod:`pygame.Surface`. Individual bits can be accessed using the + :func:`pygame.mask.Mask.get_at()` and :func:`pygame.mask.Mask.set_at()` + methods. + + .. _mask-offset-label: + + The methods :meth:`overlap`, :meth:`overlap_area`, :meth:`overlap_mask`, + :meth:`draw`, :meth:`erase`, and :meth:`convolve` use an offset parameter + to indicate the offset of another mask's top left corner from the calling + mask's top left corner. The calling mask's top left corner is considered to + be the origin ``(0, 0)``. Offsets are a sequence of two values + ``(x_offset, y_offset)``. Positive and negative offset values are supported. + + :: + + 0 to x (x_offset) + : : + 0 ..... +----:---------+ + to | : | + y .......... +-----------+ + (y_offset) | | othermask | + | +-----------+ + | calling_mask | + +--------------+ + + :param size: the dimensions of the mask (width and height) + :param bool fill: (optional) create an unfilled mask (default: ``False``) or + filled mask (``True``) + + :returns: a newly created :class:`Mask` object + :rtype: Mask + + .. versionchanged:: 2.0.0 + Shallow copy support added. The :class:`Mask` class supports the special + method ``__copy__()`` and shallow copying via ``copy.copy(mask)``. + .. versionchanged:: 2.0.0 Subclassing support added. The :class:`Mask` class + can be used as a base class. + .. versionchanged:: 1.9.5 Added support for keyword arguments. + .. versionchanged:: 1.9.5 Added the optional keyword parameter ``fill``. + .. versionchanged:: 1.9.5 Added support for masks with a width and/or a + height of 0. + + .. method:: copy + + | :sl:`Returns a new copy of the mask` + | :sg:`copy() -> Mask` + + :returns: a new copy of this mask, the new mask will have the same width, + height, and set/unset bits as the original + :rtype: Mask + + .. note:: + If a mask subclass needs to copy any instance specific attributes + then it should override the ``__copy__()`` method. The overridden + ``__copy__()`` method needs to call ``super().__copy__()`` and then + copy the required data as in the following example code. + + :: + + class SubMask(pygame.mask.Mask): + def __copy__(self): + new_mask = super().__copy__() + # Do any SubMask attribute copying here. + return new_mask + + .. versionadded:: 2.0.0 + + .. ## Mask.copy ## + + .. method:: get_size + + | :sl:`Returns the size of the mask` + | :sg:`get_size() -> (width, height)` + + :returns: the size of the mask, (width, height) + :rtype: tuple(int, int) + + .. ## Mask.get_size ## + + .. method:: get_rect + + | :sl:`Returns a Rect based on the size of the mask` + | :sg:`get_rect(\**kwargs) -> Rect` + + Returns a new :func:`pygame.Rect` object based on the size of this mask. + The rect's default position will be ``(0, 0)`` and its default width and + height will be the same as this mask's. The rect's attributes can be + altered via :func:`pygame.Rect` attribute keyword arguments/values passed + into this method. As an example, ``a_mask.get_rect(center=(10, 5))`` would + create a :func:`pygame.Rect` based on the mask's size centered at the + given position. + + :param dict kwargs: :func:`pygame.Rect` attribute keyword arguments/values + that will be applied to the rect + + :returns: a new :func:`pygame.Rect` object based on the size of this mask + with any :func:`pygame.Rect` attribute keyword arguments/values applied + to it + :rtype: Rect + + .. versionadded:: 2.0.0 + + .. ## Mask.get_rect ## + + .. method:: get_at + + | :sl:`Gets the bit at the given position` + | :sg:`get_at(pos) -> int` + + :param pos: the position of the bit to get (x, y) + + :returns: 1 if the bit is set, 0 if the bit is not set + :rtype: int + + :raises IndexError: if the position is outside of the mask's bounds + + .. ## Mask.get_at ## + + .. method:: set_at + + | :sl:`Sets the bit at the given position` + | :sg:`set_at(pos) -> None` + | :sg:`set_at(pos, value=1) -> None` + + :param pos: the position of the bit to set (x, y) + :param int value: any nonzero int will set the bit to 1, 0 will set the + bit to 0 (default is 1) + + :returns: ``None`` + :rtype: NoneType + + :raises IndexError: if the position is outside of the mask's bounds + + .. ## Mask.set_at ## + + .. method:: overlap + + | :sl:`Returns the point of intersection` + | :sg:`overlap(other, offset) -> (x, y)` + | :sg:`overlap(other, offset) -> None` + + Returns the first point of intersection encountered between this mask and + ``other``. A point of intersection is 2 overlapping set bits. + + The current algorithm searches the overlapping area in + ``sizeof(unsigned long int) * CHAR_BIT`` bit wide column blocks (the value + of ``sizeof(unsigned long int) * CHAR_BIT`` is platform dependent, for + clarity it will be referred to as ``W``). Starting at the top left corner + it checks bits 0 to ``W - 1`` of the first row (``(0, 0)`` to + ``(W - 1, 0)``) then continues to the next row (``(0, 1)`` to + ``(W - 1, 1)``). Once this entire column block is checked, it continues to + the next one (``W`` to ``2 * W - 1``). This is repeated until it finds a + point of intersection or the entire overlapping area is checked. + + :param Mask other: the other mask to overlap with this mask + :param offset: the offset of ``other`` from this mask, for more + details refer to the :ref:`Mask offset notes ` + + :returns: point of intersection or ``None`` if no intersection + :rtype: tuple(int, int) or NoneType + + .. ## Mask.overlap ## + + .. method:: overlap_area + + | :sl:`Returns the number of overlapping set bits` + | :sg:`overlap_area(other, offset) -> numbits` + + Returns the number of overlapping set bits between between this mask and + ``other``. + + This can be useful for collision detection. An approximate collision + normal can be found by calculating the gradient of the overlapping area + through the finite difference. + + :: + + dx = mask.overlap_area(other, (x + 1, y)) - mask.overlap_area(other, (x - 1, y)) + dy = mask.overlap_area(other, (x, y + 1)) - mask.overlap_area(other, (x, y - 1)) + + :param Mask other: the other mask to overlap with this mask + :param offset: the offset of ``other`` from this mask, for more + details refer to the :ref:`Mask offset notes ` + + :returns: the number of overlapping set bits + :rtype: int + + .. ## Mask.overlap_area ## + + .. method:: overlap_mask + + | :sl:`Returns a mask of the overlapping set bits` + | :sg:`overlap_mask(other, offset) -> Mask` + + Returns a :class:`Mask`, the same size as this mask, containing the + overlapping set bits between this mask and ``other``. + + :param Mask other: the other mask to overlap with this mask + :param offset: the offset of ``other`` from this mask, for more + details refer to the :ref:`Mask offset notes ` + + :returns: a newly created :class:`Mask` with the overlapping bits set + :rtype: Mask + + .. ## Mask.overlap_mask ## + + .. method:: fill + + | :sl:`Sets all bits to 1` + | :sg:`fill() -> None` + + Sets all bits in the mask to 1. + + :returns: ``None`` + :rtype: NoneType + + .. ## Mask.fill ## + + .. method:: clear + + | :sl:`Sets all bits to 0` + | :sg:`clear() -> None` + + Sets all bits in the mask to 0. + + :returns: ``None`` + :rtype: NoneType + + .. ## Mask.clear ## + + .. method:: invert + + | :sl:`Flips all the bits` + | :sg:`invert() -> None` + + Flips all of the bits in the mask. All the set bits are cleared to 0 and + all the unset bits are set to 1. + + :returns: ``None`` + :rtype: NoneType + + .. ## Mask.invert ## + + .. method:: scale + + | :sl:`Resizes a mask` + | :sg:`scale((width, height)) -> Mask` + + Creates a new :class:`Mask` of the requested size with its bits scaled + from this mask. + + :param size: the width and height (size) of the mask to create + + :returns: a new :class:`Mask` object with its bits scaled from this mask + :rtype: Mask + + :raises ValueError: if ``width < 0`` or ``height < 0`` + + .. ## Mask.scale ## + + .. method:: draw + + | :sl:`Draws a mask onto another` + | :sg:`draw(other, offset) -> None` + + Performs a bitwise OR, drawing ``othermask`` onto this mask. + + :param Mask other: the mask to draw onto this mask + :param offset: the offset of ``other`` from this mask, for more + details refer to the :ref:`Mask offset notes ` + + :returns: ``None`` + :rtype: NoneType + + .. ## Mask.draw ## + + .. method:: erase + + | :sl:`Erases a mask from another` + | :sg:`erase(other, offset) -> None` + + Erases (clears) all bits set in ``other`` from this mask. + + :param Mask other: the mask to erase from this mask + :param offset: the offset of ``other`` from this mask, for more + details refer to the :ref:`Mask offset notes ` + + :returns: ``None`` + :rtype: NoneType + + .. ## Mask.erase ## + + .. method:: count + + | :sl:`Returns the number of set bits` + | :sg:`count() -> bits` + + :returns: the number of set bits in the mask + :rtype: int + + .. ## Mask.count ## + + .. method:: centroid + + | :sl:`Returns the centroid of the set bits` + | :sg:`centroid() -> (x, y)` + + Finds the centroid (the center mass of the set bits) for this mask. + + :returns: a coordinate tuple indicating the centroid of the mask, it will + return ``(0, 0)`` if the mask has no bits set + :rtype: tuple(int, int) + + .. ## Mask.centroid ## + + .. method:: angle + + | :sl:`Returns the orientation of the set bits` + | :sg:`angle() -> theta` + + Finds the approximate orientation (from -90 to 90 degrees) of the set bits + in the mask. This works best if performed on a mask with only one + connected component. + + :returns: the orientation of the set bits in the mask, it will return + ``0.0`` if the mask has no bits set + :rtype: float + + .. note:: + See :meth:`connected_component` for details on how a connected + component is calculated. + + .. ## Mask.angle ## + + .. method:: outline + + | :sl:`Returns a list of points outlining an object` + | :sg:`outline() -> [(x, y), ...]` + | :sg:`outline(every=1) -> [(x, y), ...]` + + Returns a list of points of the outline of the first connected component + encountered in the mask. To find a connected component, the mask is + searched per row (left to right) starting in the top left corner. + + The ``every`` optional parameter skips set bits in the outline. For + example, setting it to 10 would return a list of every 10th set bit in the + outline. + + :param int every: (optional) indicates the number of bits to skip over in + the outline (default is 1) + + :returns: a list of points outlining the first connected component + encountered, an empty list is returned if the mask has no bits set + :rtype: list[tuple(int, int)] + + .. note:: + See :meth:`connected_component` for details on how a connected + component is calculated. + + .. ## Mask.outline ## + + .. method:: convolve + + | :sl:`Returns the convolution of this mask with another mask` + | :sg:`convolve(other) -> Mask` + | :sg:`convolve(other, output=None, offset=(0, 0)) -> Mask` + + Convolve this mask with the given ``other`` Mask. + + :param Mask other: mask to convolve this mask with + :param output: (optional) mask for output (default is ``None``) + :type output: Mask or NoneType + :param offset: the offset of ``other`` from this mask, (default is + ``(0, 0)``) + + :returns: a :class:`Mask` with the ``(i - offset[0], j - offset[1])`` bit + set, if shifting ``other`` (such that its bottom right corner is at + ``(i, j)``) causes it to overlap with this mask + + If an ``output`` Mask is specified, the output is drawn onto it and + it is returned. Otherwise a mask of size ``(MAX(0, width + other mask's + width - 1), MAX(0, height + other mask's height - 1))`` is created and + returned. + :rtype: Mask + + .. ## Mask.convolve ## + + .. method:: connected_component + + | :sl:`Returns a mask containing a connected component` + | :sg:`connected_component() -> Mask` + | :sg:`connected_component(pos) -> Mask` + + A connected component is a group (1 or more) of connected set bits + (orthogonally and diagonally). The SAUF algorithm, which checks 8 point + connectivity, is used to find a connected component in the mask. + + By default this method will return a :class:`Mask` containing the largest + connected component in the mask. Optionally, a bit coordinate can be + specified and the connected component containing it will be returned. If + the bit at the given location is not set, the returned :class:`Mask` will + be empty (no bits set). + + :param pos: (optional) selects the connected component that contains the + bit at this position + + :returns: a :class:`Mask` object (same size as this mask) with the largest + connected component from this mask, if this mask has no bits set then + an empty mask will be returned + + If the ``pos`` parameter is provided then the mask returned will have + the connected component that contains this position. An empty mask will + be returned if the ``pos`` parameter selects an unset bit. + :rtype: Mask + + :raises IndexError: if the optional ``pos`` parameter is outside of the + mask's bounds + + .. ## Mask.connected_component ## + + .. method:: connected_components + + | :sl:`Returns a list of masks of connected components` + | :sg:`connected_components() -> [Mask, ...]` + | :sg:`connected_components(minimum=0) -> [Mask, ...]` + + Provides a list containing a :class:`Mask` object for each connected + component. + + :param int minimum: (optional) indicates the minimum number of bits (to + filter out noise) per connected component (default is 0, which equates + to no minimum and is equivalent to setting it to 1, as a connected + component must have at least 1 bit set) + + :returns: a list containing a :class:`Mask` object for each connected + component, an empty list is returned if the mask has no bits set + :rtype: list[Mask] + + .. note:: + See :meth:`connected_component` for details on how a connected + component is calculated. + + .. ## Mask.connected_components ## + + .. method:: get_bounding_rects + + | :sl:`Returns a list of bounding rects of connected components` + | :sg:`get_bounding_rects() -> [Rect, ...]` + + Provides a list containing a bounding rect for each connected component. + + :returns: a list containing a bounding rect for each connected component, + an empty list is returned if the mask has no bits set + :rtype: list[Rect] + + .. note:: + See :meth:`connected_component` for details on how a connected + component is calculated. + + .. ## Mask.get_bounding_rects ## + + .. method:: to_surface + + | :sl:`Returns a surface with the mask drawn on it` + | :sg:`to_surface() -> Surface` + | :sg:`to_surface(surface=None, setsurface=None, unsetsurface=None, setcolor=(255, 255, 255, 255), unsetcolor=(0, 0, 0, 255), dest=(0, 0)) -> Surface` + + Draws this mask on the given surface. Set bits (bits set to 1) and unset + bits (bits set to 0) can be drawn onto a surface. + + :param surface: (optional) Surface to draw mask onto, if no surface is + provided one will be created (default is ``None``, which will cause a + surface with the parameters + ``Surface(size=mask.get_size(), flags=SRCALPHA, depth=32)`` to be + created, drawn on, and returned) + :type surface: Surface or None + :param setsurface: (optional) use this surface's color values to draw + set bits (default is ``None``), if this surface is smaller than the + mask any bits outside its bounds will use the ``setcolor`` value + :type setsurface: Surface or None + :param unsetsurface: (optional) use this surface's color values to draw + unset bits (default is ``None``), if this surface is smaller than the + mask any bits outside its bounds will use the ``unsetcolor`` value + :type unsetsurface: Surface or None + :param setcolor: (optional) color to draw set bits (default is + ``(255, 255, 255, 255)``, white), use ``None`` to skip drawing the set + bits, the ``setsurface`` parameter (if set) will takes precedence over + this parameter + :type setcolor: Color or str or int or tuple(int, int, int, [int]) or + list(int, int, int, [int]) or None + :param unsetcolor: (optional) color to draw unset bits (default is + ``(0, 0, 0, 255)``, black), use ``None`` to skip drawing the unset + bits, the ``unsetsurface`` parameter (if set) will takes precedence + over this parameter + :type unsetcolor: Color or str or int or tuple(int, int, int, [int]) or + list(int, int, int, [int]) or None + :param dest: (optional) surface destination of where to position the + topleft corner of the mask being drawn (default is ``(0, 0)``), if a + Rect is used as the ``dest`` parameter, its ``x`` and ``y`` attributes + will be used as the destination, **NOTE1:** rects with a negative width + or height value will not be normalized before using their ``x`` and + ``y`` values, **NOTE2:** this destination value is only used to + position the mask on the surface, it does not offset the ``setsurface`` + and ``unsetsurface`` from the mask, they are always aligned with the + mask (i.e. position ``(0, 0)`` on the mask always corresponds to + position ``(0, 0)`` on the ``setsurface`` and ``unsetsurface``) + :type dest: Rect or tuple(int, int) or list(int, int) or Vector2(int, int) + + :returns: the ``surface`` parameter (or a newly created surface if no + ``surface`` parameter was provided) with this mask drawn on it + :rtype: Surface + + :raises ValueError: if the ``setsurface`` parameter or ``unsetsurface`` + parameter does not have the same format (bytesize/bitsize/alpha) as + the ``surface`` parameter + + .. note :: + To skip drawing the set bits, both ``setsurface`` and ``setcolor`` must + be ``None``. The ``setsurface`` parameter defaults to ``None``, but + ``setcolor`` defaults to a color value and therefore must be set to + ``None``. + + .. note :: + To skip drawing the unset bits, both ``unsetsurface`` and + ``unsetcolor`` must be ``None``. The ``unsetsurface`` parameter + defaults to ``None``, but ``unsetcolor`` defaults to a color value and + therefore must be set to ``None``. + + .. versionadded:: 2.0.0 + + .. ## Mask.to_surface ## + + .. ## pygame.mask.Mask ## + +.. ## pygame.mask ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/math.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/math.rst.txt new file mode 100644 index 00000000..0f037d9f --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/math.rst.txt @@ -0,0 +1,1143 @@ +.. include:: common.txt + +:mod:`pygame.math` +================== + +.. module:: pygame.math + :synopsis: pygame module for vector classes + +| :sl:`pygame module for vector classes` + +The pygame math module currently provides Vector classes in two and three +dimensions, ``Vector2`` and ``Vector3`` respectively. + +They support the following numerical operations: ``vec + vec``, ``vec - vec``, +``vec * number``, ``number * vec``, ``vec / number``, ``vec // number``, ``vec += vec``, +``vec -= vec``, ``vec *= number``, ``vec /= number``, ``vec //= number``, ``round(vec, ndigits=0)``. + +All these operations will be performed elementwise. +In addition ``vec * vec`` will perform a scalar-product (a.k.a. dot-product). +If you want to multiply every element from vector v with every element from +vector w you can use the elementwise method: ``v.elementwise() * w`` + +The coordinates of a vector can be retrieved or set using attributes or +subscripts + +:: + + v = pygame.Vector3() + + v.x = 5 + v[1] = 2 * v.x + print(v[1]) # 10 + + v.x == v[0] + v.y == v[1] + v.z == v[2] + +Multiple coordinates can be set using slices or swizzling + +:: + + v = pygame.Vector2() + v.xy = 1, 2 + v[:] = 1, 2 + +.. versionadded:: 1.9.2pre +.. versionchanged:: 1.9.4 Removed experimental notice. +.. versionchanged:: 1.9.4 Allow scalar construction like GLSL Vector2(2) == Vector2(2.0, 2.0) +.. versionchanged:: 1.9.4 :mod:`pygame.math` import not required. More convenient ``pygame.Vector2`` and ``pygame.Vector3``. +.. versionchanged:: 2.2.0 `round` returns a new vector with components rounded to the specified digits. + +.. function:: clamp + + | :sl:`returns value clamped to min and max.` + | :sg:`clamp(value, min, max) -> float` + + **Experimental:** feature still in development available for testing and feedback. It may change. + `Please leave clamp feedback with authors `_ + + Clamps a numeric ``value`` so that it's no lower than ``min``, and no higher + than ``max``. + + .. versionadded:: 2.1.3 + + .. ## math.clamp ## + +.. function:: lerp + + | :sl:`interpolates between two values by a weight.` + | :sg:`lerp(a, b, weight) -> float` + + Linearly interpolates between ``a`` and ``b`` by ``weight`` using the formula ``a + (b-a) * weight``. + + If ``weight`` is ``0.5``, ``lerp`` will return the value half-way between ``a`` + and ``b``. When ``a = 10`` and ``b = 20``, ``lerp(a, b, 0.5)`` will return ``15``. You + can think of weight as the percentage of interpolation from ``a`` to ``b``, ``0.0`` + being 0% and ``1.0`` being 100%. + + ``lerp`` can be used for many things. You could rotate a sprite by a weight with + ``angle = lerp(0, 360, weight)``. You could even scale an enemy's attack value + based on the level you're playing: + + :: + + FINAL_LEVEL = 10 + current_level = 2 + + attack = lerp(10, 50, current_level/MAX_LEVEL) # 18 + + If you're on level 0, ``attack`` will be ``10``, if you're on level 10, + ``attack`` will be ``50``. If you're on level 5, the + result of ``current_level/MAX_LEVEL`` will be ``0.5`` + which represents 50%, therefore ``attack`` will be ``30``, which is the midpoint of ``10`` and ``50``. + + Raises a ValueError if ``weight`` is outside the range of ``[0, 1]``. + + .. versionadded:: 2.1.3 + + .. ## math.lerp ## + +.. class:: Vector2 + + | :sl:`a 2-Dimensional Vector` + | :sg:`Vector2() -> Vector2(0, 0)` + | :sg:`Vector2(int) -> Vector2` + | :sg:`Vector2(float) -> Vector2` + | :sg:`Vector2(Vector2) -> Vector2` + | :sg:`Vector2(x, y) -> Vector2` + | :sg:`Vector2((x, y)) -> Vector2` + + Some general information about the ``Vector2`` class. + + .. versionchanged:: 2.1.3 + Inherited methods of vector subclasses now correctly return an instance of the + subclass instead of the superclass + + .. method:: dot + + | :sl:`calculates the dot- or scalar-product with the other vector` + | :sg:`dot(Vector2) -> float` + + .. ## Vector2.dot ## + + .. method:: cross + + | :sl:`calculates the cross- or vector-product` + | :sg:`cross(Vector2) -> float` + + calculates the third component of the cross-product. + + .. ## Vector2.cross ## + + .. method:: magnitude + + | :sl:`returns the Euclidean magnitude of the vector.` + | :sg:`magnitude() -> float` + + calculates the magnitude of the vector which follows from the + theorem: ``vec.magnitude() == math.sqrt(vec.x**2 + vec.y**2)`` + + .. ## Vector2.magnitude ## + + .. method:: magnitude_squared + + | :sl:`returns the squared magnitude of the vector.` + | :sg:`magnitude_squared() -> float` + + calculates the magnitude of the vector which follows from the + theorem: ``vec.magnitude_squared() == vec.x**2 + vec.y**2``. This + is faster than ``vec.magnitude()`` because it avoids the square root. + + .. ## Vector2.magnitude_squared ## + + .. method:: length + + | :sl:`returns the Euclidean length of the vector.` + | :sg:`length() -> float` + + calculates the Euclidean length of the vector which follows from the + Pythagorean theorem: ``vec.length() == math.sqrt(vec.x**2 + vec.y**2)`` + + .. ## Vector2.length ## + + .. method:: length_squared + + | :sl:`returns the squared Euclidean length of the vector.` + | :sg:`length_squared() -> float` + + calculates the Euclidean length of the vector which follows from the + Pythagorean theorem: ``vec.length_squared() == vec.x**2 + vec.y**2``. + This is faster than ``vec.length()`` because it avoids the square root. + + .. ## Vector2.length_squared ## + + .. method:: normalize + + | :sl:`returns a vector with the same direction but length 1.` + | :sg:`normalize() -> Vector2` + + Returns a new vector that has ``length`` equal to ``1`` and the same + direction as self. + + .. ## Vector2.normalize ## + + .. method:: normalize_ip + + | :sl:`normalizes the vector in place so that its length is 1.` + | :sg:`normalize_ip() -> None` + + Normalizes the vector so that it has ``length`` equal to ``1``. + The direction of the vector is not changed. + + .. ## Vector2.normalize_ip ## + + .. method:: is_normalized + + | :sl:`tests if the vector is normalized i.e. has length == 1.` + | :sg:`is_normalized() -> Bool` + + Returns True if the vector has ``length`` equal to ``1``. Otherwise + it returns ``False``. + + .. ## Vector2.is_normalized ## + + .. method:: scale_to_length + + | :sl:`scales the vector to a given length.` + | :sg:`scale_to_length(float) -> None` + + Scales the vector so that it has the given length. The direction of the + vector is not changed. You can also scale to length ``0``. If the vector + is the zero vector (i.e. has length ``0`` thus no direction) a + ``ValueError`` is raised. + + .. ## Vector2.scale_to_length ## + + .. method:: reflect + + | :sl:`returns a vector reflected of a given normal.` + | :sg:`reflect(Vector2) -> Vector2` + + Returns a new vector that points in the direction as if self would bounce + of a surface characterized by the given surface normal. The length of the + new vector is the same as self's. + + .. ## Vector2.reflect ## + + .. method:: reflect_ip + + | :sl:`reflect the vector of a given normal in place.` + | :sg:`reflect_ip(Vector2) -> None` + + Changes the direction of self as if it would have been reflected of a + surface with the given surface normal. + + .. ## Vector2.reflect_ip ## + + .. method:: distance_to + + | :sl:`calculates the Euclidean distance to a given vector.` + | :sg:`distance_to(Vector2) -> float` + + .. ## Vector2.distance_to ## + + .. method:: distance_squared_to + + | :sl:`calculates the squared Euclidean distance to a given vector.` + | :sg:`distance_squared_to(Vector2) -> float` + + .. ## Vector2.distance_squared_to ## + + .. method:: move_towards + + | :sl:`returns a vector moved toward the target by a given distance.` + | :sg:`move_towards(Vector2, float) -> Vector2` + + **Experimental:** feature still in development available for testing and feedback. It may change. + `Please leave move_towards feedback with authors `_ + + Returns a Vector which is moved towards the given Vector by a given + distance and does not overshoot past its target Vector. + The first parameter determines the target Vector, while the second + parameter determines the delta distance. If the distance is in the + negatives, then it will move away from the target Vector. + + .. versionadded:: 2.1.3 + + .. ## Vector2.move_towards ## + + .. method:: move_towards_ip + + | :sl:`moves the vector toward its target at a given distance.` + | :sg:`move_towards_ip(Vector2, float) -> None` + + **Experimental:** feature still in development available for testing and feedback. It may change. + `Please leave move_towards_ip feedback with authors `_ + + Moves itself toward the given Vector at a given distance and does not + overshoot past its target Vector. + The first parameter determines the target Vector, while the second + parameter determines the delta distance. If the distance is in the + negatives, then it will move away from the target Vector. + + .. versionadded:: 2.1.3 + + .. ## Vector2.move_towards_ip ## + + .. method:: lerp + + | :sl:`returns a linear interpolation to the given vector.` + | :sg:`lerp(Vector2, float) -> Vector2` + + Returns a Vector which is a linear interpolation between self and the + given Vector. The second parameter determines how far between self and + other the result is going to be. It must be a value between ``0`` and ``1`` + where ``0`` means self and ``1`` means other will be returned. + + .. ## Vector2.lerp ## + + .. method:: slerp + + | :sl:`returns a spherical interpolation to the given vector.` + | :sg:`slerp(Vector2, float) -> Vector2` + + Calculates the spherical interpolation from self to the given Vector. The + second argument - often called t - must be in the range ``[-1, 1]``. It + parametrizes where - in between the two vectors - the result should be. + If a negative value is given the interpolation will not take the + complement of the shortest path. + + .. ## Vector2.slerp ## + + .. method:: elementwise + + | :sl:`The next operation will be performed elementwise.` + | :sg:`elementwise() -> VectorElementwiseProxy` + + Applies the following operation to each element of the vector. + + .. ## Vector2.elementwise ## + + .. method:: rotate + + | :sl:`rotates a vector by a given angle in degrees.` + | :sg:`rotate(angle) -> Vector2` + + Returns a vector which has the same length as self but is rotated + counterclockwise by the given angle in degrees. + (Note that due to pygame's inverted y coordinate system, the rotation + will look clockwise if displayed). + + .. ## Vector2.rotate ## + + .. method:: rotate_rad + + | :sl:`rotates a vector by a given angle in radians.` + | :sg:`rotate_rad(angle) -> Vector2` + + Returns a vector which has the same length as self but is rotated + counterclockwise by the given angle in radians. + (Note that due to pygame's inverted y coordinate system, the rotation + will look clockwise if displayed). + + .. versionadded:: 2.0.0 + + .. ## Vector2.rotate_rad ## + + .. method:: rotate_ip + + | :sl:`rotates the vector by a given angle in degrees in place.` + | :sg:`rotate_ip(angle) -> None` + + Rotates the vector counterclockwise by the given angle in degrees. The + length of the vector is not changed. + (Note that due to pygame's inverted y coordinate system, the rotation + will look clockwise if displayed). + + .. ## Vector2.rotate_ip ## + + .. method:: rotate_ip_rad + + | :sl:`rotates the vector by a given angle in radians in place.` + | :sg:`rotate_ip_rad(angle) -> None` + + DEPRECATED: Use rotate_rad_ip() instead. + + .. versionadded:: 2.0.0 + .. deprecated:: 2.1.1 + + .. ## Vector2.rotate_rad_ip ## + + .. method:: rotate_rad_ip + + | :sl:`rotates the vector by a given angle in radians in place.` + | :sg:`rotate_rad_ip(angle) -> None` + + Rotates the vector counterclockwise by the given angle in radians. The + length of the vector is not changed. + (Note that due to pygame's inverted y coordinate system, the rotation + will look clockwise if displayed). + + .. versionadded:: 2.1.1 + + .. ## Vector2.rotate_rad_ip ## + + .. method:: angle_to + + | :sl:`calculates the angle to a given vector in degrees.` + | :sg:`angle_to(Vector2) -> float` + + Returns the angle from self to the passed ``Vector2`` that would rotate self + to be aligned with the passed ``Vector2`` without crossing over the negative + x-axis. + + .. figure:: code_examples/angle_to.png + :alt: angle_to image + + Example demonstrating the angle returned + + .. ## Vector2.angle_to ## + + .. method:: as_polar + + | :sl:`returns a tuple with radial distance and azimuthal angle.` + | :sg:`as_polar() -> (r, phi)` + + Returns a tuple ``(r, phi)`` where r is the radial distance, and phi + is the azimuthal angle. + + .. ## Vector2.as_polar ## + + .. method:: from_polar + + | :sl:`Creates a Vector2(x, y) or sets x and y from a polar coordinates tuple.` + | :sg:`Vector2.from_polar((r, phi)) -> Vector2` + | :sg:`Vector2().from_polar((r, phi)) -> None` + + If used from the class creates a Vector2(x,y), else sets x and y. + The values of x and y are defined from a tuple ``(r, phi)`` where r + is the radial distance, and phi is the azimuthal angle. + + .. ## Vector2.from_polar ## + + .. method:: project + + | :sl:`projects a vector onto another.` + | :sg:`project(Vector2) -> Vector2` + + Returns the projected vector. This is useful for collision detection in finding the components in a certain direction (e.g. in direction of the wall). + For a more detailed explanation see `Wikipedia `_. + + .. versionadded:: 2.0.2 + + .. ## Vector2.project ## + + + .. method:: copy + + | :sl:`Returns a copy of itself.` + | :sg:`copy() -> Vector2` + + Returns a new Vector2 having the same dimensions. + + .. versionadded:: 2.1.1 + + .. ## Vector2.copy ## + + + .. method:: clamp_magnitude + + | :sl:`Returns a copy of a vector with the magnitude clamped between max_length and min_length.` + | :sg:`clamp_magnitude(max_length) -> Vector2` + | :sg:`clamp_magnitude(min_length, max_length) -> Vector2` + + **Experimental:** feature still in development available for testing and feedback. It may change. + `Please leave clamp_magnitude feedback with authors `_ + + Returns a new copy of a vector with the magnitude clamped between + ``max_length`` and ``min_length``. If only one argument is passed, it is + taken to be the ``max_length`` + + This function raises ``ValueError`` if ``min_length`` is greater than + ``max_length``, or if either of these values are negative. + + .. versionadded:: 2.1.3 + + .. ## Vector2.clamp_magnitude ## + + + .. method:: clamp_magnitude_ip + + | :sl:`Clamps the vector's magnitude between max_length and min_length` + | :sg:`clamp_magnitude_ip(max_length) -> None` + | :sg:`clamp_magnitude_ip(min_length, max_length) -> None` + + **Experimental:** feature still in development available for testing and feedback. It may change. + `Please leave clamp_magnitude_ip feedback with authors `_ + + Clamps the vector's magnitude between ``max_length`` and ``min_length``. + If only one argument is passed, it is taken to be the ``max_length`` + + This function raises ``ValueError`` if ``min_length`` is greater than + ``max_length``, or if either of these values are negative. + + .. versionadded:: 2.1.3 + + .. ## Vector2.clamp_magnitude_ip ## + + + .. method:: update + + | :sl:`Sets the coordinates of the vector.` + | :sg:`update() -> None` + | :sg:`update(int) -> None` + | :sg:`update(float) -> None` + | :sg:`update(Vector2) -> None` + | :sg:`update(x, y) -> None` + | :sg:`update((x, y)) -> None` + + Sets coordinates x and y in place. + + .. versionadded:: 1.9.5 + + .. ## Vector2.update ## + + + .. attribute:: epsilon + + | :sl:`Determines the tolerance of vector calculations.` + + Both Vector classes have a value named ``epsilon`` that defaults to ``1e-6``. + This value acts as a numerical margin in various methods to account for floating point + arithmetic errors. Specifically, ``epsilon`` is used in the following places: + + * comparing Vectors (``==`` and ``!=``) + * the ``is_normalized`` method (if the square of the length is within ``epsilon`` of 1, it's normalized) + * slerping (a Vector with a length of ``> True + print(v == u) # >> False + + You'll probably never have to change ``epsilon`` from the default value, but in rare situations you might + find that either the margin is too large or too small, in which case changing ``epsilon`` slightly + might help you out. + + + .. ## pygame.math.Vector2 ## + +.. class:: Vector3 + + | :sl:`a 3-Dimensional Vector` + | :sg:`Vector3() -> Vector3(0, 0, 0)` + | :sg:`Vector3(int) -> Vector3` + | :sg:`Vector3(float) -> Vector3` + | :sg:`Vector3(Vector3) -> Vector3` + | :sg:`Vector3(x, y, z) -> Vector3` + | :sg:`Vector3((x, y, z)) -> Vector3` + + Some general information about the Vector3 class. + + .. versionchanged:: 2.1.3 + Inherited methods of vector subclasses now correctly return an instance of the + subclass instead of the superclass + + .. method:: dot + + | :sl:`calculates the dot- or scalar-product with the other vector` + | :sg:`dot(Vector3) -> float` + + .. ## Vector3.dot ## + + .. method:: cross + + | :sl:`calculates the cross- or vector-product` + | :sg:`cross(Vector3) -> Vector3` + + calculates the cross-product. + + .. ## Vector3.cross ## + + .. method:: magnitude + + | :sl:`returns the Euclidean magnitude of the vector.` + | :sg:`magnitude() -> float` + + calculates the magnitude of the vector which follows from the + theorem: ``vec.magnitude() == math.sqrt(vec.x**2 + vec.y**2 + vec.z**2)`` + + .. ## Vector3.magnitude ## + + .. method:: magnitude_squared + + | :sl:`returns the squared Euclidean magnitude of the vector.` + | :sg:`magnitude_squared() -> float` + + calculates the magnitude of the vector which follows from the + theorem: + ``vec.magnitude_squared() == vec.x**2 + vec.y**2 + vec.z**2``. + This is faster than ``vec.magnitude()`` because it avoids the + square root. + + .. ## Vector3.magnitude_squared ## + + .. method:: length + + | :sl:`returns the Euclidean length of the vector.` + | :sg:`length() -> float` + + calculates the Euclidean length of the vector which follows from the + Pythagorean theorem: + ``vec.length() == math.sqrt(vec.x**2 + vec.y**2 + vec.z**2)`` + + .. ## Vector3.length ## + + .. method:: length_squared + + | :sl:`returns the squared Euclidean length of the vector.` + | :sg:`length_squared() -> float` + + calculates the Euclidean length of the vector which follows from the + Pythagorean theorem: + ``vec.length_squared() == vec.x**2 + vec.y**2 + vec.z**2``. + This is faster than ``vec.length()`` because it avoids the square root. + + .. ## Vector3.length_squared ## + + .. method:: normalize + + | :sl:`returns a vector with the same direction but length 1.` + | :sg:`normalize() -> Vector3` + + Returns a new vector that has ``length`` equal to ``1`` and the same + direction as self. + + .. ## Vector3.normalize ## + + .. method:: normalize_ip + + | :sl:`normalizes the vector in place so that its length is 1.` + | :sg:`normalize_ip() -> None` + + Normalizes the vector so that it has ``length`` equal to ``1``. The + direction of the vector is not changed. + + .. ## Vector3.normalize_ip ## + + .. method:: is_normalized + + | :sl:`tests if the vector is normalized i.e. has length == 1.` + | :sg:`is_normalized() -> Bool` + + Returns True if the vector has ``length`` equal to ``1``. Otherwise it + returns ``False``. + + .. ## Vector3.is_normalized ## + + .. method:: scale_to_length + + | :sl:`scales the vector to a given length.` + | :sg:`scale_to_length(float) -> None` + + Scales the vector so that it has the given length. The direction of the + vector is not changed. You can also scale to length ``0``. If the vector + is the zero vector (i.e. has length ``0`` thus no direction) a + ``ValueError`` is raised. + + .. ## Vector3.scale_to_length ## + + .. method:: reflect + + | :sl:`returns a vector reflected of a given normal.` + | :sg:`reflect(Vector3) -> Vector3` + + Returns a new vector that points in the direction as if self would bounce + of a surface characterized by the given surface normal. The length of the + new vector is the same as self's. + + .. ## Vector3.reflect ## + + .. method:: reflect_ip + + | :sl:`reflect the vector of a given normal in place.` + | :sg:`reflect_ip(Vector3) -> None` + + Changes the direction of self as if it would have been reflected of a + surface with the given surface normal. + + .. ## Vector3.reflect_ip ## + + .. method:: distance_to + + | :sl:`calculates the Euclidean distance to a given vector.` + | :sg:`distance_to(Vector3) -> float` + + .. ## Vector3.distance_to ## + + .. method:: distance_squared_to + + | :sl:`calculates the squared Euclidean distance to a given vector.` + | :sg:`distance_squared_to(Vector3) -> float` + + .. ## Vector3.distance_squared_to ## + + .. method:: move_towards + + | :sl:`returns a vector moved toward the target by a given distance.` + | :sg:`move_towards(Vector3, float) -> Vector3` + + **Experimental:** feature still in development available for testing and feedback. It may change. + `Please leave move_towards feedback with authors `_ + + Returns a Vector which is moved towards the given Vector by a given + distance and does not overshoot past its target Vector. + The first parameter determines the target Vector, while the second + parameter determines the delta distance. If the distance is in the + negatives, then it will move away from the target Vector. + + .. versionadded:: 2.1.3 + + .. ## Vector3.move_towards ## + + .. method:: move_towards_ip + + | :sl:`moves the vector toward its target at a given distance.` + | :sg:`move_towards_ip(Vector3, float) -> None` + + **Experimental:** feature still in development available for testing and feedback. It may change. + `Please leave move_towards_ip feedback with authors `_ + + Moves itself toward the given Vector at a given distance and does not + overshoot past its target Vector. + The first parameter determines the target Vector, while the second + parameter determines the delta distance. If the distance is in the + negatives, then it will move away from the target Vector. + + .. versionadded:: 2.1.3 + + .. ## Vector3.move_towards_ip ## + + .. method:: lerp + + | :sl:`returns a linear interpolation to the given vector.` + | :sg:`lerp(Vector3, float) -> Vector3` + + Returns a Vector which is a linear interpolation between self and the + given Vector. The second parameter determines how far between self an + other the result is going to be. It must be a value between ``0`` and + ``1``, where ``0`` means self and ``1`` means other will be returned. + + .. ## Vector3.lerp ## + + .. method:: slerp + + | :sl:`returns a spherical interpolation to the given vector.` + | :sg:`slerp(Vector3, float) -> Vector3` + + Calculates the spherical interpolation from self to the given Vector. The + second argument - often called t - must be in the range ``[-1, 1]``. It + parametrizes where - in between the two vectors - the result should be. + If a negative value is given the interpolation will not take the + complement of the shortest path. + + .. ## Vector3.slerp ## + + .. method:: elementwise + + | :sl:`The next operation will be performed elementwise.` + | :sg:`elementwise() -> VectorElementwiseProxy` + + Applies the following operation to each element of the vector. + + .. ## Vector3.elementwise ## + + .. method:: rotate + + | :sl:`rotates a vector by a given angle in degrees.` + | :sg:`rotate(angle, Vector3) -> Vector3` + + Returns a vector which has the same length as self but is rotated + counterclockwise by the given angle in degrees around the given axis. + (Note that due to pygame's inverted y coordinate system, the rotation + will look clockwise if displayed). + + .. ## Vector3.rotate ## + + .. method:: rotate_rad + + | :sl:`rotates a vector by a given angle in radians.` + | :sg:`rotate_rad(angle, Vector3) -> Vector3` + + Returns a vector which has the same length as self but is rotated + counterclockwise by the given angle in radians around the given axis. + (Note that due to pygame's inverted y coordinate system, the rotation + will look clockwise if displayed). + + .. versionadded:: 2.0.0 + + .. ## Vector3.rotate_rad ## + + .. method:: rotate_ip + + | :sl:`rotates the vector by a given angle in degrees in place.` + | :sg:`rotate_ip(angle, Vector3) -> None` + + Rotates the vector counterclockwise around the given axis by the given + angle in degrees. The length of the vector is not changed. + (Note that due to pygame's inverted y coordinate system, the rotation + will look clockwise if displayed). + + .. ## Vector3.rotate_ip ## + + .. method:: rotate_ip_rad + + | :sl:`rotates the vector by a given angle in radians in place.` + | :sg:`rotate_ip_rad(angle, Vector3) -> None` + + DEPRECATED: Use rotate_rad_ip() instead. + + .. versionadded:: 2.0.0 + .. deprecated:: 2.1.1 + + .. ## Vector3.rotate_ip_rad ## + + .. method:: rotate_rad_ip + + | :sl:`rotates the vector by a given angle in radians in place.` + | :sg:`rotate_rad_ip(angle, Vector3) -> None` + + Rotates the vector counterclockwise around the given axis by the given + angle in radians. The length of the vector is not changed. + (Note that due to pygame's inverted y coordinate system, the rotation + will look clockwise if displayed). + + .. versionadded:: 2.1.1 + + .. ## Vector3.rotate_rad_ip ## + + .. method:: rotate_x + + | :sl:`rotates a vector around the x-axis by the angle in degrees.` + | :sg:`rotate_x(angle) -> Vector3` + + Returns a vector which has the same length as self but is rotated + counterclockwise around the x-axis by the given angle in degrees. + (Note that due to pygame's inverted y coordinate system, the rotation + will look clockwise if displayed). + + .. ## Vector3.rotate_x ## + + .. method:: rotate_x_rad + + | :sl:`rotates a vector around the x-axis by the angle in radians.` + | :sg:`rotate_x_rad(angle) -> Vector3` + + Returns a vector which has the same length as self but is rotated + counterclockwise around the x-axis by the given angle in radians. + (Note that due to pygame's inverted y coordinate system, the rotation + will look clockwise if displayed). + + .. versionadded:: 2.0.0 + + .. ## Vector3.rotate_x_rad ## + + .. method:: rotate_x_ip + + | :sl:`rotates the vector around the x-axis by the angle in degrees in place.` + | :sg:`rotate_x_ip(angle) -> None` + + Rotates the vector counterclockwise around the x-axis by the given angle + in degrees. The length of the vector is not changed. + (Note that due to pygame's inverted y coordinate system, the rotation + will look clockwise if displayed). + + .. ## Vector3.rotate_x_ip ## + + .. method:: rotate_x_ip_rad + + | :sl:`rotates the vector around the x-axis by the angle in radians in place.` + | :sg:`rotate_x_ip_rad(angle) -> None` + + DEPRECATED: Use rotate_x_rad_ip() instead. + + .. versionadded:: 2.0.0 + .. deprecated:: 2.1.1 + + .. ## Vector3.rotate_x_ip_rad ## + + .. method:: rotate_x_rad_ip + + | :sl:`rotates the vector around the x-axis by the angle in radians in place.` + | :sg:`rotate_x_rad_ip(angle) -> None` + + Rotates the vector counterclockwise around the x-axis by the given angle + in radians. The length of the vector is not changed. + (Note that due to pygame's inverted y coordinate system, the rotation + will look clockwise if displayed). + + .. versionadded:: 2.1.1 + + .. ## Vector3.rotate_x_rad_ip ## + + .. method:: rotate_y + + | :sl:`rotates a vector around the y-axis by the angle in degrees.` + | :sg:`rotate_y(angle) -> Vector3` + + Returns a vector which has the same length as self but is rotated + counterclockwise around the y-axis by the given angle in degrees. + (Note that due to pygame's inverted y coordinate system, the rotation + will look clockwise if displayed). + + .. ## Vector3.rotate_y ## + + .. method:: rotate_y_rad + + | :sl:`rotates a vector around the y-axis by the angle in radians.` + | :sg:`rotate_y_rad(angle) -> Vector3` + + Returns a vector which has the same length as self but is rotated + counterclockwise around the y-axis by the given angle in radians. + (Note that due to pygame's inverted y coordinate system, the rotation + will look clockwise if displayed). + + .. versionadded:: 2.0.0 + + .. ## Vector3.rotate_y_rad ## + + .. method:: rotate_y_ip + + | :sl:`rotates the vector around the y-axis by the angle in degrees in place.` + | :sg:`rotate_y_ip(angle) -> None` + + Rotates the vector counterclockwise around the y-axis by the given angle + in degrees. The length of the vector is not changed. + (Note that due to pygame's inverted y coordinate system, the rotation + will look clockwise if displayed). + + .. ## Vector3.rotate_y_ip ## + + .. method:: rotate_y_ip_rad + + | :sl:`rotates the vector around the y-axis by the angle in radians in place.` + | :sg:`rotate_y_ip_rad(angle) -> None` + + DEPRECATED: Use rotate_y_rad_ip() instead. + + .. versionadded:: 2.0.0 + .. deprecated:: 2.1.1 + + .. ## Vector3.rotate_y_ip_rad ## + + .. method:: rotate_y_rad_ip + + | :sl:`rotates the vector around the y-axis by the angle in radians in place.` + | :sg:`rotate_y_rad_ip(angle) -> None` + + Rotates the vector counterclockwise around the y-axis by the given angle + in radians. The length of the vector is not changed. + (Note that due to pygame's inverted y coordinate system, the rotation + will look clockwise if displayed). + + .. versionadded:: 2.1.1 + + .. ## Vector3.rotate_y_rad_ip ## + + .. method:: rotate_z + + | :sl:`rotates a vector around the z-axis by the angle in degrees.` + | :sg:`rotate_z(angle) -> Vector3` + + Returns a vector which has the same length as self but is rotated + counterclockwise around the z-axis by the given angle in degrees. + (Note that due to pygame's inverted y coordinate system, the rotation + will look clockwise if displayed). + + .. ## Vector3.rotate_z ## + + .. method:: rotate_z_rad + + | :sl:`rotates a vector around the z-axis by the angle in radians.` + | :sg:`rotate_z_rad(angle) -> Vector3` + + Returns a vector which has the same length as self but is rotated + counterclockwise around the z-axis by the given angle in radians. + (Note that due to pygame's inverted y coordinate system, the rotation + will look clockwise if displayed). + + .. versionadded:: 2.0.0 + + .. ## Vector3.rotate_z_rad ## + + .. method:: rotate_z_ip + + | :sl:`rotates the vector around the z-axis by the angle in degrees in place.` + | :sg:`rotate_z_ip(angle) -> None` + + Rotates the vector counterclockwise around the z-axis by the given angle + in degrees. The length of the vector is not changed. + (Note that due to pygame's inverted y coordinate system, the rotation + will look clockwise if displayed). + + .. ## Vector3.rotate_z_ip ## + + .. method:: rotate_z_ip_rad + + | :sl:`rotates the vector around the z-axis by the angle in radians in place.` + | :sg:`rotate_z_ip_rad(angle) -> None` + + DEPRECATED: Use rotate_z_rad_ip() instead. + + .. deprecated:: 2.1.1 + + .. ## Vector3.rotate_z_ip_rad ## + + .. method:: rotate_z_rad_ip + + | :sl:`rotates the vector around the z-axis by the angle in radians in place.` + | :sg:`rotate_z_rad_ip(angle) -> None` + + Rotates the vector counterclockwise around the z-axis by the given angle + in radians. The length of the vector is not changed. + (Note that due to pygame's inverted y coordinate system, the rotation + will look clockwise if displayed). + + .. versionadded:: 2.1.1 + + .. ## Vector3.rotate_z_rad_ip ## + + .. method:: angle_to + + | :sl:`calculates the angle to a given vector in degrees.` + | :sg:`angle_to(Vector3) -> float` + + Returns the angle between self and the given vector. + + .. ## Vector3.angle_to ## + + .. method:: as_spherical + + | :sl:`returns a tuple with radial distance, inclination and azimuthal angle.` + | :sg:`as_spherical() -> (r, theta, phi)` + + Returns a tuple ``(r, theta, phi)`` where r is the radial distance, theta is + the inclination angle and phi is the azimuthal angle. + + .. ## Vector3.as_spherical ## + + .. method:: from_spherical + + | :sl:`Creates a Vector3(x, y, z) or sets x, y and z from a spherical coordinates 3-tuple.` + | :sg:`Vector3.from_spherical((r, theta, phi)) -> Vector3` + | :sg:`Vector3().from_spherical((r, theta, phi)) -> None` + + If used from the class creates a Vector3(x, y, z), else sets x, y, and z. + The values of x, y, and z are from a tuple ``(r, theta, phi)`` where r is the radial + distance, theta is the inclination angle and phi is the azimuthal angle. + + .. ## Vector3.from_spherical ## + + .. method:: project + + | :sl:`projects a vector onto another.` + | :sg:`project(Vector3) -> Vector3` + + Returns the projected vector. This is useful for collision detection in finding the components in a certain direction (e.g. in direction of the wall). + For a more detailed explanation see `Wikipedia `_. + + .. versionadded:: 2.0.2 + + .. ## Vector3.project ## + + .. method:: copy + + | :sl:`Returns a copy of itself.` + | :sg:`copy() -> Vector3` + + Returns a new Vector3 having the same dimensions. + + .. versionadded:: 2.1.1 + + .. ## Vector3.copy ## + + + .. method:: clamp_magnitude + + | :sl:`Returns a copy of a vector with the magnitude clamped between max_length and min_length.` + | :sg:`clamp_magnitude(max_length) -> Vector3` + | :sg:`clamp_magnitude(min_length, max_length) -> Vector3` + + **Experimental:** feature still in development available for testing and feedback. It may change. + `Please leave clamp_magnitude feedback with authors `_ + + Returns a new copy of a vector with the magnitude clamped between + ``max_length`` and ``min_length``. If only one argument is passed, it is + taken to be the ``max_length`` + + This function raises ``ValueError`` if ``min_length`` is greater than + ``max_length``, or if either of these values are negative. + + .. versionadded:: 2.1.3 + + .. ## Vector3.clamp_magnitude ## + + + .. method:: clamp_magnitude_ip + + | :sl:`Clamps the vector's magnitude between max_length and min_length` + | :sg:`clamp_magnitude_ip(max_length) -> None` + | :sg:`clamp_magnitude_ip(min_length, max_length) -> None` + + **Experimental:** feature still in development available for testing and feedback. It may change. + `Please leave clamp_magnitude_ip feedback with authors `_ + + Clamps the vector's magnitude between ``max_length`` and ``min_length``. + If only one argument is passed, it is taken to be the ``max_length`` + + This function raises ``ValueError`` if ``min_length`` is greater than + ``max_length``, or if either of these values are negative. + + .. versionadded:: 2.1.3 + + .. ## Vector3.clamp_magnitude_ip ## + + .. method:: update + + | :sl:`Sets the coordinates of the vector.` + | :sg:`update() -> None` + | :sg:`update(int) -> None` + | :sg:`update(float) -> None` + | :sg:`update(Vector3) -> None` + | :sg:`update(x, y, z) -> None` + | :sg:`update((x, y, z)) -> None` + + Sets coordinates x, y, and z in place. + + .. versionadded:: 1.9.5 + + .. ## Vector3.update ## + + .. attribute:: epsilon + + | :sl:`Determines the tolerance of vector calculations.` + + With lengths within this number, vectors are considered equal. For more information see :attr:`pygame.math.Vector2.epsilon` + + .. ## ## + + .. ## pygame.math.Vector3 ## + +.. ## pygame.math ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/midi.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/midi.rst.txt new file mode 100644 index 00000000..edc9f252 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/midi.rst.txt @@ -0,0 +1,484 @@ +.. include:: common.txt + +:mod:`pygame.midi` +================== + +.. module:: pygame.midi + :synopsis: pygame module for interacting with midi input and output. + +| :sl:`pygame module for interacting with midi input and output.` + +.. versionadded:: 1.9.0 + +The midi module can send output to midi devices and get input from midi +devices. It can also list midi devices on the system. + +The midi module supports real and virtual midi devices. + +It uses the portmidi library. Is portable to which ever platforms portmidi +supports (currently Windows, Mac OS X, and Linux). + +This uses pyportmidi for now, but may use its own bindings at some point in the +future. The pyportmidi bindings are included with pygame. + +| + +.. versionadded:: 2.0.0 + +These are pygame events (:mod:`pygame.event`) reserved for midi use. The +``MIDIIN`` event is used by :func:`pygame.midi.midis2events` when converting +midi events to pygame events. + +:: + + MIDIIN + MIDIOUT + +| + +.. function:: init + + | :sl:`initialize the midi module` + | :sg:`init() -> None` + + Initializes the :mod:`pygame.midi` module. Must be called before using the + :mod:`pygame.midi` module. + + It is safe to call this more than once. + + .. ## pygame.midi.init ## + +.. function:: quit + + | :sl:`uninitialize the midi module` + | :sg:`quit() -> None` + + Uninitializes the :mod:`pygame.midi` module. If :func:`pygame.midi.init` was + called to initialize the :mod:`pygame.midi` module, then this function will + be called automatically when your program exits. + + It is safe to call this function more than once. + + .. ## pygame.midi.quit ## + +.. function:: get_init + + | :sl:`returns True if the midi module is currently initialized` + | :sg:`get_init() -> bool` + + Gets the initialization state of the :mod:`pygame.midi` module. + + :returns: ``True`` if the :mod:`pygame.midi` module is currently initialized. + :rtype: bool + + .. versionadded:: 1.9.5 + + .. ## pygame.midi.get_init ## + +.. class:: Input + + | :sl:`Input is used to get midi input from midi devices.` + | :sg:`Input(device_id) -> None` + | :sg:`Input(device_id, buffer_size) -> None` + + :param int device_id: midi device id + :param int buffer_size: (optional) the number of input events to be buffered + + .. method:: close + + | :sl:`closes a midi stream, flushing any pending buffers.` + | :sg:`close() -> None` + + PortMidi attempts to close open streams when the application exits. + + .. note:: This is particularly difficult under Windows. + + .. ## Input.close ## + + .. method:: poll + + | :sl:`returns True if there's data, or False if not.` + | :sg:`poll() -> bool` + + Used to indicate if any data exists. + + :returns: ``True`` if there is data, ``False`` otherwise + :rtype: bool + + :raises MidiException: on error + + .. ## Input.poll ## + + .. method:: read + + | :sl:`reads num_events midi events from the buffer.` + | :sg:`read(num_events) -> midi_event_list` + + Reads from the input buffer and gives back midi events. + + :param int num_events: number of input events to read + + :returns: the format for midi_event_list is + ``[[[status, data1, data2, data3], timestamp], ...]`` + :rtype: list + + .. ## Input.read ## + + .. ## pygame.midi.Input ## + +.. class:: Output + + | :sl:`Output is used to send midi to an output device` + | :sg:`Output(device_id) -> None` + | :sg:`Output(device_id, latency=0) -> None` + | :sg:`Output(device_id, buffer_size=256) -> None` + | :sg:`Output(device_id, latency, buffer_size) -> None` + + The ``buffer_size`` specifies the number of output events to be buffered + waiting for output. In some cases (see below) PortMidi does not buffer + output at all and merely passes data to a lower-level API, in which case + buffersize is ignored. + + ``latency`` is the delay in milliseconds applied to timestamps to determine + when the output should actually occur. If ``latency`` is <<0, 0 is assumed. + + If ``latency`` is zero, timestamps are ignored and all output is delivered + immediately. If ``latency`` is greater than zero, output is delayed until the + message timestamp plus the ``latency``. In some cases, PortMidi can obtain + better timing than your application by passing timestamps along to the + device driver or hardware. Latency may also help you to synchronize midi + data to audio data by matching midi latency to the audio buffer latency. + + .. note:: + Time is measured relative to the time source indicated by time_proc. + Timestamps are absolute, not relative delays or offsets. + + .. method:: abort + + | :sl:`terminates outgoing messages immediately` + | :sg:`abort() -> None` + + The caller should immediately close the output port; this call may result + in transmission of a partial midi message. There is no abort for Midi + input because the user can simply ignore messages in the buffer and close + an input device at any time. + + .. ## Output.abort ## + + .. method:: close + + | :sl:`closes a midi stream, flushing any pending buffers.` + | :sg:`close() -> None` + + PortMidi attempts to close open streams when the application exits. + + .. note:: This is particularly difficult under Windows. + + .. ## Output.close ## + + .. method:: note_off + + | :sl:`turns a midi note off (note must be on)` + | :sg:`note_off(note, velocity=None, channel=0) -> None` + + Turn a note off in the output stream. The note must already be on for + this to work correctly. + + .. ## Output.note_off ## + + .. method:: note_on + + | :sl:`turns a midi note on (note must be off)` + | :sg:`note_on(note, velocity=None, channel=0) -> None` + + Turn a note on in the output stream. The note must already be off for + this to work correctly. + + .. ## Output.note_on ## + + .. method:: set_instrument + + | :sl:`select an instrument, with a value between 0 and 127` + | :sg:`set_instrument(instrument_id, channel=0) -> None` + + Select an instrument. + + .. ## Output.set_instrument ## + + .. method:: pitch_bend + + | :sl:`modify the pitch of a channel.` + | :sg:`set_instrument(value=0, channel=0) -> None` + + Adjust the pitch of a channel. The value is a signed integer + from -8192 to +8191. For example, 0 means "no change", +4096 is + typically a semitone higher, and -8192 is 1 whole tone lower (though + the musical range corresponding to the pitch bend range can also be + changed in some synthesizers). + + If no value is given, the pitch bend is returned to "no change". + + .. versionadded:: 1.9.4 + + .. method:: write + + | :sl:`writes a list of midi data to the Output` + | :sg:`write(data) -> None` + + Writes series of MIDI information in the form of a list. + + :param list data: data to write, the expected format is + ``[[[status, data1=0, data2=0, ...], timestamp], ...]`` + with the ``data#`` fields being optional + + :raises IndexError: if more than 1024 elements in the data list + + Example: + :: + + # Program change at time 20000 and 500ms later send note 65 with + # velocity 100. + write([[[0xc0, 0, 0], 20000], [[0x90, 60, 100], 20500]]) + + .. note:: + - Timestamps will be ignored if latency = 0 + - To get a note to play immediately, send MIDI info with timestamp + read from function Time + - Optional data fields: ``write([[[0xc0, 0, 0], 20000]])`` is + equivalent to ``write([[[0xc0], 20000]])`` + + .. ## Output.write ## + + .. method:: write_short + + | :sl:`writes up to 3 bytes of midi data to the Output` + | :sg:`write_short(status) -> None` + | :sg:`write_short(status, data1=0, data2=0) -> None` + + Output MIDI information of 3 bytes or less. The ``data`` fields are + optional and assumed to be 0 if omitted. + + Examples of status byte values: + :: + + 0xc0 # program change + 0x90 # note on + # etc. + + Example: + :: + + # note 65 on with velocity 100 + write_short(0x90, 65, 100) + + .. ## Output.write_short ## + + .. method:: write_sys_ex + + | :sl:`writes a timestamped system-exclusive midi message.` + | :sg:`write_sys_ex(when, msg) -> None` + + Writes a timestamped system-exclusive midi message. + + :param msg: midi message + :type msg: list[int] or str + :param when: timestamp in milliseconds + + Example: + :: + + midi_output.write_sys_ex(0, '\xF0\x7D\x10\x11\x12\x13\xF7') + + # is equivalent to + + midi_output.write_sys_ex(pygame.midi.time(), + [0xF0, 0x7D, 0x10, 0x11, 0x12, 0x13, 0xF7]) + + .. ## Output.write_sys_ex ## + + .. ## pygame.midi.Output ## + +.. function:: get_count + + | :sl:`gets the number of devices.` + | :sg:`get_count() -> num_devices` + + Device ids range from 0 to ``get_count() - 1`` + + .. ## pygame.midi.get_count ## + +.. function:: get_default_input_id + + | :sl:`gets default input device number` + | :sg:`get_default_input_id() -> default_id` + + The following describes the usage details for this function and the + :func:`get_default_output_id` function. + + Return the default device ID or ``-1`` if there are no devices. The result + can be passed to the :class:`Input`/:class:`Output` class. + + On a PC the user can specify a default device by setting an environment + variable. To use device #1, for example: + :: + + set PM_RECOMMENDED_INPUT_DEVICE=1 + or + set PM_RECOMMENDED_OUTPUT_DEVICE=1 + + The user should first determine the available device ID by using the + supplied application "testin" or "testout". + + In general, the registry is a better place for this kind of info. With + USB devices that can come and go, using integers is not very reliable + for device identification. Under Windows, if ``PM_RECOMMENDED_INPUT_DEVICE`` + (or ``PM_RECOMMENDED_OUTPUT_DEVICE``) is NOT found in the environment, + then the default device is obtained by looking for a string in the registry + under: + :: + + HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Input_Device + or + HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Output_Device + + + The number of the first device with a substring that matches the + string exactly is returned. For example, if the string in the registry is + "USB" and device 1 is named "In USB MidiSport 1x1", then that will be + the default input because it contains the string "USB". + + In addition to the name, :func:`get_device_info()` returns "interf", which is + the interface name. The "interface" is the underlying software system or + API used by PortMidi to access devices. Supported interfaces: + :: + + MMSystem # the only Win32 interface currently supported + ALSA # the only Linux interface currently supported + CoreMIDI # the only Mac OS X interface currently supported + # DirectX - not implemented + # OSS - not implemented + + To specify both the interface and the device name in the registry, separate + the two with a comma and a space. The string before the comma must be a + substring of the "interf" string and the string after the space must be a + substring of the "name" name string in order to match the device. e.g.: + :: + + MMSystem, In USB MidiSport 1x1 + + .. note:: + In the current release, the default is simply the first device (the + input or output device with the lowest PmDeviceID). + + .. ## pygame.midi.get_default_input_id ## + +.. function:: get_default_output_id + + | :sl:`gets default output device number` + | :sg:`get_default_output_id() -> default_id` + + See :func:`get_default_input_id` for usage details. + + .. ## pygame.midi.get_default_output_id ## + +.. function:: get_device_info + + | :sl:`returns information about a midi device` + | :sg:`get_device_info(an_id) -> (interf, name, input, output, opened)` + | :sg:`get_device_info(an_id) -> None` + + Gets the device info for a given id. + + :param int an_id: id of the midi device being queried + + :returns: if the id is out of range ``None`` is returned, otherwise + a tuple of (interf, name, input, output, opened) is returned. + + - interf: string describing the device interface (e.g. 'ALSA') + - name: string name of the device (e.g. 'Midi Through Port-0') + - input: 1 if the device is an input device, otherwise 0 + - output: 1 if the device is an output device, otherwise 0 + - opened: 1 if the device is opened, otherwise 0 + :rtype: tuple or None + + .. ## pygame.midi.get_device_info ## + +.. function:: midis2events + + | :sl:`converts midi events to pygame events` + | :sg:`midis2events(midi_events, device_id) -> [Event, ...]` + + Takes a sequence of midi events and returns list of pygame events. + + The ``midi_events`` data is expected to be a sequence of + ``((status, data1, data2, data3), timestamp)`` midi events (all values + required). + + :returns: a list of pygame events of event type ``MIDIIN`` + :rtype: list + + .. ## pygame.midi.midis2events ## + +.. function:: time + + | :sl:`returns the current time in ms of the PortMidi timer` + | :sg:`time() -> time` + + The time is reset to 0 when the :mod:`pygame.midi` module is initialized. + + .. ## pygame.midi.time ## + + +.. function:: frequency_to_midi + + | :sl:`Converts a frequency into a MIDI note. Rounds to the closest midi note.` + | :sg:`frequency_to_midi(midi_note) -> midi_note` + + example: + :: + + frequency_to_midi(27.5) == 21 + + .. versionadded:: 1.9.5 + + .. ## pygame.midi.frequency_to_midi ## + + +.. function:: midi_to_frequency + + | :sl:`Converts a midi note to a frequency.` + | :sg:`midi_to_frequency(midi_note) -> frequency` + + example: + :: + + midi_to_frequency(21) == 27.5 + + .. versionadded:: 1.9.5 + + .. ## pygame.midi.midi_to_frequency ## + + +.. function:: midi_to_ansi_note + + | :sl:`Returns the Ansi Note name for a midi number.` + | :sg:`midi_to_ansi_note(midi_note) -> ansi_note` + + example: + :: + + midi_to_ansi_note(21) == 'A0' + + .. versionadded:: 1.9.5 + + .. ## pygame.midi.midi_to_ansi_note ## + +.. exception:: MidiException + + | :sl:`exception that pygame.midi functions and classes can raise` + | :sg:`MidiException(errno) -> None` + + .. ## pygame.midi.MidiException ## + + +.. ## pygame.midi ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/mixer.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/mixer.rst.txt new file mode 100644 index 00000000..07bc7932 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/mixer.rst.txt @@ -0,0 +1,605 @@ +.. include:: common.txt + +:mod:`pygame.mixer` +=================== + +.. module:: pygame.mixer + :synopsis: pygame module for loading and playing sounds + +| :sl:`pygame module for loading and playing sounds` + +This module contains classes for loading Sound objects and controlling +playback. The mixer module is optional and depends on SDL_mixer. Your program +should test that :mod:`pygame.mixer` is available and initialized before using +it. + +The mixer module has a limited number of channels for playback of sounds. +Usually programs tell pygame to start playing audio and it selects an available +channel automatically. The default is 8 simultaneous channels, but complex +programs can get more precise control over the number of channels and their +use. + +All sound playback is mixed in background threads. When you begin to play a +Sound object, it will return immediately while the sound continues to play. A +single Sound object can also be actively played back multiple times. + +The mixer also has a special streaming channel. This is for music playback and +is accessed through the :mod:`pygame.mixer.music` module. Consider using this +module for playing long running music. Unlike mixer module, the music module +streams the music from the files without loading music at once into memory. + +The mixer module must be initialized like other pygame modules, but it has some +extra conditions. The ``pygame.mixer.init()`` function takes several optional +arguments to control the playback rate and sample size. Pygame will default to +reasonable values, but pygame cannot perform Sound resampling, so the mixer +should be initialized to match the values of your audio resources. + +``NOTE``: For less laggy sound use a smaller buffer size. The default +is set to reduce the chance of scratchy sounds on some computers. You can +change the default buffer by calling :func:`pygame.mixer.pre_init` before +:func:`pygame.mixer.init` or :func:`pygame.init` is called. For example: +``pygame.mixer.pre_init(44100,-16,2, 1024)`` + + +.. function:: init + + | :sl:`initialize the mixer module` + | :sg:`init(frequency=44100, size=-16, channels=2, buffer=512, devicename=None, allowedchanges=AUDIO_ALLOW_FREQUENCY_CHANGE | AUDIO_ALLOW_CHANNELS_CHANGE) -> None` + + Initialize the mixer module for Sound loading and playback. The default + arguments can be overridden to provide specific audio mixing. Keyword + arguments are accepted. For backwards compatibility, argument values of + 0 are replaced with the startup defaults, except for ``allowedchanges``, + where -1 is used. (startup defaults may be changed by a :func:`pre_init` call). + + The size argument represents how many bits are used for each audio sample. + If the value is negative then signed sample values will be used. Positive + values mean unsigned audio samples will be used. An invalid value raises an + exception. + + The channels argument is used to specify whether to use mono or stereo. 1 + for mono and 2 for stereo. + + The buffer argument controls the number of internal samples used in the + sound mixer. The default value should work for most cases. It can be lowered + to reduce latency, but sound dropout may occur. It can be raised to larger + values to ensure playback never skips, but it will impose latency on sound + playback. The buffer size must be a power of two (if not it is rounded up to + the next nearest power of 2). + + Some platforms require the :mod:`pygame.mixer` module to be initialized + after the display modules have initialized. The top level ``pygame.init()`` + takes care of this automatically, but cannot pass any arguments to the mixer + init. To solve this, mixer has a function ``pygame.mixer.pre_init()`` to set + the proper defaults before the toplevel init is used. + + When using allowedchanges=0 it will convert the samples at runtime to match + what the hardware supports. For example a sound card may not + support 16bit sound samples, so instead it will use 8bit samples internally. + If AUDIO_ALLOW_FORMAT_CHANGE is supplied, then the requested format will + change to the closest that SDL2 supports. + + Apart from 0, allowedchanged accepts the following constants ORed together: + + - AUDIO_ALLOW_FREQUENCY_CHANGE + - AUDIO_ALLOW_FORMAT_CHANGE + - AUDIO_ALLOW_CHANNELS_CHANGE + - AUDIO_ALLOW_ANY_CHANGE + + It is safe to call this more than once, but after the mixer is initialized + you cannot change the playback arguments without first calling + ``pygame.mixer.quit()``. + + .. versionchanged:: 1.8 The default ``buffersize`` changed from 1024 to 3072. + .. versionchanged:: 1.9.1 The default ``buffersize`` changed from 3072 to 4096. + .. versionchanged:: 2.0.0 The default ``buffersize`` changed from 4096 to 512. + .. versionchanged:: 2.0.0 The default ``frequency`` changed from 22050 to 44100. + .. versionchanged:: 2.0.0 ``size`` can be 32 (32-bit floats). + .. versionchanged:: 2.0.0 ``channels`` can also be 4 or 6. + .. versionadded:: 2.0.0 ``allowedchanges``, ``devicename`` arguments added + + .. ## pygame.mixer.init ## + +.. function:: pre_init + + | :sl:`preset the mixer init arguments` + | :sg:`pre_init(frequency=44100, size=-16, channels=2, buffer=512, devicename=None, allowedchanges=AUDIO_ALLOW_FREQUENCY_CHANGE | AUDIO_ALLOW_CHANNELS_CHANGE) -> None` + + Call pre_init to change the defaults used when the real + ``pygame.mixer.init()`` is called. Keyword arguments are accepted. The best + way to set custom mixer playback values is to call + ``pygame.mixer.pre_init()`` before calling the top level ``pygame.init()``. + For backwards compatibility, argument values of 0 are replaced with the + startup defaults, except for ``allowedchanges``, where -1 is used. + + .. versionchanged:: 1.8 The default ``buffersize`` changed from 1024 to 3072. + .. versionchanged:: 1.9.1 The default ``buffersize`` changed from 3072 to 4096. + .. versionchanged:: 2.0.0 The default ``buffersize`` changed from 4096 to 512. + .. versionchanged:: 2.0.0 The default ``frequency`` changed from 22050 to 44100. + .. versionadded:: 2.0.0 ``allowedchanges``, ``devicename`` arguments added + + .. ## pygame.mixer.pre_init ## + +.. function:: quit + + | :sl:`uninitialize the mixer` + | :sg:`quit() -> None` + + This will uninitialize :mod:`pygame.mixer`. All playback will stop and any + loaded Sound objects may not be compatible with the mixer if it is + reinitialized later. + + .. ## pygame.mixer.quit ## + +.. function:: get_init + + | :sl:`test if the mixer is initialized` + | :sg:`get_init() -> (frequency, format, channels)` + + If the mixer is initialized, this returns the playback arguments it is + using. If the mixer has not been initialized this returns ``None``. + + .. ## pygame.mixer.get_init ## + +.. function:: stop + + | :sl:`stop playback of all sound channels` + | :sg:`stop() -> None` + + This will stop all playback of all active mixer channels. + + .. ## pygame.mixer.stop ## + +.. function:: pause + + | :sl:`temporarily stop playback of all sound channels` + | :sg:`pause() -> None` + + This will temporarily stop all playback on the active mixer channels. The + playback can later be resumed with ``pygame.mixer.unpause()`` + + .. ## pygame.mixer.pause ## + +.. function:: unpause + + | :sl:`resume paused playback of sound channels` + | :sg:`unpause() -> None` + + This will resume all active sound channels after they have been paused. + + .. ## pygame.mixer.unpause ## + +.. function:: fadeout + + | :sl:`fade out the volume on all sounds before stopping` + | :sg:`fadeout(time) -> None` + + This will fade out the volume on all active channels over the time argument + in milliseconds. After the sound is muted the playback will stop. + + .. ## pygame.mixer.fadeout ## + +.. function:: set_num_channels + + | :sl:`set the total number of playback channels` + | :sg:`set_num_channels(count) -> None` + + Sets the number of available channels for the mixer. The default value is 8. + The value can be increased or decreased. If the value is decreased, sounds + playing on the truncated channels are stopped. + + .. ## pygame.mixer.set_num_channels ## + +.. function:: get_num_channels + + | :sl:`get the total number of playback channels` + | :sg:`get_num_channels() -> count` + + Returns the number of currently active playback channels. + + .. ## pygame.mixer.get_num_channels ## + +.. function:: set_reserved + + | :sl:`reserve channels from being automatically used` + | :sg:`set_reserved(count) -> count` + + The mixer can reserve any number of channels that will not be automatically + selected for playback by Sounds. This means that whenever you play a Sound + without specifying a channel, a reserved channel will never be used. If sounds + are currently playing on the reserved channels they will not be stopped. + + This allows the application to reserve a specific number of channels for + important sounds that must not be dropped or have a guaranteed channel to + play on. + + Will return number of channels actually reserved, this may be less than requested + depending on the number of channels previously allocated. + + .. ## pygame.mixer.set_reserved ## + +.. function:: find_channel + + | :sl:`find an unused channel` + | :sg:`find_channel(force=False) -> Channel` + + This will find and return an inactive Channel object. If there are no + inactive Channels this function will return ``None``. If there are no + inactive channels and the force argument is ``True``, this will find the + Channel with the longest running Sound and return it. + + .. ## pygame.mixer.find_channel ## + +.. function:: get_busy + + | :sl:`test if any sound is being mixed` + | :sg:`get_busy() -> bool` + + Returns ``True`` if the mixer is busy mixing any channels. If the mixer is + idle then this return ``False``. + + .. ## pygame.mixer.get_busy ## + +.. function:: get_sdl_mixer_version + + | :sl:`get the mixer's SDL version` + | :sg:`get_sdl_mixer_version() -> (major, minor, patch)` + | :sg:`get_sdl_mixer_version(linked=True) -> (major, minor, patch)` + + :param bool linked: if ``True`` (default) the linked version number is + returned, otherwise the compiled version number is returned + + :returns: the mixer's SDL library version number (linked or compiled + depending on the ``linked`` parameter) as a tuple of 3 integers + ``(major, minor, patch)`` + :rtype: tuple + + .. note:: + The linked and compile version numbers should be the same. + + .. versionadded:: 2.0.0 + + .. ## pygame.mixer.get_sdl_mixer_version ## + +.. class:: Sound + + | :sl:`Create a new Sound object from a file or buffer object` + | :sg:`Sound(filename) -> Sound` + | :sg:`Sound(file=filename) -> Sound` + | :sg:`Sound(file=pathlib_path) -> Sound` + | :sg:`Sound(buffer) -> Sound` + | :sg:`Sound(buffer=buffer) -> Sound` + | :sg:`Sound(object) -> Sound` + | :sg:`Sound(file=object) -> Sound` + | :sg:`Sound(array=object) -> Sound` + + Load a new sound buffer from a filename, a python file object or a readable + buffer object. Limited resampling will be performed to help the sample match + the initialize arguments for the mixer. A Unicode string can only be a file + pathname. A bytes object can be either a pathname or a buffer object. + Use the 'file' or 'buffer' keywords to avoid ambiguity; otherwise Sound may + guess wrong. If the array keyword is used, the object is expected to export + a new buffer interface (The object is checked for a buffer interface first.) + + The Sound object represents actual sound sample data. Methods that change + the state of the Sound object will the all instances of the Sound playback. + A Sound object also exports a new buffer interface. + + The Sound can be loaded from an ``OGG`` audio file or from an uncompressed + ``WAV``. + + Note: The buffer will be copied internally, no data will be shared between + it and the Sound object. + + For now buffer and array support is consistent with ``sndarray.make_sound`` + for Numeric arrays, in that sample sign and byte order are ignored. This + will change, either by correctly handling sign and byte order, or by raising + an exception when different. Also, source samples are truncated to fit the + audio sample size. This will not change. + + .. versionadded:: 1.8 ``pygame.mixer.Sound(buffer)`` + .. versionadded:: 1.9.2 + :class:`pygame.mixer.Sound` keyword arguments and array interface support + .. versionadded:: 2.0.1 pathlib.Path support on Python 3. + + .. method:: play + + | :sl:`begin sound playback` + | :sg:`play(loops=0, maxtime=0, fade_ms=0) -> Channel` + + Begin playback of the Sound (i.e., on the computer's speakers) on an + available Channel. This will forcibly select a Channel, so playback may + cut off a currently playing sound if necessary. + + The loops argument controls how many times the sample will be repeated + after being played the first time. A value of 5 means that the sound will + be played once, then repeated five times, and so is played a total of six + times. The default value (zero) means the Sound is not repeated, and so + is only played once. If loops is set to -1 the Sound will loop + indefinitely (though you can still call ``stop()`` to stop it). + + The maxtime argument can be used to stop playback after a given number of + milliseconds. + + The fade_ms argument will make the sound start playing at 0 volume and + fade up to full volume over the time given. The sample may end before the + fade-in is complete. + + This returns the Channel object for the channel that was selected. + + .. ## Sound.play ## + + .. method:: stop + + | :sl:`stop sound playback` + | :sg:`stop() -> None` + + This will stop the playback of this Sound on any active Channels. + + .. ## Sound.stop ## + + .. method:: fadeout + + | :sl:`stop sound playback after fading out` + | :sg:`fadeout(time) -> None` + + This will stop playback of the sound after fading it out over the time + argument in milliseconds. The Sound will fade and stop on all actively + playing channels. + + .. ## Sound.fadeout ## + + .. method:: set_volume + + | :sl:`set the playback volume for this Sound` + | :sg:`set_volume(value) -> None` + + This will set the playback volume (loudness) for this Sound. This will + immediately affect the Sound if it is playing. It will also affect any + future playback of this Sound. + + :param float value: volume in the range of 0.0 to 1.0 (inclusive) + + | If value < 0.0, the volume will not be changed + | If value > 1.0, the volume will be set to 1.0 + + .. ## Sound.set_volume ## + + .. method:: get_volume + + | :sl:`get the playback volume` + | :sg:`get_volume() -> value` + + Return a value from 0.0 to 1.0 representing the volume for this Sound. + + .. ## Sound.get_volume ## + + .. method:: get_num_channels + + | :sl:`count how many times this Sound is playing` + | :sg:`get_num_channels() -> count` + + Return the number of active channels this sound is playing on. + + .. ## Sound.get_num_channels ## + + .. method:: get_length + + | :sl:`get the length of the Sound` + | :sg:`get_length() -> seconds` + + Return the length of this Sound in seconds. + + .. ## Sound.get_length ## + + .. method:: get_raw + + | :sl:`return a bytestring copy of the Sound samples.` + | :sg:`get_raw() -> bytes` + + Return a copy of the Sound object buffer as a bytes. + + .. versionadded:: 1.9.2 + + .. ## Sound.get_raw ## + + .. ## pygame.mixer.Sound ## + +.. class:: Channel + + | :sl:`Create a Channel object for controlling playback` + | :sg:`Channel(id) -> Channel` + + Return a Channel object for one of the current channels. The id must be a + value from 0 to the value of ``pygame.mixer.get_num_channels()``. + + The Channel object can be used to get fine control over the playback of + Sounds. A channel can only playback a single Sound at time. Using channels + is entirely optional since pygame can manage them by default. + + .. method:: play + + | :sl:`play a Sound on a specific Channel` + | :sg:`play(Sound, loops=0, maxtime=0, fade_ms=0) -> None` + + This will begin playback of a Sound on a specific Channel. If the Channel + is currently playing any other Sound it will be stopped. + + The loops argument has the same meaning as in ``Sound.play()``: it is the + number of times to repeat the sound after the first time. If it is 3, the + sound will be played 4 times (the first time, then three more). If loops + is -1 then the playback will repeat indefinitely. + + As in ``Sound.play()``, the maxtime argument can be used to stop playback + of the Sound after a given number of milliseconds. + + As in ``Sound.play()``, the fade_ms argument can be used fade in the + sound. + + .. ## Channel.play ## + + .. method:: stop + + | :sl:`stop playback on a Channel` + | :sg:`stop() -> None` + + Stop sound playback on a channel. After playback is stopped the channel + becomes available for new Sounds to play on it. + + .. ## Channel.stop ## + + .. method:: pause + + | :sl:`temporarily stop playback of a channel` + | :sg:`pause() -> None` + + Temporarily stop the playback of sound on a channel. It can be resumed at + a later time with ``Channel.unpause()`` + + .. ## Channel.pause ## + + .. method:: unpause + + | :sl:`resume pause playback of a channel` + | :sg:`unpause() -> None` + + Resume the playback on a paused channel. + + .. ## Channel.unpause ## + + .. method:: fadeout + + | :sl:`stop playback after fading channel out` + | :sg:`fadeout(time) -> None` + + Stop playback of a channel after fading out the sound over the given time + argument in milliseconds. + + .. ## Channel.fadeout ## + + .. method:: set_volume + + | :sl:`set the volume of a playing channel` + | :sg:`set_volume(value) -> None` + | :sg:`set_volume(left, right) -> None` + + Set the volume (loudness) of a playing sound. When a channel starts to + play its volume value is reset. This only affects the current sound. The + value argument is between 0.0 and 1.0. + + If one argument is passed, it will be the volume of both speakers. If two + arguments are passed and the mixer is in stereo mode, the first argument + will be the volume of the left speaker and the second will be the volume + of the right speaker. (If the second argument is ``None``, the first + argument will be the volume of both speakers.) + + If the channel is playing a Sound on which ``set_volume()`` has also been + called, both calls are taken into account. For example: + + :: + + sound = pygame.mixer.Sound("s.wav") + channel = s.play() # Sound plays at full volume by default + sound.set_volume(0.9) # Now plays at 90% of full volume. + sound.set_volume(0.6) # Now plays at 60% (previous value replaced). + channel.set_volume(0.5) # Now plays at 30% (0.6 * 0.5). + + .. ## Channel.set_volume ## + + .. method:: get_volume + + | :sl:`get the volume of the playing channel` + | :sg:`get_volume() -> value` + + Return the volume of the channel for the current playing sound. This does + not take into account stereo separation used by + :meth:`Channel.set_volume`. The Sound object also has its own volume + which is mixed with the channel. + + .. ## Channel.get_volume ## + + .. method:: get_busy + + | :sl:`check if the channel is active` + | :sg:`get_busy() -> bool` + + Returns ``True`` if the channel is actively mixing sound. If the channel + is idle this returns ``False``. + + .. ## Channel.get_busy ## + + .. method:: get_sound + + | :sl:`get the currently playing Sound` + | :sg:`get_sound() -> Sound` + + Return the actual Sound object currently playing on this channel. If the + channel is idle ``None`` is returned. + + .. ## Channel.get_sound ## + + .. method:: queue + + | :sl:`queue a Sound object to follow the current` + | :sg:`queue(Sound) -> None` + + When a Sound is queued on a Channel, it will begin playing immediately + after the current Sound is finished. Each channel can only have a single + Sound queued at a time. The queued Sound will only play if the current + playback finished automatically. It is cleared on any other call to + ``Channel.stop()`` or ``Channel.play()``. + + If there is no sound actively playing on the Channel then the Sound will + begin playing immediately. + + .. ## Channel.queue ## + + .. method:: get_queue + + | :sl:`return any Sound that is queued` + | :sg:`get_queue() -> Sound` + + If a Sound is already queued on this channel it will be returned. Once + the queued sound begins playback it will no longer be on the queue. + + .. ## Channel.get_queue ## + + .. method:: set_endevent + + | :sl:`have the channel send an event when playback stops` + | :sg:`set_endevent() -> None` + | :sg:`set_endevent(type) -> None` + + When an endevent is set for a channel, it will send an event to the + pygame queue every time a sound finishes playing on that channel (not + just the first time). Use ``pygame.event.get()`` to retrieve the endevent + once it's sent. + + Note that if you called ``Sound.play(n)`` or ``Channel.play(sound,n)``, + the end event is sent only once: after the sound has been played "n+1" + times (see the documentation of Sound.play). + + If ``Channel.stop()`` or ``Channel.play()`` is called while the sound was + still playing, the event will be posted immediately. + + The type argument will be the event id sent to the queue. This can be any + valid event type, but a good choice would be a value between + ``pygame.locals.USEREVENT`` and ``pygame.locals.NUMEVENTS``. If no type + argument is given then the Channel will stop sending endevents. + + .. ## Channel.set_endevent ## + + .. method:: get_endevent + + | :sl:`get the event a channel sends when playback stops` + | :sg:`get_endevent() -> type` + + Returns the event type to be sent every time the Channel finishes + playback of a Sound. If there is no endevent the function returns + ``pygame.NOEVENT``. + + .. ## Channel.get_endevent ## + + .. ## pygame.mixer.Channel ## + +.. ## pygame.mixer ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/mouse.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/mouse.rst.txt new file mode 100644 index 00000000..dda6c7bd --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/mouse.rst.txt @@ -0,0 +1,219 @@ +.. include:: common.txt + +:mod:`pygame.mouse` +=================== + +.. module:: pygame.mouse + :synopsis: pygame module to work with the mouse + +| :sl:`pygame module to work with the mouse` + +The mouse functions can be used to get the current state of the mouse device. +These functions can also alter the system cursor for the mouse. + +When the display mode is set, the event queue will start receiving mouse +events. The mouse buttons generate ``pygame.MOUSEBUTTONDOWN`` and +``pygame.MOUSEBUTTONUP`` events when they are pressed and released. These +events contain a button attribute representing which button was pressed. The +mouse wheel will generate ``pygame.MOUSEBUTTONDOWN`` and +``pygame.MOUSEBUTTONUP`` events when rolled. The button will be set to 4 +when the wheel is rolled up, and to button 5 when the wheel is rolled down. +Whenever the mouse is moved it generates a ``pygame.MOUSEMOTION`` event. The +mouse movement is broken into small and accurate motion events. As the mouse +is moving many motion events will be placed on the queue. Mouse motion events +that are not properly cleaned from the event queue are the primary reason the +event queue fills up. + +If the mouse cursor is hidden, and input is grabbed to the current display the +mouse will enter a virtual input mode, where the relative movements of the +mouse will never be stopped by the borders of the screen. See the functions +``pygame.mouse.set_visible()`` and ``pygame.event.set_grab()`` to get this +configured. + + +**Mouse Wheel Behavior in pygame 2** + +There is proper functionality for mouse wheel behaviour with pygame 2 supporting +``pygame.MOUSEWHEEL`` events. The new events support horizontal and vertical +scroll movements, with signed integer values representing the amount scrolled +(``x`` and ``y``), as well as ``flipped`` direction (the set positive and +negative values for each axis is flipped). Read more about SDL2 +input-related changes here ``_ + +In pygame 2, the mouse wheel functionality can be used by listening for the +``pygame.MOUSEWHEEL`` type of an event (Bear in mind they still emit +``pygame.MOUSEBUTTONDOWN`` events like in pygame 1.x, as well). +When this event is triggered, a developer can access the appropriate ``Event`` object +with ``pygame.event.get()``. The object can be used to access data about the mouse +scroll, such as ``which`` (it will tell you what exact mouse device trigger the event). + +.. code-block:: python + :caption: Code example of mouse scroll (tested on 2.0.0.dev7) + :name: test.py + + # Taken from husano896's PR thread (slightly modified) + import pygame + from pygame.locals import * + pygame.init() + screen = pygame.display.set_mode((640, 480)) + clock = pygame.time.Clock() + + def main(): + while True: + for event in pygame.event.get(): + if event.type == QUIT: + pygame.quit() + return + elif event.type == MOUSEWHEEL: + print(event) + print(event.x, event.y) + print(event.flipped) + print(event.which) + # can access properties with + # proper notation(ex: event.y) + clock.tick(60) + + # Execute game: + main() + +.. function:: get_pressed + + | :sl:`get the state of the mouse buttons` + | :sg:`get_pressed(num_buttons=3) -> (button1, button2, button3)` + | :sg:`get_pressed(num_buttons=5) -> (button1, button2, button3, button4, button5)` + + Returns a sequence of booleans representing the state of all the mouse + buttons. A true value means the mouse is currently being pressed at the time + of the call. + + Note, to get all of the mouse events it is better to use either + ``pygame.event.wait()`` or ``pygame.event.get()`` and check all of those + events to see if they are ``MOUSEBUTTONDOWN``, ``MOUSEBUTTONUP``, or + ``MOUSEMOTION``. + + Note, that on ``X11`` some X servers use middle button emulation. When you + click both buttons ``1`` and ``3`` at the same time a ``2`` button event + can be emitted. + + Note, remember to call ``pygame.event.get()`` before this function. + Otherwise it will not work as expected. + + To support five button mice, an optional parameter ``num_buttons`` has been + added in pygame 2. When this is set to ``5``, ``button4`` and ``button5`` + are added to the returned tuple. Only ``3`` and ``5`` are valid values + for this parameter. + + .. versionchanged:: 2.0.0 ``num_buttons`` argument added + + .. ## pygame.mouse.get_pressed ## + +.. function:: get_pos + + | :sl:`get the mouse cursor position` + | :sg:`get_pos() -> (x, y)` + + Returns the ``x`` and ``y`` position of the mouse cursor. The position is + relative to the top-left corner of the display. The cursor position can be + located outside of the display window, but is always constrained to the + screen. + + .. ## pygame.mouse.get_pos ## + +.. function:: get_rel + + | :sl:`get the amount of mouse movement` + | :sg:`get_rel() -> (x, y)` + + Returns the amount of movement in ``x`` and ``y`` since the previous call to + this function. The relative movement of the mouse cursor is constrained to + the edges of the screen, but see the virtual input mouse mode for a way + around this. Virtual input mode is described at the top of the page. + + .. ## pygame.mouse.get_rel ## + +.. function:: set_pos + + | :sl:`set the mouse cursor position` + | :sg:`set_pos([x, y]) -> None` + + Set the current mouse position to arguments given. If the mouse cursor is + visible it will jump to the new coordinates. Moving the mouse will generate + a new ``pygame.MOUSEMOTION`` event. + + .. ## pygame.mouse.set_pos ## + +.. function:: set_visible + + | :sl:`hide or show the mouse cursor` + | :sg:`set_visible(bool) -> bool` + + If the bool argument is true, the mouse cursor will be visible. This will + return the previous visible state of the cursor. + + .. ## pygame.mouse.set_visible ## + +.. function:: get_visible + + | :sl:`get the current visibility state of the mouse cursor` + | :sg:`get_visible() -> bool` + + Get the current visibility state of the mouse cursor. ``True`` if the mouse is + visible, ``False`` otherwise. + + .. versionadded:: 2.0.0 + + .. ## pygame.mouse.get_visible ## + +.. function:: get_focused + + | :sl:`check if the display is receiving mouse input` + | :sg:`get_focused() -> bool` + + Returns true when pygame is receiving mouse input events (or, in windowing + terminology, is "active" or has the "focus"). + + This method is most useful when working in a window. By contrast, in + full-screen mode, this method always returns true. + + Note: under ``MS`` Windows, the window that has the mouse focus also has the + keyboard focus. But under X-Windows, one window can receive mouse events and + another receive keyboard events. ``pygame.mouse.get_focused()`` indicates + whether the pygame window receives mouse events. + + .. ## pygame.mouse.get_focused ## + +.. function:: set_cursor + + | :sl:`set the mouse cursor to a new cursor` + | :sg:`set_cursor(pygame.cursors.Cursor) -> None` + | :sg:`set_cursor(size, hotspot, xormasks, andmasks) -> None` + | :sg:`set_cursor(hotspot, surface) -> None` + | :sg:`set_cursor(constant) -> None` + + Set the mouse cursor to something new. This function accepts either an explicit + ``Cursor`` object or arguments to create a ``Cursor`` object. + + See :class:`pygame.cursors.Cursor` for help creating cursors and for examples. + + .. versionchanged:: 2.0.1 + + .. ## pygame.mouse.set_cursor ## + + +.. function:: get_cursor + + | :sl:`get the current mouse cursor` + | :sg:`get_cursor() -> pygame.cursors.Cursor` + + Get the information about the mouse system cursor. The return value contains + the same data as the arguments passed into :func:`pygame.mouse.set_cursor()`. + + .. note:: Code that unpacked a get_cursor() call into + ``size, hotspot, xormasks, andmasks`` will still work, + assuming the call returns an old school type cursor. + + .. versionchanged:: 2.0.1 + + .. ## pygame.mouse.get_cursor ## + +.. ## pygame.mouse ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/music.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/music.rst.txt new file mode 100644 index 00000000..96a7b815 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/music.rst.txt @@ -0,0 +1,274 @@ +.. include:: common.txt + +:mod:`pygame.mixer.music` +========================= + +.. module:: pygame.mixer.music + :synopsis: pygame module for controlling streamed audio + +| :sl:`pygame module for controlling streamed audio` + +The music module is closely tied to :mod:`pygame.mixer`. Use the music module +to control the playback of music in the sound mixer. + +The difference between the music playback and regular Sound playback is that +the music is streamed, and never actually loaded all at once. The mixer system +only supports a single music stream at once. + +On older pygame versions, ``MP3`` support was limited under Mac and Linux. This +changed in pygame ``v2.0.2`` which got improved MP3 support. Consider using +``OGG`` file format for music as that can give slightly better compression than +MP3 in most cases. + +.. function:: load + + | :sl:`Load a music file for playback` + | :sg:`load(filename) -> None` + | :sg:`load(fileobj, namehint="") -> None` + + This will load a music filename/file object and prepare it for playback. If + a music stream is already playing it will be stopped. This does not start + the music playing. + + If you are loading from a file object, the namehint parameter can be used to specify + the type of music data in the object. For example: :code:`load(fileobj, "ogg")`. + + .. versionchanged:: 2.0.2 Added optional ``namehint`` argument + + .. ## pygame.mixer.music.load ## + +.. function:: unload + + | :sl:`Unload the currently loaded music to free up resources` + | :sg:`unload() -> None` + + This closes resources like files for any music that may be loaded. + + .. versionadded:: 2.0.0 + + .. ## pygame.mixer.music.load ## + + +.. function:: play + + | :sl:`Start the playback of the music stream` + | :sg:`play(loops=0, start=0.0, fade_ms=0) -> None` + + This will play the loaded music stream. If the music is already playing it + will be restarted. + + ``loops`` is an optional integer argument, which is ``0`` by default, which + indicates how many times to repeat the music. The music repeats indefinitely if + this argument is set to ``-1``. + + ``start`` is an optional float argument, which is ``0.0`` by default, which + denotes the position in time from which the music starts playing. The starting + position depends on the format of the music played. ``MP3`` and ``OGG`` use + the position as time in seconds. For ``MP3`` files the start time position + selected may not be accurate as things like variable bit rate encoding and ID3 + tags can throw off the timing calculations. For ``MOD`` music it is the pattern + order number. Passing a start position will raise a NotImplementedError if + the start position cannot be set. + + ``fade_ms`` is an optional integer argument, which is ``0`` by default, + which denotes the period of time (in milliseconds) over which the music + will fade up from volume level ``0.0`` to full volume (or the volume level + previously set by :func:`set_volume`). The sample may end before the fade-in + is complete. If the music is already streaming ``fade_ms`` is ignored. + + .. versionchanged:: 2.0.0 Added optional ``fade_ms`` argument + + .. ## pygame.mixer.music.play ## + +.. function:: rewind + + | :sl:`restart music` + | :sg:`rewind() -> None` + + Resets playback of the current music to the beginning. If :func:`pause` has + previously been used to pause the music, the music will remain paused. + + .. note:: :func:`rewind` supports a limited number of file types and notably + ``WAV`` files are NOT supported. For unsupported file types use :func:`play` + which will restart the music that's already playing (note that this + will start the music playing again even if previously paused). + + .. ## pygame.mixer.music.rewind ## + +.. function:: stop + + | :sl:`stop the music playback` + | :sg:`stop() -> None` + + Stops the music playback if it is currently playing. + endevent will be triggered, if set. + It won't unload the music. + + .. ## pygame.mixer.music.stop ## + +.. function:: pause + + | :sl:`temporarily stop music playback` + | :sg:`pause() -> None` + + Temporarily stop playback of the music stream. It can be resumed with the + :func:`unpause` function. + + .. ## pygame.mixer.music.pause ## + +.. function:: unpause + + | :sl:`resume paused music` + | :sg:`unpause() -> None` + + This will resume the playback of a music stream after it has been paused. + + .. ## pygame.mixer.music.unpause ## + +.. function:: fadeout + + | :sl:`stop music playback after fading out` + | :sg:`fadeout(time) -> None` + + Fade out and stop the currently playing music. + + The ``time`` argument denotes the integer milliseconds for which the + fading effect is generated. + + Note, that this function blocks until the music has faded out. Calls + to :func:`fadeout` and :func:`set_volume` will have no effect during + this time. If an event was set using :func:`set_endevent` it will be + called after the music has faded. + + .. ## pygame.mixer.music.fadeout ## + +.. function:: set_volume + + | :sl:`set the music volume` + | :sg:`set_volume(volume) -> None` + + Set the volume of the music playback. + + The ``volume`` argument is a float between ``0.0`` and ``1.0`` that sets + the volume level. When new music is loaded the volume is reset to full + volume. If ``volume`` is a negative value it will be ignored and the + volume will remain set at the current level. If the ``volume`` argument + is greater than ``1.0``, the volume will be set to ``1.0``. + + .. ## pygame.mixer.music.set_volume ## + +.. function:: get_volume + + | :sl:`get the music volume` + | :sg:`get_volume() -> value` + + Returns the current volume for the mixer. The value will be between ``0.0`` + and ``1.0``. + + .. ## pygame.mixer.music.get_volume ## + +.. function:: get_busy + + | :sl:`check if the music stream is playing` + | :sg:`get_busy() -> bool` + + Returns True when the music stream is actively playing. When the music is + idle this returns False. In pygame 2.0.1 and above this function returns + False when the music is paused. In pygame 1 it returns True when the music + is paused. + + .. versionchanged:: 2.0.1 Returns False when music paused. + + .. ## pygame.mixer.music.get_busy ## + +.. function:: set_pos + + | :sl:`set position to play from` + | :sg:`set_pos(pos) -> None` + + This sets the position in the music file where playback will start. + The meaning of "pos", a float (or a number that can be converted to a float), + depends on the music format. + + For ``MOD`` files, pos is the integer pattern number in the module. + For ``OGG`` it is the absolute position, in seconds, from + the beginning of the sound. For ``MP3`` files, it is the relative position, + in seconds, from the current position. For absolute positioning in an ``MP3`` + file, first call :func:`rewind`. + + Other file formats are unsupported. Newer versions of SDL_mixer have + better positioning support than earlier ones. An SDLError is raised if a + particular format does not support positioning. + + Function :func:`set_pos` calls underlining SDL_mixer function + ``Mix_SetMusicPosition``. + + .. versionadded:: 1.9.2 + + .. ## pygame.mixer.music.set_pos ## + +.. function:: get_pos + + | :sl:`get the music play time` + | :sg:`get_pos() -> time` + + This gets the number of milliseconds that the music has been playing for. + The returned time only represents how long the music has been playing; it + does not take into account any starting position offsets. + + .. ## pygame.mixer.music.get_pos ## + +.. function:: queue + + | :sl:`queue a sound file to follow the current` + | :sg:`queue(filename) -> None` + | :sg:`queue(fileobj, namehint="", loops=0) -> None` + + This will load a sound file and queue it. A queued sound file will begin as + soon as the current sound naturally ends. Only one sound can be queued at a + time. Queuing a new sound while another sound is queued will result in the + new sound becoming the queued sound. Also, if the current sound is ever + stopped or changed, the queued sound will be lost. + + If you are loading from a file object, the namehint parameter can be used to specify + the type of music data in the object. For example: :code:`queue(fileobj, "ogg")`. + + The following example will play music by Bach six times, then play music by + Mozart once: + + :: + + pygame.mixer.music.load('bach.ogg') + pygame.mixer.music.play(5) # Plays six times, not five! + pygame.mixer.music.queue('mozart.ogg') + + .. versionchanged:: 2.0.2 Added optional ``namehint`` argument + + .. ## pygame.mixer.music.queue ## + +.. function:: set_endevent + + | :sl:`have the music send an event when playback stops` + | :sg:`set_endevent() -> None` + | :sg:`set_endevent(type) -> None` + + This causes pygame to signal (by means of the event queue) when the music is + done playing. The argument determines the type of event that will be queued. + + The event will be queued every time the music finishes, not just the first + time. To stop the event from being queued, call this method with no + argument. + + .. ## pygame.mixer.music.set_endevent ## + +.. function:: get_endevent + + | :sl:`get the event a channel sends when playback stops` + | :sg:`get_endevent() -> type` + + Returns the event type to be sent every time the music finishes playback. If + there is no endevent the function returns ``pygame.NOEVENT``. + + .. ## pygame.mixer.music.get_endevent ## + +.. ## pygame.mixer.music ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/overlay.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/overlay.rst.txt new file mode 100644 index 00000000..04ff9ae1 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/overlay.rst.txt @@ -0,0 +1,79 @@ +.. include:: common.txt + +:mod:`pygame.Overlay` +===================== + +.. currentmodule:: pygame + +.. warning:: + This module is non functional in pygame 2.0 and above, unless you have manually compiled pygame with SDL1. + This module will not be supported in the future. + +.. class:: Overlay + + | :sl:`pygame object for video overlay graphics` + | :sg:`Overlay(format, (width, height)) -> Overlay` + + The Overlay objects provide support for accessing hardware video overlays. + Video overlays do not use standard ``RGB`` pixel formats, and can use + multiple resolutions of data to create a single image. + + The Overlay objects represent lower level access to the display hardware. To + use the object you must understand the technical details of video overlays. + + The Overlay format determines the type of pixel data used. Not all hardware + will support all types of overlay formats. Here is a list of available + format types: + + :: + + YV12_OVERLAY, IYUV_OVERLAY, YUY2_OVERLAY, UYVY_OVERLAY, YVYU_OVERLAY + + The width and height arguments control the size for the overlay image data. + The overlay image can be displayed at any size, not just the resolution of + the overlay. + + The overlay objects are always visible, and always show above the regular + display contents. + + .. method:: display + + | :sl:`set the overlay pixel data` + | :sg:`display((y, u, v)) -> None` + | :sg:`display() -> None` + + Display the YUV data in SDL's overlay planes. The y, u, and v arguments + are strings of binary data. The data must be in the correct format used + to create the Overlay. + + If no argument is passed in, the Overlay will simply be redrawn with the + current data. This can be useful when the Overlay is not really hardware + accelerated. + + The strings are not validated, and improperly sized strings could crash + the program. + + .. ## Overlay.display ## + + .. method:: set_location + + | :sl:`control where the overlay is displayed` + | :sg:`set_location(rect) -> None` + + Set the location for the overlay. The overlay will always be shown + relative to the main display Surface. This does not actually redraw the + overlay, it will be updated on the next call to ``Overlay.display()``. + + .. ## Overlay.set_location ## + + .. method:: get_hardware + + | :sl:`test if the Overlay is hardware accelerated` + | :sg:`get_hardware(rect) -> int` + + Returns a True value when the Overlay is hardware accelerated. If the + platform does not support acceleration, software rendering is used. + + .. ## Overlay.get_hardware ## + + .. ## pygame.Overlay ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/pixelarray.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/pixelarray.rst.txt new file mode 100644 index 00000000..9bdc38c0 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/pixelarray.rst.txt @@ -0,0 +1,295 @@ +.. include:: common.txt + +:class:`pygame.PixelArray` +========================== + +.. currentmodule:: pygame + +.. class:: PixelArray + + | :sl:`pygame object for direct pixel access of surfaces` + | :sg:`PixelArray(Surface) -> PixelArray` + + The PixelArray wraps a Surface and provides direct access to the + surface's pixels. A pixel array can be one or two dimensional. + A two dimensional array, like its surface, is indexed [column, row]. + Pixel arrays support slicing, both for returning a subarray or + for assignment. A pixel array sliced on a single column or row + returns a one dimensional pixel array. Arithmetic and other operations + are not supported. A pixel array can be safely assigned to itself. + Finally, pixel arrays export an array struct interface, allowing + them to interact with :mod:`pygame.pixelcopy` methods and NumPy + arrays. + + A PixelArray pixel item can be assigned a raw integer values, a + :class:`pygame.Color` instance, or a (r, g, b[, a]) tuple. + + :: + + pxarray[x, y] = 0xFF00FF + pxarray[x, y] = pygame.Color(255, 0, 255) + pxarray[x, y] = (255, 0, 255) + + However, only a pixel's integer value is returned. So, to compare a pixel + to a particular color the color needs to be first mapped using + the :meth:`Surface.map_rgb()` method of the Surface object for which the + PixelArray was created. + + :: + + pxarray = pygame.PixelArray(surface) + # Check, if the first pixel at the topleft corner is blue + if pxarray[0, 0] == surface.map_rgb((0, 0, 255)): + ... + + When assigning to a range of of pixels, a non tuple sequence of colors or + a PixelArray can be used as the value. For a sequence, the length must + match the PixelArray width. + + :: + + pxarray[a:b] = 0xFF00FF # set all pixels to 0xFF00FF + pxarray[a:b] = (0xFF00FF, 0xAACCEE, ... ) # first pixel = 0xFF00FF, + # second pixel = 0xAACCEE, ... + pxarray[a:b] = [(255, 0, 255), (170, 204, 238), ...] # same as above + pxarray[a:b] = [(255, 0, 255), 0xAACCEE, ...] # same as above + pxarray[a:b] = otherarray[x:y] # slice sizes must match + + For PixelArray assignment, if the right hand side array has a row length + of 1, then the column is broadcast over the target array's rows. An + array of height 1 is broadcast over the target's columns, and is equivalent + to assigning a 1D PixelArray. + + Subscript slices can also be used to assign to a rectangular subview of + the target PixelArray. + + :: + + # Create some new PixelArray objects providing a different view + # of the original array/surface. + newarray = pxarray[2:4, 3:5] + otherarray = pxarray[::2, ::2] + + Subscript slices can also be used to do fast rectangular pixel manipulations + instead of iterating over the x or y axis. The + + :: + + pxarray[::2, :] = (0, 0, 0) # Make even columns black. + pxarray[::2] = (0, 0, 0) # Same as [::2, :] + + During its lifetime, the PixelArray locks the surface, thus you explicitly + have to close() it once its not used any more and the surface should perform + operations in the same scope. It is best to use it as a context manager + using the with PixelArray(surf) as pixel_array: style. So it works on pypy too. + + A simple ``:`` slice index for the column can be omitted. + + :: + + pxarray[::2, ...] = (0, 0, 0) # Same as pxarray[::2, :] + pxarray[...] = (255, 0, 0) # Same as pxarray[:] + + A note about PixelArray to PixelArray assignment, for arrays with an + item size of 3 (created from 24 bit surfaces) pixel values are translated + from the source to the destinations format. The red, green, and blue + color elements of each pixel are shifted to match the format of the + target surface. For all other pixel sizes no such remapping occurs. + This should change in later pygame releases, where format conversions + are performed for all pixel sizes. To avoid code breakage when full mapped + copying is implemented it is suggested PixelArray to PixelArray copies be + only between surfaces of identical format. + + .. versionadded:: 1.9.4 + + - close() method was added. For explicitly cleaning up. + - being able to use PixelArray as a context manager for cleanup. + - both of these are useful for when working without reference counting (pypy). + + .. versionadded:: 1.9.2 + + - array struct interface + - transpose method + - broadcasting for a length 1 dimension + + .. versionchanged:: 1.9.2 + + - A 2D PixelArray can have a length 1 dimension. + Only an integer index on a 2D PixelArray returns a 1D array. + - For assignment, a tuple can only be a color. Any other sequence type + is a sequence of colors. + + + .. versionadded: 1.8.0 + Subscript support + + .. versionadded: 1.8.1 + Methods :meth:`make_surface`, :meth:`replace`, :meth:`extract`, and + :meth:`compare` + + .. versionadded: 1.9.2 + Properties :attr:`itemsize`, :attr:`ndim`, :attr:`shape`, + and :attr:`strides` + + .. versionadded: 1.9.2 + Array struct interface + + .. versionadded: 1.9.4 + Methods :meth:`close` + + .. attribute:: surface + + | :sl:`Gets the Surface the PixelArray uses.` + | :sg:`surface -> Surface` + + The Surface the PixelArray was created for. + + .. ## PixelArray.surface ## + + .. attribute:: itemsize + + | :sl:`Returns the byte size of a pixel array item` + | :sg:`itemsize -> int` + + This is the same as :meth:`Surface.get_bytesize` for the + pixel array's surface. + + .. versionadded:: 1.9.2 + + .. attribute:: ndim + + | :sl:`Returns the number of dimensions.` + | :sg:`ndim -> int` + + A pixel array can be 1 or 2 dimensional. + + .. versionadded:: 1.9.2 + + .. attribute:: shape + + | :sl:`Returns the array size.` + | :sg:`shape -> tuple of int's` + + A tuple or length :attr:`ndim` giving the length of each + dimension. Analogous to :meth:`Surface.get_size`. + + .. versionadded:: 1.9.2 + + .. attribute:: strides + + | :sl:`Returns byte offsets for each array dimension.` + | :sg:`strides -> tuple of int's` + + A tuple or length :attr:`ndim` byte counts. When a stride is + multiplied by the corresponding index it gives the offset + of that index from the start of the array. A stride is negative + for an array that has is inverted (has a negative step). + + .. versionadded:: 1.9.2 + + .. method:: make_surface + + | :sl:`Creates a new Surface from the current PixelArray.` + | :sg:`make_surface() -> Surface` + + Creates a new Surface from the current PixelArray. Depending on the + current PixelArray the size, pixel order etc. will be different from the + original Surface. + + :: + + # Create a new surface flipped around the vertical axis. + sf = pxarray[:,::-1].make_surface () + + .. versionadded:: 1.8.1 + + .. ## PixelArray.make_surface ## + + .. method:: replace + + | :sl:`Replaces the passed color in the PixelArray with another one.` + | :sg:`replace(color, repcolor, distance=0, weights=(0.299, 0.587, 0.114)) -> None` + + Replaces the pixels with the passed color in the PixelArray by changing + them them to the passed replacement color. + + It uses a simple weighted Euclidean distance formula to calculate the + distance between the colors. The distance space ranges from 0.0 to 1.0 + and is used as threshold for the color detection. This causes the + replacement to take pixels with a similar, but not exactly identical + color, into account as well. + + This is an in place operation that directly affects the pixels of the + PixelArray. + + .. versionadded:: 1.8.1 + + .. ## PixelArray.replace ## + + .. method:: extract + + | :sl:`Extracts the passed color from the PixelArray.` + | :sg:`extract(color, distance=0, weights=(0.299, 0.587, 0.114)) -> PixelArray` + + Extracts the passed color by changing all matching pixels to white, while + non-matching pixels are changed to black. This returns a new PixelArray + with the black/white color mask. + + It uses a simple weighted Euclidean distance formula to calculate the + distance between the colors. The distance space ranges from 0.0 to 1.0 + and is used as threshold for the color detection. This causes the + extraction to take pixels with a similar, but not exactly identical + color, into account as well. + + .. versionadded:: 1.8.1 + + .. ## PixelArray.extract ## + + .. method:: compare + + | :sl:`Compares the PixelArray with another one.` + | :sg:`compare(array, distance=0, weights=(0.299, 0.587, 0.114)) -> PixelArray` + + Compares the contents of the PixelArray with those from the passed in + PixelArray. It returns a new PixelArray with a black/white color mask + that indicates the differences (black) of both arrays. Both PixelArray + objects must have identical bit depths and dimensions. + + It uses a simple weighted Euclidean distance formula to calculate the + distance between the colors. The distance space ranges from 0.0 to 1.0 + and is used as a threshold for the color detection. This causes the + comparison to mark pixels with a similar, but not exactly identical + color, as white. + + .. versionadded:: 1.8.1 + + .. ## PixelArray.compare ## + + .. method:: transpose + + | :sl:`Exchanges the x and y axis.` + | :sg:`transpose() -> PixelArray` + + This method returns a new view of the pixel array with the rows and + columns swapped. So for a (w, h) sized array a (h, w) slice is returned. + If an array is one dimensional, then a length 1 x dimension is added, + resulting in a 2D pixel array. + + .. versionadded:: 1.9.2 + + .. ## PixelArray.transpose ## + + .. method:: close + + | :sl:`Closes the PixelArray, and releases Surface lock.` + | :sg:`close() -> PixelArray` + + This method is for explicitly closing the PixelArray, and releasing + a lock on the Surface. + + .. versionadded:: 1.9.4 + + .. ## PixelArray.close ## + + + .. ## pygame.PixelArray ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/pixelcopy.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/pixelcopy.rst.txt new file mode 100644 index 00000000..dd8ecf73 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/pixelcopy.rst.txt @@ -0,0 +1,104 @@ +.. include:: common.txt + +:mod:`pygame.pixelcopy` +======================= + +.. module:: pygame.pixelcopy + :synopsis: pygame module for general pixel array copying + +| :sl:`pygame module for general pixel array copying` + +The ``pygame.pixelcopy`` module contains functions for copying between +surfaces and objects exporting an array structure interface. It is a backend +for :mod:`pygame.surfarray`, adding NumPy support. But pixelcopy is more +general, and intended for direct use. + +The array struct interface exposes an array's data in a standard way. +It was introduced in NumPy. In Python 2.7 and above it is replaced by the +new buffer protocol, though the buffer protocol is still a work in progress. +The array struct interface, on the other hand, is stable and works with earlier +Python versions. So for now the array struct interface is the predominate way +pygame handles array introspection. + +For 2d arrays of integer pixel values, the values are mapped to the +pixel format of the related surface. To get the actual color of a pixel +value use :meth:`pygame.Surface.unmap_rgb`. 2d arrays can only be used +directly between surfaces having the same pixel layout. + +New in pygame 1.9.2. + +.. function:: surface_to_array + + | :sl:`copy surface pixels to an array object` + | :sg:`surface_to_array(array, surface, kind='P', opaque=255, clear=0) -> None` + + The surface_to_array function copies pixels from a Surface object + to a 2D or 3D array. Depending on argument ``kind`` and the target array + dimension, a copy may be raw pixel value, RGB, a color component slice, + or colorkey alpha transparency value. Recognized ``kind`` values are the + single character codes 'P', 'R', 'G', 'B', 'A', and 'C'. Kind codes are case + insensitive, so 'p' is equivalent to 'P'. The first two dimensions + of the target must be the surface size (w, h). + + The default 'P' kind code does a direct raw integer pixel (mapped) value + copy to a 2D array and a 'RGB' pixel component (unmapped) copy to a 3D array + having shape (w, h, 3). For an 8 bit colormap surface this means the + table index is copied to a 2D array, not the table value itself. A 2D + array's item size must be at least as large as the surface's pixel + byte size. The item size of a 3D array must be at least one byte. + + For the 'R', 'G', 'B', and 'A' copy kinds a single color component + of the unmapped surface pixels are copied to the target 2D array. + For kind 'A' and surfaces with source alpha (the surface was created with + the SRCALPHA flag), has a colorkey + (set with :meth:`Surface.set_colorkey() `), + or has a blanket alpha + (set with :meth:`Surface.set_alpha() `) + then the alpha values are those expected for a SDL surface. + If a surface has no explicit alpha value, then the target array + is filled with the value of the optional ``opaque`` surface_to_array + argument (default 255: not transparent). + + Copy kind 'C' is a special case for alpha copy of a source surface + with colorkey. Unlike the 'A' color component copy, the ``clear`` + argument value is used for colorkey matches, ``opaque`` otherwise. + By default, a match has alpha 0 (totally transparent), while everything + else is alpha 255 (totally opaque). It is a more general implementation + of :meth:`pygame.surfarray.array_colorkey`. + + Specific to surface_to_array, a ValueError is raised for target arrays + with incorrect shape or item size. A TypeError is raised for an incorrect + kind code. Surface specific problems, such as locking, raise a pygame.error. + + .. ## pygame.pixelcopy.surface_to_array ## + +.. function:: array_to_surface + + | :sl:`copy an array object to a surface` + | :sg:`array_to_surface(, ) -> None` + + See :func:`pygame.surfarray.blit_array`. + + .. ## pygame.pixelcopy.array_to_surface ## + +.. function:: map_array + + | :sl:`copy an array to another array, using surface format` + | :sg:`map_array(, , ) -> None` + + Map an array of color element values - (w, h, ..., 3) - to an array of + pixels - (w, h) according to the format of . + + .. ## pygame.pixelcopy.map_array ## + +.. function:: make_surface + + | :sl:`Copy an array to a new surface` + | :sg:`pygame.pixelcopy.make_surface(array) -> Surface` + + Create a new Surface that best resembles the data and format of the array. + The array can be 2D or 3D with any sized integer values. + + .. ## pygame.pixelcopy.make_surface ## + +.. ## pygame.pixelcopy ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/pygame.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/pygame.rst.txt new file mode 100644 index 00000000..280831a3 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/pygame.rst.txt @@ -0,0 +1,505 @@ +.. include:: common.txt + +:mod:`pygame` +============= + +.. module:: pygame + :synopsis: the top level pygame package + +| :sl:`the top level pygame package` + +The pygame package represents the top-level package for others to use. Pygame +itself is broken into many submodules, but this does not affect programs that +use pygame. + +As a convenience, most of the top-level variables in pygame have been placed +inside a module named :mod:`pygame.locals`. This is meant to be used with +``from pygame.locals import *``, in addition to ``import pygame``. + +When you ``import pygame`` all available pygame submodules are automatically +imported. Be aware that some of the pygame modules are considered *optional*, +and may not be available. In that case, pygame will provide a placeholder +object instead of the module, which can be used to test for availability. + +.. function:: init + + | :sl:`initialize all imported pygame modules` + | :sg:`init() -> (numpass, numfail)` + + Initialize all imported pygame modules. No exceptions will be raised if a + module fails, but the total number if successful and failed inits will be + returned as a tuple. You can always initialize individual modules manually, + but :func:`pygame.init` is a convenient way to get everything started. The + ``init()`` functions for individual modules will raise exceptions when they + fail. + + You may want to initialize the different modules separately to speed up your + program or to not use modules your game does not require. + + It is safe to call this ``init()`` more than once as repeated calls will have + no effect. This is true even if you have ``pygame.quit()`` all the modules. + + .. ## pygame.init ## + +.. function:: quit + + | :sl:`uninitialize all pygame modules` + | :sg:`quit() -> None` + + Uninitialize all pygame modules that have previously been initialized. When + the Python interpreter shuts down, this method is called regardless, so your + program should not need it, except when it wants to terminate its pygame + resources and continue. It is safe to call this function more than once as + repeated calls have no effect. + + .. note:: + Calling :func:`pygame.quit` will not exit your program. Consider letting + your program end in the same way a normal Python program will end. + + .. ## pygame.quit ## + +.. function:: get_init + + | :sl:`returns True if pygame is currently initialized` + | :sg:`get_init() -> bool` + + Returns ``True`` if pygame is currently initialized. + + .. versionadded:: 1.9.5 + + .. ## pygame.get_init ## + +.. exception:: error + + | :sl:`standard pygame exception` + | :sg:`raise pygame.error(message)` + + This exception is raised whenever a pygame or SDL operation fails. You + can catch any anticipated problems and deal with the error. The exception is + always raised with a descriptive message about the problem. + + Derived from the ``RuntimeError`` exception, which can also be used to catch + these raised errors. + + .. ## pygame.error ## + +.. function:: get_error + + | :sl:`get the current error message` + | :sg:`get_error() -> errorstr` + + SDL maintains an internal error message. This message will usually be + given to you when :func:`pygame.error` is raised, so this function will + rarely be needed. + + .. ## pygame.get_error ## + +.. function:: set_error + + | :sl:`set the current error message` + | :sg:`set_error(error_msg) -> None` + + SDL maintains an internal error message. This message will usually be + given to you when :func:`pygame.error` is raised, so this function will + rarely be needed. + + .. ## pygame.set_error ## + +.. function:: get_sdl_version + + | :sl:`get the version number of SDL` + | :sg:`get_sdl_version(linked=True) -> major, minor, patch` + + Returns the three version numbers of the SDL library. ``linked=True`` + will cause the function to return the version of the library that pygame + is linked against while ``linked=False`` will cause the function to return + the version of the library that pygame is compiled against. + It can be used to detect which features may or may not be + available through pygame. + + .. versionadded:: 1.7.0 + + .. versionchanged:: 2.2.0 ``linked`` keyword argument added + + .. ## pygame.get_sdl_version ## + +.. function:: get_sdl_byteorder + + | :sl:`get the byte order of SDL` + | :sg:`get_sdl_byteorder() -> int` + + Returns the byte order of the SDL library. It returns ``1234`` for little + endian byte order and ``4321`` for big endian byte order. + + .. versionadded:: 1.8 + + .. ## pygame.get_sdl_byteorder ## + +.. function:: register_quit + + | :sl:`register a function to be called when pygame quits` + | :sg:`register_quit(callable) -> None` + + When :func:`pygame.quit` is called, all registered quit functions are + called. Pygame modules do this automatically when they are initializing, so + this function will rarely be needed. + + .. ## pygame.register_quit ## + +.. function:: encode_string + + | :sl:`Encode a Unicode or bytes object` + | :sg:`encode_string([obj [, encoding [, errors [, etype]]]]) -> bytes or None` + + obj: If Unicode, encode; if bytes, return unaltered; if anything else, + return ``None``; if not given, raise ``SyntaxError``. + + encoding (string): If present, encoding to use. The default is + ``'unicode_escape'``. + + errors (string): If given, how to handle unencodable characters. The default + is ``'backslashreplace'``. + + etype (exception type): If given, the exception type to raise for an + encoding error. The default is ``UnicodeEncodeError``, as returned by + ``PyUnicode_AsEncodedString()``. For the default encoding and errors values + there should be no encoding errors. + + This function is used in encoding file paths. Keyword arguments are + supported. + + .. versionadded:: 1.9.2 (primarily for use in unit tests) + + .. ## pygame.encode_string ## + +.. function:: encode_file_path + + | :sl:`Encode a Unicode or bytes object as a file system path` + | :sg:`encode_file_path([obj [, etype]]) -> bytes or None` + + obj: If Unicode, encode; if bytes, return unaltered; if anything else, + return ``None``; if not given, raise ``SyntaxError``. + + etype (exception type): If given, the exception type to raise for an + encoding error. The default is ``UnicodeEncodeError``, as returned by + ``PyUnicode_AsEncodedString()``. + + This function is used to encode file paths in pygame. Encoding is to the + codec as returned by ``sys.getfilesystemencoding()``. Keyword arguments are + supported. + + .. versionadded:: 1.9.2 (primarily for use in unit tests) + + .. ## pygame.encode_file_path ## + + +:mod:`pygame.version` +===================== + +.. module:: pygame.version + :synopsis: small module containing version information + +| :sl:`small module containing version information` + +This module is automatically imported into the pygame package and can be used to +check which version of pygame has been imported. + +.. data:: ver + + | :sl:`version number as a string` + | :sg:`ver = '1.2'` + + This is the version represented as a string. It can contain a micro release + number as well, e.g. ``'1.5.2'`` + + .. ## pygame.version.ver ## + +.. data:: vernum + + | :sl:`tupled integers of the version` + | :sg:`vernum = (1, 5, 3)` + + This version information can easily be compared with other version + numbers of the same format. An example of checking pygame version numbers + would look like this: + + :: + + if pygame.version.vernum < (1, 5): + print('Warning, older version of pygame (%s)' % pygame.version.ver) + disable_advanced_features = True + + .. versionadded:: 1.9.6 Attributes ``major``, ``minor``, and ``patch``. + + :: + + vernum.major == vernum[0] + vernum.minor == vernum[1] + vernum.patch == vernum[2] + + .. versionchanged:: 1.9.6 + ``str(pygame.version.vernum)`` returns a string like ``"2.0.0"`` instead + of ``"(2, 0, 0)"``. + + .. versionchanged:: 1.9.6 + ``repr(pygame.version.vernum)`` returns a string like + ``"PygameVersion(major=2, minor=0, patch=0)"`` instead of ``"(2, 0, 0)"``. + + .. ## pygame.version.vernum ## + +.. data:: rev + + | :sl:`repository revision of the build` + | :sg:`rev = 'a6f89747b551+'` + + The Mercurial node identifier of the repository checkout from which this + package was built. If the identifier ends with a plus sign '+' then the + package contains uncommitted changes. Please include this revision number + in bug reports, especially for non-release pygame builds. + + Important note: pygame development has moved to github, this variable is + obsolete now. As soon as development shifted to github, this variable started + returning an empty string ``""``. + It has always been returning an empty string since ``v1.9.5``. + + .. versionchanged:: 1.9.5 + Always returns an empty string ``""``. + + .. ## pygame.version.rev ## + +.. data:: SDL + + | :sl:`tupled integers of the SDL library version` + | :sg:`SDL = '(2, 0, 12)'` + + This is the SDL library version represented as an extended tuple. It also has + attributes 'major', 'minor' & 'patch' that can be accessed like this: + + :: + + >>> pygame.version.SDL.major + 2 + + printing the whole thing returns a string like this: + + :: + + >>> pygame.version.SDL + SDLVersion(major=2, minor=0, patch=12) + + .. versionadded:: 2.0.0 + + .. ## pygame.version.SDL ## + +.. ## pygame.version ## + +.. ## pygame ## + +.. _environment-variables: + +**Setting Environment Variables** + +Some aspects of pygame's behaviour can be controlled by setting environment variables, they cover a wide +range of the library's functionality. Some of the variables are from pygame itself, while others come from +the underlying C SDL library that pygame uses. + +In python, environment variables are usually set in code like this:: + + import os + os.environ['NAME_OF_ENVIRONMENT_VARIABLE'] = 'value_to_set' + +Or to preserve users ability to override the variable:: + + import os + os.environ['ENV_VAR'] = os.environ.get('ENV_VAR', 'value') + +If the variable is more useful for users of an app to set than the developer then they can set it like this: + +**Windows**:: + + set NAME_OF_ENVIRONMENT_VARIABLE=value_to_set + python my_application.py + +**Linux/Mac**:: + + ENV_VAR=value python my_application.py + +For some variables they need to be set before initialising pygame, some must be set before even importing pygame, +and others can simply be set right before the area of code they control is run. + +Below is a list of environment variables, their settable values, and a brief description of what they do. + +| + +**Pygame Environment Variables** + +These variables are defined by pygame itself. + +| + +:: + + PYGAME_DISPLAY - Experimental (subject to change) + Set index of the display to use, "0" is the default. + +This sets the display where pygame will open its window +or screen. The value set here will be used if set before +calling :func:`pygame.display.set_mode()`, and as long as no +'display' parameter is passed into :func:`pygame.display.set_mode()`. + +| + +:: + + PYGAME_FORCE_SCALE - + Set to "photo" or "default". + +This forces set_mode() to use the SCALED display mode and, +if "photo" is set, makes the scaling use the slowest, but +highest quality anisotropic scaling algorithm, if it is +available. Must be set before calling :func:`pygame.display.set_mode()`. + +| + +:: + + PYGAME_BLEND_ALPHA_SDL2 - New in pygame 2.0.0 + Set to "1" to enable the SDL2 blitter. + +This makes pygame use the SDL2 blitter for all alpha +blending. The SDL2 blitter is sometimes faster than +the default blitter but uses a different formula so +the final colours may differ. Must be set before +:func:`pygame.init()` is called. + +| + +:: + + PYGAME_HIDE_SUPPORT_PROMPT - + Set to "1" to hide the prompt. + +This stops the welcome message popping up in the +console that tells you which version of python, +pygame & SDL you are using. Must be set before +importing pygame. + +| + +:: + + PYGAME_FREETYPE - + Set to "1" to enable. + +This switches the pygame.font module to a pure +freetype implementation that bypasses SDL_ttf. +See the font module for why you might want to +do this. Must be set before importing pygame. + +| + +:: + + PYGAME_CAMERA - + Set to "opencv" or "vidcapture" + +Forces the library backend used in the camera +module, overriding the platform defaults. Must +be set before calling :func:`pygame.camera.init()`. + +In pygame 2.0.3, backends can be set programmatically instead, and the old +OpenCV backend has been replaced with one on top of "opencv-python," rather +than the old "highgui" OpenCV port. Also, there is a new native Windows +backend available. + +| +| + +**SDL Environment Variables** + +These variables are defined by SDL. + +For documentation on the environment variables available in +pygame 1 try `here +`__. +For Pygame 2, some selected environment variables are listed below. + +| + +:: + + SDL_VIDEO_CENTERED - + Set to "1" to enable centering the window. + +This will make the pygame window open in the centre of the display. +Must be set before calling :func:`pygame.display.set_mode()`. + +| + +:: + + SDL_VIDEO_WINDOW_POS - + Set to "x,y" to position the top left corner of the window. + +This allows control over the placement of the pygame window within +the display. Must be set before calling :func:`pygame.display.set_mode()`. + +| + +:: + + SDL_VIDEODRIVER - + Set to "drivername" to change the video driver used. + +On some platforms there are multiple video drivers available and +this allows users to pick between them. More information is available +`here `__. Must be set before +calling :func:`pygame.init()` or :func:`pygame.display.init()`. + +| + +:: + + SDL_AUDIODRIVER - + Set to "drivername" to change the audio driver used. + +On some platforms there are multiple audio drivers available and +this allows users to pick between them. More information is available +`here `__. Must be set before +calling :func:`pygame.init()` or :func:`pygame.mixer.init()`. + +| + +:: + + SDL_VIDEO_ALLOW_SCREENSAVER + Set to "1" to allow screensavers while pygame apps are running. + +By default pygame apps disable screensavers while +they are running. Setting this environment variable allows users or +developers to change that and make screensavers run again. + +| + +:: + + SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR + Set to "0" to re-enable the compositor. + +By default SDL tries to disable the X11 compositor for all pygame +apps. This is usually a good thing as it's faster, however if you +have an app which *doesn't* update every frame and are using linux +you may want to disable this bypass. The bypass has reported problems +on KDE linux. This variable is only used on x11/linux platforms. + +| + +:: + + SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS + Set to "1" to allow joysticks to be updated even when the window is out of focus + +By default, when the window is not in focus, input devices do not get +updated. However, using this environment variable it is possible to get +joystick updates even when the window is in the background. Must be set +before calling :func:`pygame.init()` or :func:`pygame.joystick.init()`. diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/rect.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/rect.rst.txt new file mode 100644 index 00000000..ce4e605e --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/rect.rst.txt @@ -0,0 +1,604 @@ +.. include:: common.txt + +:mod:`pygame.Rect` +================== + +.. currentmodule:: pygame + +.. class:: Rect + + | :sl:`pygame object for storing rectangular coordinates` + | :sg:`Rect(left, top, width, height) -> Rect` + | :sg:`Rect((left, top), (width, height)) -> Rect` + | :sg:`Rect(object) -> Rect` + + Pygame uses Rect objects to store and manipulate rectangular areas. A Rect + can be created from a combination of left, top, width, and height values. + Rects can also be created from Python objects that are already a Rect or + have an attribute named "rect". + + Any Pygame function that requires a Rect argument also accepts any of these + values to construct a Rect. This makes it easier to create Rects on the fly + as arguments for functions. + + The Rect functions that change the position or size of a Rect return a new + copy of the Rect with the affected changes. The original Rect is not + modified. Some methods have an alternate "in-place" version that returns + None but affects the original Rect. These "in-place" methods are denoted + with the "ip" suffix. + + The Rect object has several virtual attributes which can be used to move and + align the Rect: + + :: + + x,y + top, left, bottom, right + topleft, bottomleft, topright, bottomright + midtop, midleft, midbottom, midright + center, centerx, centery + size, width, height + w,h + + All of these attributes can be assigned to: + + :: + + rect1.right = 10 + rect2.center = (20,30) + + Assigning to size, width or height changes the dimensions of the rectangle; + all other assignments move the rectangle without resizing it. Notice that + some attributes are integers and others are pairs of integers. + + If a Rect has a nonzero width or height, it will return ``True`` for a + nonzero test. Some methods return a Rect with 0 size to represent an invalid + rectangle. A Rect with a 0 size will not collide when using collision + detection methods (e.g. :meth:`collidepoint`, :meth:`colliderect`, etc.). + + The coordinates for Rect objects are all integers. The size values can be + programmed to have negative values, but these are considered illegal Rects + for most operations. + + There are several collision tests between other rectangles. Most python + containers can be searched for collisions against a single Rect. + + The area covered by a Rect does not include the right- and bottom-most edge + of pixels. If one Rect's bottom border is another Rect's top border (i.e., + rect1.bottom=rect2.top), the two meet exactly on the screen but do not + overlap, and ``rect1.colliderect(rect2)`` returns false. + + The Rect object is also iterable: + + :: + + r = Rect(0, 1, 2, 3) + x, y, w, h = r + + .. versionadded:: 1.9.2 + The Rect class can be subclassed. Methods such as ``copy()`` and ``move()`` + will recognize this and return instances of the subclass. + However, the subclass's ``__init__()`` method is not called, + and ``__new__()`` is assumed to take no arguments. So these methods should be + overridden if any extra attributes need to be copied. + + .. method:: copy + + | :sl:`copy the rectangle` + | :sg:`copy() -> Rect` + + Returns a new rectangle having the same position and size as the original. + + New in pygame 1.9 + + .. ## Rect.copy ## + + .. method:: move + + | :sl:`moves the rectangle` + | :sg:`move(x, y) -> Rect` + + Returns a new rectangle that is moved by the given offset. The x and y + arguments can be any integer value, positive or negative. + + .. ## Rect.move ## + + .. method:: move_ip + + | :sl:`moves the rectangle, in place` + | :sg:`move_ip(x, y) -> None` + + Same as the ``Rect.move()`` method, but operates in place. + + .. ## Rect.move_ip ## + + .. method:: inflate + + | :sl:`grow or shrink the rectangle size` + | :sg:`inflate(x, y) -> Rect` + + Returns a new rectangle with the size changed by the given offset. The + rectangle remains centered around its current center. Negative values + will shrink the rectangle. Note, uses integers, if the offset given is + too small(< 2 > -2), center will be off. + + .. ## Rect.inflate ## + + .. method:: inflate_ip + + | :sl:`grow or shrink the rectangle size, in place` + | :sg:`inflate_ip(x, y) -> None` + + Same as the ``Rect.inflate()`` method, but operates in place. + + .. ## Rect.inflate_ip ## + + .. method:: scale_by + + | :sl:`scale the rectangle by given a multiplier` + | :sg:`scale_by(scalar) -> Rect` + | :sg:`scale_by(scalex, scaley) -> Rect` + + Returns a new rectangle with the size scaled by the given multipliers. + The rectangle remains centered around its current center. A single + scalar or separate width and height scalars are allowed. Values above + one will increase the size of the rectangle, whereas values between + zero and one will decrease the size of the rectangle. + + .. versionchanged:: 2.5.0 Added support for keyword arguments. + + .. ## Rect.scale_by ## + + .. method:: scale_by_ip + + | :sl:`grow or shrink the rectangle size, in place` + | :sg:`scale_by_ip(scalar) -> None` + | :sg:`scale_by_ip(scalex, scaley) -> None` + + Same as the ``Rect.scale_by()`` method, but operates in place. + + .. versionchanged:: 2.5.0 Added support for keyword arguments. + + .. ## Rect.scale_by_ip ## + + .. method:: update + + | :sl:`sets the position and size of the rectangle` + | :sg:`update(left, top, width, height) -> None` + | :sg:`update((left, top), (width, height)) -> None` + | :sg:`update(object) -> None` + + Sets the position and size of the rectangle, in place. See + parameters for :meth:`pygame.Rect` for the parameters of this function. + + .. versionadded:: 2.0.1 + + .. ## Rect.update ## + + .. method:: clamp + + | :sl:`moves the rectangle inside another` + | :sg:`clamp(Rect) -> Rect` + + Returns a new rectangle that is moved to be completely inside the + argument Rect. If the rectangle is too large to fit inside, it is + centered inside the argument Rect, but its size is not changed. + + .. ## Rect.clamp ## + + .. method:: clamp_ip + + | :sl:`moves the rectangle inside another, in place` + | :sg:`clamp_ip(Rect) -> None` + + Same as the ``Rect.clamp()`` method, but operates in place. + + .. ## Rect.clamp_ip ## + + .. method:: clip + + | :sl:`crops a rectangle inside another` + | :sg:`clip(Rect) -> Rect` + + Returns a new rectangle that is cropped to be completely inside the + argument Rect. If the two rectangles do not overlap to begin with, a Rect + with 0 size is returned. + + .. ## Rect.clip ## + + .. method:: clipline + + | :sl:`crops a line inside a rectangle` + | :sg:`clipline(x1, y1, x2, y2) -> ((cx1, cy1), (cx2, cy2))` + | :sg:`clipline(x1, y1, x2, y2) -> ()` + | :sg:`clipline((x1, y1), (x2, y2)) -> ((cx1, cy1), (cx2, cy2))` + | :sg:`clipline((x1, y1), (x2, y2)) -> ()` + | :sg:`clipline((x1, y1, x2, y2)) -> ((cx1, cy1), (cx2, cy2))` + | :sg:`clipline((x1, y1, x2, y2)) -> ()` + | :sg:`clipline(((x1, y1), (x2, y2))) -> ((cx1, cy1), (cx2, cy2))` + | :sg:`clipline(((x1, y1), (x2, y2))) -> ()` + + Returns the coordinates of a line that is cropped to be completely inside + the rectangle. If the line does not overlap the rectangle, then an empty + tuple is returned. + + The line to crop can be any of the following formats (floats can be used + in place of ints, but they will be truncated): + + - four ints + - 2 lists/tuples/Vector2s of 2 ints + - a list/tuple of four ints + - a list/tuple of 2 lists/tuples/Vector2s of 2 ints + + :returns: a tuple with the coordinates of the given line cropped to be + completely inside the rectangle is returned, if the given line does + not overlap the rectangle, an empty tuple is returned + :rtype: tuple(tuple(int, int), tuple(int, int)) or () + + :raises TypeError: if the line coordinates are not given as one of the + above described line formats + + .. note :: + This method can be used for collision detection between a rect and a + line. See example code below. + + .. note :: + The ``rect.bottom`` and ``rect.right`` attributes of a + :mod:`pygame.Rect` always lie one pixel outside of its actual border. + + :: + + # Example using clipline(). + clipped_line = rect.clipline(line) + + if clipped_line: + # If clipped_line is not an empty tuple then the line + # collides/overlaps with the rect. The returned value contains + # the endpoints of the clipped line. + start, end = clipped_line + x1, y1 = start + x2, y2 = end + else: + print("No clipping. The line is fully outside the rect.") + + .. versionchanged:: 2.5.0 Added support for keyword arguments. + + .. versionadded:: 2.0.0 + + .. ## Rect.clipline ## + + .. method:: union + + | :sl:`joins two rectangles into one` + | :sg:`union(Rect) -> Rect` + + Returns a new rectangle that completely covers the area of the two + provided rectangles. There may be area inside the new Rect that is not + covered by the originals. + + .. ## Rect.union ## + + .. method:: union_ip + + | :sl:`joins two rectangles into one, in place` + | :sg:`union_ip(Rect) -> None` + + Same as the ``Rect.union()`` method, but operates in place. + + .. ## Rect.union_ip ## + + .. method:: unionall + + | :sl:`the union of many rectangles` + | :sg:`unionall(Rect_sequence) -> Rect` + + Returns the union of one rectangle with a sequence of many rectangles. + + .. versionchanged:: 2.5.0 Added support for keyword arguments. + + .. ## Rect.unionall ## + + .. method:: unionall_ip + + | :sl:`the union of many rectangles, in place` + | :sg:`unionall_ip(Rect_sequence) -> None` + + The same as the ``Rect.unionall()`` method, but operates in place. + + .. versionchanged:: 2.5.0 Added support for keyword arguments. + + .. ## Rect.unionall_ip ## + + .. method:: fit + + | :sl:`resize and move a rectangle with aspect ratio` + | :sg:`fit(Rect) -> Rect` + + Returns a new rectangle that is moved and resized to fit another. The + aspect ratio of the original Rect is preserved, so the new rectangle may + be smaller than the target in either width or height. + + .. ## Rect.fit ## + + .. method:: normalize + + | :sl:`correct negative sizes` + | :sg:`normalize() -> None` + + This will flip the width or height of a rectangle if it has a negative + size. The rectangle will remain in the same place, with only the sides + swapped. + + .. ## Rect.normalize ## + + .. method:: contains + + | :sl:`test if one rectangle is inside another` + | :sg:`contains(Rect) -> bool` + + Returns true when the argument is completely inside the Rect. + + .. ## Rect.contains ## + + .. method:: collidepoint + + | :sl:`test if a point is inside a rectangle` + | :sg:`collidepoint(x, y) -> bool` + | :sg:`collidepoint((x,y)) -> bool` + + Returns true if the given point is inside the rectangle. A point along + the right or bottom edge is not considered to be inside the rectangle. + + .. note :: + For collision detection between a rect and a line the :meth:`clipline` + method can be used. + + .. ## Rect.collidepoint ## + + .. method:: colliderect + + | :sl:`test if two rectangles overlap` + | :sg:`colliderect(Rect) -> bool` + + Returns true if any portion of either rectangle overlap (except the + top+bottom or left+right edges). + + .. note :: + For collision detection between a rect and a line the :meth:`clipline` + method can be used. + + .. ## Rect.colliderect ## + + .. method:: collidelist + + | :sl:`test if one rectangle in a list intersects` + | :sg:`collidelist(list) -> index` + + Test whether the rectangle collides with any in a sequence of rectangles. + The index of the first collision found is returned. If no collisions are + found an index of -1 is returned. + + .. versionchanged:: 2.5.0 Added support for keyword arguments. + + .. ## Rect.collidelist ## + + .. method:: collidelistall + + | :sl:`test if all rectangles in a list intersect` + | :sg:`collidelistall(list) -> indices` + + Returns a list of all the indices that contain rectangles that collide + with the Rect. If no intersecting rectangles are found, an empty list is + returned. + + Not only Rects are valid arguments, but these are all valid calls: + + .. code-block:: python + + Rect = pygame.Rect + r = Rect(0, 0, 10, 10) + + list_of_rects = [Rect(1, 1, 1, 1), Rect(2, 2, 2, 2)] + indices0 = r.collidelistall(list_of_rects) + + list_of_lists = [[1, 1, 1, 1], [2, 2, 2, 2]] + indices1 = r.collidelistall(list_of_lists) + + list_of_tuples = [(1, 1, 1, 1), (2, 2, 2, 2)] + indices2 = r.collidelistall(list_of_tuples) + + list_of_double_tuples = [((1, 1), (1, 1)), ((2, 2), (2, 2))] + indices3 = r.collidelistall(list_of_double_tuples) + + class ObjectWithRectAttribute(object): + def __init__(self, r): + self.rect = r + + list_of_object_with_rect_attribute = [ + ObjectWithRectAttribute(Rect(1, 1, 1, 1)), + ObjectWithRectAttribute(Rect(2, 2, 2, 2)), + ] + indices4 = r.collidelistall(list_of_object_with_rect_attribute) + + class ObjectWithCallableRectAttribute(object): + def __init__(self, r): + self._rect = r + + def rect(self): + return self._rect + + list_of_object_with_callable_rect = [ + ObjectWithCallableRectAttribute(Rect(1, 1, 1, 1)), + ObjectWithCallableRectAttribute(Rect(2, 2, 2, 2)), + ] + indices5 = r.collidelistall(list_of_object_with_callable_rect) + + .. versionchanged:: 2.5.0 Added support for keyword arguments. + + .. ## Rect.collidelistall ## + + .. method:: collideobjects + + | :sl:`test if any object in a list intersects` + | :sg:`collideobjects(rect_list) -> object` + | :sg:`collideobjects(obj_list, key=func) -> object` + + **Experimental:** feature still in development available for testing and feedback. It may change. + `Please leave collideobjects feedback with authors `_ + + Test whether the rectangle collides with any object in the sequence. + The object of the first collision found is returned. If no collisions are + found then ``None`` is returned + + If key is given, then it should be a method taking an object from the list + as input and returning a rect like object e.g. ``lambda obj: obj.rectangle``. + If an object has multiple attributes of type Rect then key could return one + of them. + + .. code-block:: python + + r = Rect(1, 1, 10, 10) + + rects = [ + Rect(1, 1, 10, 10), + Rect(5, 5, 10, 10), + Rect(15, 15, 1, 1), + Rect(2, 2, 1, 1), + ] + + result = r.collideobjects(rects) # -> + print(result) + + class ObjectWithSomRectAttribute: + def __init__(self, name, collision_box, draw_rect): + self.name = name + self.draw_rect = draw_rect + self.collision_box = collision_box + + def __repr__(self): + return f'<{self.__class__.__name__}("{self.name}", {list(self.collision_box)}, {list(self.draw_rect)})>' + + objects = [ + ObjectWithSomRectAttribute("A", Rect(15, 15, 1, 1), Rect(150, 150, 50, 50)), + ObjectWithSomRectAttribute("B", Rect(1, 1, 10, 10), Rect(300, 300, 50, 50)), + ObjectWithSomRectAttribute("C", Rect(5, 5, 10, 10), Rect(200, 500, 50, 50)), + ] + + # collision = r.collideobjects(objects) # this does not work because the items in the list are no Rect like object + collision = r.collideobjects( + objects, key=lambda o: o.collision_box + ) # -> + print(collision) + + screen_rect = r.collideobjects(objects, key=lambda o: o.draw_rect) # -> None + print(screen_rect) + + .. versionadded:: 2.1.3 + + .. ## Rect.collideobjects ## + + .. method:: collideobjectsall + + | :sl:`test if all objects in a list intersect` + | :sg:`collideobjectsall(rect_list) -> objects` + | :sg:`collideobjectsall(obj_list, key=func) -> objects` + + **Experimental:** feature still in development available for testing and feedback. It may change. + `Please leave collideobjectsall feedback with authors `_ + + Returns a list of all the objects that contain rectangles that collide + with the Rect. If no intersecting objects are found, an empty list is + returned. + + If key is given, then it should be a method taking an object from the list + as input and returning a rect like object e.g. ``lambda obj: obj.rectangle``. + If an object has multiple attributes of type Rect then key could return one + of them. + + .. code-block:: python + + r = Rect(1, 1, 10, 10) + + rects = [ + Rect(1, 1, 10, 10), + Rect(5, 5, 10, 10), + Rect(15, 15, 1, 1), + Rect(2, 2, 1, 1), + ] + + result = r.collideobjectsall( + rects + ) # -> [, , ] + print(result) + + class ObjectWithSomRectAttribute: + def __init__(self, name, collision_box, draw_rect): + self.name = name + self.draw_rect = draw_rect + self.collision_box = collision_box + + def __repr__(self): + return f'<{self.__class__.__name__}("{self.name}", {list(self.collision_box)}, {list(self.draw_rect)})>' + + objects = [ + ObjectWithSomRectAttribute("A", Rect(1, 1, 10, 10), Rect(300, 300, 50, 50)), + ObjectWithSomRectAttribute("B", Rect(5, 5, 10, 10), Rect(200, 500, 50, 50)), + ObjectWithSomRectAttribute("C", Rect(15, 15, 1, 1), Rect(150, 150, 50, 50)), + ] + + # collisions = r.collideobjectsall(objects) # this does not work because ObjectWithSomRectAttribute is not a Rect like object + collisions = r.collideobjectsall( + objects, key=lambda o: o.collision_box + ) # -> [, ] + print(collisions) + + screen_rects = r.collideobjectsall(objects, key=lambda o: o.draw_rect) # -> [] + print(screen_rects) + + .. versionadded:: 2.1.3 + + .. ## Rect.collideobjectsall ## + + .. method:: collidedict + + | :sl:`test if one rectangle in a dictionary intersects` + | :sg:`collidedict(dict) -> (key, value)` + | :sg:`collidedict(dict) -> None` + | :sg:`collidedict(dict, use_values=0) -> (key, value)` + | :sg:`collidedict(dict, use_values=0) -> None` + + Returns the first key and value pair that intersects with the calling + Rect object. If no collisions are found, ``None`` is returned. If + ``use_values`` is 0 (default) then the dict's keys will be used in the + collision detection, otherwise the dict's values will be used. + + .. note :: + Rect objects cannot be used as keys in a dictionary (they are not + hashable), so they must be converted to a tuple. + e.g. ``rect.collidedict({tuple(key_rect) : value})`` + + .. versionchanged:: 2.5.0 Added support for keyword arguments. + + .. ## Rect.collidedict ## + + .. method:: collidedictall + + | :sl:`test if all rectangles in a dictionary intersect` + | :sg:`collidedictall(dict) -> [(key, value), ...]` + | :sg:`collidedictall(dict, use_values=0) -> [(key, value), ...]` + + Returns a list of all the key and value pairs that intersect with the + calling Rect object. If no collisions are found an empty list is returned. + If ``use_values`` is 0 (default) then the dict's keys will be used in the + collision detection, otherwise the dict's values will be used. + + .. note :: + Rect objects cannot be used as keys in a dictionary (they are not + hashable), so they must be converted to a tuple. + e.g. ``rect.collidedictall({tuple(key_rect) : value})`` + + .. versionchanged:: 2.5.0 Added support for keyword arguments. + + .. ## Rect.collidedictall ## + + .. ## pygame.Rect ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/scrap.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/scrap.rst.txt new file mode 100644 index 00000000..1aac5d09 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/scrap.rst.txt @@ -0,0 +1,240 @@ +.. include:: common.txt + +:mod:`pygame.scrap` +=================== + +.. module:: pygame.scrap + :synopsis: pygame module for clipboard support. + +| :sl:`pygame module for clipboard support.` + +**EXPERIMENTAL!**: This API may change or disappear in later pygame releases. If +you use this, your code may break with the next pygame release. + +The scrap module is for transferring data to/from the clipboard. This allows +for cutting and pasting data between pygame and other applications. Some basic +data (MIME) types are defined and registered: + +:: + + pygame string + constant value description + -------------------------------------------------- + SCRAP_TEXT "text/plain" plain text + SCRAP_BMP "image/bmp" BMP encoded image data + SCRAP_PBM "image/pbm" PBM encoded image data + SCRAP_PPM "image/ppm" PPM encoded image data + +``pygame.SCRAP_PPM``, ``pygame.SCRAP_PBM`` and ``pygame.SCRAP_BMP`` are +suitable for surface buffers to be shared with other applications. +``pygame.SCRAP_TEXT`` is an alias for the plain text clipboard type. + +Depending on the platform, additional types are automatically registered when +data is placed into the clipboard to guarantee a consistent sharing behaviour +with other applications. The following listed types can be used as strings to +be passed to the respective :mod:`pygame.scrap` module functions. + +For **Windows** platforms, these additional types are supported automatically +and resolve to their internal definitions: + +:: + + "text/plain;charset=utf-8" UTF-8 encoded text + "audio/wav" WAV encoded audio + "image/tiff" TIFF encoded image data + +For **X11** platforms, these additional types are supported automatically and +resolve to their internal definitions: + +:: + + "text/plain;charset=utf-8" UTF-8 encoded text + "UTF8_STRING" UTF-8 encoded text + "COMPOUND_TEXT" COMPOUND text + +User defined types can be used, but the data might not be accessible by other +applications unless they know what data type to look for. +Example: Data placed into the clipboard by +``pygame.scrap.put("my_data_type", byte_data)`` can only be accessed by +applications which query the clipboard for the ``"my_data_type"`` data type. + +For an example of how the scrap module works refer to the examples page +(:func:`pygame.examples.scrap_clipboard.main`) or the code directly in GitHub +(`pygame/examples/scrap_clipboard.py `_). + +.. versionadded:: 1.8 + +.. note:: + The scrap module is currently only supported for Windows, X11 and Mac OS X. + On Mac OS X only text works at the moment - other types may be supported in + future releases. + +.. function:: init + + | :sl:`Initializes the scrap module.` + | :sg:`init() -> None` + + Initialize the scrap module. + + :raises pygame.error: if unable to initialize scrap module + + .. note:: The scrap module requires :func:`pygame.display.set_mode()` be + called before being initialized. + + .. ## pygame.scrap.init ## + +.. function:: get_init + + | :sl:`Returns True if the scrap module is currently initialized.` + | :sg:`get_init() -> bool` + + Gets the scrap module's initialization state. + + :returns: ``True`` if the :mod:`pygame.scrap` module is currently + initialized, ``False`` otherwise + :rtype: bool + + .. versionadded:: 1.9.5 + + .. ## pygame.scrap.get_init ## + +.. function:: get + + | :sl:`Gets the data for the specified type from the clipboard.` + | :sg:`get(type) -> bytes | None` + + Retrieves the data for the specified type from the clipboard. The data is + returned as a byte string and might need further processing (such as + decoding to Unicode). + + :param string type: data type to retrieve from the clipboard + + :returns: data (bytes object) for the given type identifier or ``None`` if + no data for the given type is available + :rtype: bytes | None + + :: + + text = pygame.scrap.get(pygame.SCRAP_TEXT) + if text: + print("There is text in the clipboard.") + else: + print("There does not seem to be text in the clipboard.") + + .. ## pygame.scrap.get ## + +.. function:: get_types + + | :sl:`Gets a list of the available clipboard types.` + | :sg:`get_types() -> list` + + Gets a list of data type string identifiers for the data currently + available on the clipboard. Each identifier can be used in the + :func:`pygame.scrap.get()` method to get the clipboard content of the + specific type. + + :returns: list of strings of the available clipboard data types, if there + is no data in the clipboard an empty list is returned + :rtype: list + + :: + + for t in pygame.scrap.get_types(): + if "text" in t: + # There is some content with the word "text" in its type string. + print(pygame.scrap.get(t)) + + .. ## pygame.scrap.get_types ## + +.. function:: put + + | :sl:`Places data into the clipboard.` + | :sg:`put(type, data) -> None` + + Places data for a given clipboard type into the clipboard. The data must + be a string buffer. The type is a string identifying the type of data to be + placed into the clipboard. This can be one of the predefined + ``pygame.SCRAP_PBM``, ``pygame.SCRAP_PPM``, ``pygame.SCRAP_BMP`` or + ``pygame.SCRAP_TEXT`` values or a user defined string identifier. + + :param string type: type identifier of the data to be placed into the + clipboard + :param data: data to be place into the clipboard, a bytes object + :type data: bytes + + :raises pygame.error: if unable to put the data into the clipboard + + :: + + with open("example.bmp", "rb") as fp: + pygame.scrap.put(pygame.SCRAP_BMP, fp.read()) + # The image data is now on the clipboard for other applications to access + # it. + pygame.scrap.put(pygame.SCRAP_TEXT, b"A text to copy") + pygame.scrap.put("Plain text", b"Data for user defined type 'Plain text'") + + .. ## pygame.scrap.put ## + +.. function:: contains + + | :sl:`Checks whether data for a given type is available in the clipboard.` + | :sg:`contains(type) -> bool` + + Checks whether data for the given type is currently available in the + clipboard. + + :param string type: data type to check availability of + + :returns: ``True`` if data for the passed type is available in the + clipboard, ``False`` otherwise + :rtype: bool + + :: + + if pygame.scrap.contains(pygame.SCRAP_TEXT): + print("There is text in the clipboard.") + if pygame.scrap.contains("own_data_type"): + print("There is stuff in the clipboard.") + + .. ## pygame.scrap.contains ## + +.. function:: lost + + | :sl:`Indicates if the clipboard ownership has been lost by the pygame application.` + | :sg:`lost() -> bool` + + Indicates if the clipboard ownership has been lost by the pygame + application. + + :returns: ``True``, if the clipboard ownership has been lost by the pygame + application, ``False`` if the pygame application still owns the clipboard + :rtype: bool + + :: + + if pygame.scrap.lost(): + print("The clipboard is in use by another application.") + + .. ## pygame.scrap.lost ## + +.. function:: set_mode + + | :sl:`Sets the clipboard access mode.` + | :sg:`set_mode(mode) -> None` + + Sets the access mode for the clipboard. This is only of interest for X11 + environments where clipboard modes ``pygame.SCRAP_SELECTION`` (for mouse + selections) and ``pygame.SCRAP_CLIPBOARD`` (for the clipboard) are + available. Setting the mode to ``pygame.SCRAP_SELECTION`` in other + environments will not change the mode from ``pygame.SCRAP_CLIPBOARD``. + + :param mode: access mode, supported values are ``pygame.SCRAP_CLIPBOARD`` + and ``pygame.SCRAP_SELECTION`` (``pygame.SCRAP_SELECTION`` only has an + effect when used on X11 platforms) + + :raises ValueError: if the ``mode`` parameter is not + ``pygame.SCRAP_CLIPBOARD`` or ``pygame.SCRAP_SELECTION`` + + .. ## pygame.scrap.set_mode ## + +.. ## pygame.scrap ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/sdl2_controller.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/sdl2_controller.rst.txt new file mode 100644 index 00000000..dbea64a9 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/sdl2_controller.rst.txt @@ -0,0 +1,290 @@ +.. include:: common.txt + +:mod:`pygame._sdl2.controller` +============================== + +.. module:: pygame._sdl2.controller + :synopsis: pygame module to work with controllers + +| :sl:`Pygame module to work with controllers.` + +.. note:: + Use import pygame._sdl2.controller before using this module. + +This module offers control over common controller types like the dualshock 4 or +the xbox 360 controllers: They have two analog sticks, two triggers, two shoulder buttons, +a dpad, 4 buttons on the side, 2 (or 3) buttons in the middle. + +Pygame uses xbox controllers naming conventions (like a, b, x, y for buttons) but +they always refer to the same buttons. For example ``CONTROLLER_BUTTON_X`` is +always the leftmost button of the 4 buttons on the right. + +Controllers can generate the following events:: + + CONTROLLERAXISMOTION, CONTROLLERBUTTONDOWN, CONTROLLERBUTTONUP, + CONTROLLERDEVICEREMAPPED, CONTROLLERDEVICEADDED, CONTROLLERDEVICEREMOVED + +Additionally if pygame is built with SDL 2.0.14 or higher the following events can also be generated +(to get the version of sdl pygame is built with use :meth:`pygame.version.SDL`):: + + CONTROLLERTOUCHPADDOWN, CONTROLLERTOUCHPADMOTION, CONTROLLERTOUCHPADUP + +These events can be enabled/disabled by :meth:`pygame._sdl2.controller.set_eventstate` +Note that controllers can generate joystick events as well. This function only toggles +events related to controllers. + +.. note:: + See the :mod:`pygame.joystick` for a more versatile but more advanced api. + +.. versionadded:: 2 This module requires SDL2. + +.. function:: init + + | :sl:`initialize the controller module` + | :sg:`init() -> None` + + Initialize the controller module. + + .. ## pygame._sdl2.controller.init ## + +.. function:: quit + + | :sl:`Uninitialize the controller module.` + | :sg:`quit() -> None` + + Uninitialize the controller module. + + .. ## pygame._sdl2.controller.quit ## + +.. function:: get_init + + | :sl:`Returns True if the controller module is initialized.` + | :sg:`get_init() -> bool` + + Test if ``pygame._sdl2.controller.init()`` was called. + + .. ## pygame._sdl2.controller.get_init ## + +.. function:: set_eventstate + + | :sl:`Sets the current state of events related to controllers` + | :sg:`set_eventstate(state) -> None` + + Enable or disable events connected to controllers. + + .. note:: + Controllers can still generate joystick events, which will not be toggled by this function. + + .. versionchanged:: 2.0.2: Changed return type from int to None + + .. ## pygame._sdl2.controller.set_eventstate ## + +.. function:: get_eventstate + + | :sl:`Gets the current state of events related to controllers` + | :sg:`get_eventstate() -> bool` + + Returns the current state of events related to controllers, True meaning + events will be posted. + + .. versionadded:: 2.0.2 + + .. ## pygame._sdl2.controller.get_eventstate ## + +.. function:: get_count + + | :sl:`Get the number of joysticks connected` + | :sg:`get_count() -> int` + + Get the number of joysticks connected. + + .. ## pygame._sdl2.controller.get_count ## + +.. function:: is_controller + + | :sl:`Check if the given joystick is supported by the game controller interface` + | :sg:`is_controller(index) -> bool` + + Returns True if the index given can be used to create a controller object. + + .. ## pygame._sdl2.controller.is_controller ## + +.. function:: name_forindex + + | :sl:`Get the name of the controller` + | :sg:`name_forindex(index) -> name or None` + + Returns the name of controller, or None if there's no name or the + index is invalid. + + .. ## pygame._sdl2.controller.name_forindex ## + +.. class:: Controller + + | :sl:`Create a new Controller object.` + | :sg:`Controller(index) -> Controller` + + Create a new Controller object. Index should be integer between + 0 and ``pygame._sdl2.controller.get_count()``. Controllers also + can be created from a ``pygame.joystick.Joystick`` using + ``pygame._sdl2.controller.from_joystick``. Controllers are + initialized on creation. + + .. method:: quit + + | :sl:`uninitialize the Controller` + | :sg:`quit() -> None` + + Close a Controller object. After this the pygame event queue will no longer + receive events from the device. + + It is safe to call this more than once. + + .. ## Controller.quit ## + + .. method:: get_init + + | :sl:`check if the Controller is initialized` + | :sg:`get_init() -> bool` + + Returns True if the Controller object is currently initialised. + + .. ## Controller.get_init ## + + .. staticmethod:: from_joystick + + | :sl:`Create a Controller from a pygame.joystick.Joystick object` + | :sg:`from_joystick(joystick) -> Controller` + + Create a Controller object from a ``pygame.joystick.Joystick`` object + + .. ## Controller.from_joystick ## + + .. method:: attached + + | :sl:`Check if the Controller has been opened and is currently connected.` + | :sg:`attached() -> bool` + + Returns True if the Controller object is opened and connected. + + .. ## Controller.attached ## + + .. method:: as_joystick + + | :sl:`Returns a pygame.joystick.Joystick() object` + | :sg:`as_joystick() -> Joystick object` + + Returns a pygame.joystick.Joystick() object created from this controller's index + + .. ## Controller.as_joystick ## + + .. method:: get_axis + + | :sl:`Get the current state of a joystick axis` + | :sg:`get_axis(axis) -> int` + + Get the current state of a trigger or joystick axis. + The axis argument must be one of the following constants:: + + CONTROLLER_AXIS_LEFTX, CONTROLLER_AXIS_LEFTY, + CONTROLLER_AXIS_RIGHTX, CONTROLLER_AXIS_RIGHTY, + CONTROLLER_AXIS_TRIGGERLEFT, CONTROLLER_AXIS_TRIGGERRIGHT + + Joysticks can return a value between -32768 and 32767. Triggers however + can only return a value between 0 and 32768. + + .. ## Controller.get_axis ## + + .. method:: get_button + + | :sl:`Get the current state of a button` + | :sg:`get_button(button) -> bool` + + Get the current state of a button, True meaning it is pressed down. + The button argument must be one of the following constants:: + + CONTROLLER_BUTTON_A, CONTROLLER_BUTTON_B, + CONTROLLER_BUTTON_X, CONTROLLER_BUTTON_Y + CONTROLLER_BUTTON_DPAD_UP, CONTROLLER_BUTTON_DPAD_DOWN, + CONTROLLER_BUTTON_DPAD_LEFT, CONTROLLER_BUTTON_DPAD_RIGHT, + CONTROLLER_BUTTON_LEFTSHOULDER, CONTROLLER_BUTTON_RIGHTSHOULDER, + CONTROLLER_BUTTON_LEFTSTICK, CONTROLLER_BUTTON_RIGHTSTICK, + CONTROLLER_BUTTON_BACK, CONTROLLER_BUTTON_GUIDE, + CONTROLLER_BUTTON_START + + + .. ## Controller.get_button ## + + .. method:: get_mapping + + | :sl:`Get the mapping assigned to the controller` + | :sg:`get_mapping() -> mapping` + + Returns a dict containing the mapping of the Controller. For more + information see :meth:`Controller.set_mapping()` + + .. versionchanged:: 2.0.2: Return type changed from ``str`` to ``dict`` + + .. ## Contorller.get_mapping ## + + .. method:: set_mapping + + | :sl:`Assign a mapping to the controller` + | :sg:`set_mapping(mapping) -> int` + + Rebind buttons, axes, triggers and dpads. The mapping should be a + dict containing all buttons, hats and axes. The easiest way to get this + is to use the dict returned by :meth:`Controller.get_mapping`. To edit + this mapping assign a value to the original button. The value of the + dictionary must be a button, hat or axis represented in the following way: + + * For a button use: bX where X is the index of the button. + * For a hat use: hX.Y where X is the index and the Y is the direction (up: 1, right: 2, down: 3, left: 4). + * For an axis use: aX where x is the index of the axis. + + An example of mapping:: + + mapping = controller.get_mapping() # Get current mapping + mapping["a"] = "b3" # Remap button a to y + mapping["y"] = "b0" # Remap button y to a + controller.set_mapping(mapping) # Set the mapping + + + The function will return 1 if a new mapping is added or 0 if an existing one is updated. + + .. versionchanged:: 2.0.2: Renamed from ``add_mapping`` to ``set_mapping`` + .. versionchanged:: 2.0.2: Argument type changed from ``str`` to ``dict`` + + .. ## Contorller.set_mapping ## + + .. method:: rumble + + | :sl:`Start a rumbling effect` + | :sg:`rumble(low_frequency, high_frequency, duration) -> bool` + + Start a rumble effect on the controller, with the specified strength ranging + from 0 to 1. Duration is length of the effect, in ms. Setting the duration + to 0 will play the effect until another one overwrites it or + :meth:`Controller.stop_rumble` is called. If an effect is already + playing, then it will be overwritten. + + Returns True if the rumble was played successfully or False if the + controller does not support it or :meth:`pygame.version.SDL` is below 2.0.9. + + .. versionadded:: 2.0.2 + + .. ## Contorller.rumble ## + + .. method:: stop_rumble + + | :sl:`Stop any rumble effect playing` + | :sg:`stop_rumble() -> None` + + Stops any rumble effect playing on the controller. See + :meth:`Controller.rumble` for more information. + + .. versionadded:: 2.0.2 + + .. ## Contorller.stop_rumble ## + +.. ## pygame._sdl2.controller ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/sdl2_video.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/sdl2_video.rst.txt new file mode 100644 index 00000000..e273363b --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/sdl2_video.rst.txt @@ -0,0 +1,334 @@ +.. include:: common.txt + +:mod:`pygame.sdl2_video` +======================== + +.. module:: pygame._sdl2.video + :synopsis: Experimental pygame module for porting new SDL video systems + +.. warning:: + This module isn't ready for prime time yet, it's still in development. + These docs are primarily meant to help the pygame developers and super-early adopters + who are in communication with the developers. This API will change. + +| :sl:`Experimental pygame module for porting new SDL video systems` + +.. class:: Window + + | :sl:`pygame object that represents a window` + | :sg:`Window(title="pygame", size=(640, 480), position=None, fullscreen=False, fullscreen_desktop=False, keywords) -> Window` + + .. classmethod:: from_display_module + + | :sl:`Creates window using window created by pygame.display.set_mode().` + | :sg:`from_display_module() -> Window` + + .. classmethod:: from_window + + | :sl:`Create Window from another window. Could be from another UI toolkit.` + | :sg:`from_window(other) -> Window` + + .. attribute:: grab + + | :sl:`Gets or sets whether the mouse is confined to the window.` + | :sg:`grab -> bool` + + .. attribute:: relative_mouse + + | :sl:`Gets or sets the window's relative mouse motion state.` + | :sg:`relative_mouse -> bool` + + .. method:: set_windowed + + | :sl:`Enable windowed mode (exit fullscreen).` + | :sg:`set_windowed() -> None` + + .. method:: set_fullscreen + + | :sl:`Enter fullscreen.` + | :sg:`set_fullscreen(desktop=False) -> None` + + .. attribute:: title + + | :sl:`Gets or sets whether the window title.` + | :sg:`title -> string` + + .. method:: destroy + + | :sl:`Destroys the window.` + | :sg:`destroy() -> None` + + .. method:: hide + + | :sl:`Hide the window.` + | :sg:`hide() -> None` + + .. method:: show + + | :sl:`Show the window.` + | :sg:`show() -> None` + + .. method:: focus + + | :sl:`Raise the window above other windows and set the input focus. The "input_only" argument is only supported on X11.` + | :sg:`focus(input_only=False) -> None` + + .. method:: restore + + | :sl:`Restore the size and position of a minimized or maximized window.` + | :sg:`restore() -> None` + + .. method:: maximize + + | :sl:`Maximize the window.` + | :sg:`maximize() -> None` + + .. method:: minimize + + | :sl:`Minimize the window.` + | :sg:`maximize() -> None` + + .. attribute:: resizable + + | :sl:`Gets and sets whether the window is resizable.` + | :sg:`resizable -> bool` + + .. attribute:: borderless + + | :sl:`Add or remove the border from the window.` + | :sg:`borderless -> bool` + + .. method:: set_icon + + | :sl:`Set the icon for the window.` + | :sg:`set_icon(surface) -> None` + + .. attribute:: id + + | :sl:`Get the unique window ID. *Read-only*` + | :sg:`id -> int` + + .. attribute:: size + + | :sl:`Gets and sets the window size.` + | :sg:`size -> (int, int)` + + .. attribute:: position + + | :sl:`Gets and sets the window position.` + | :sg:`position -> (int, int) or WINDOWPOS_CENTERED or WINDOWPOS_UNDEFINED` + + .. attribute:: opacity + + | :sl:`Gets and sets the window opacity. Between 0.0 (fully transparent) and 1.0 (fully opaque).` + | :sg:`opacity -> float` + + .. attribute:: display_index + + | :sl:`Get the index of the display that owns the window. *Read-only*` + | :sg:`display_index -> int` + + .. method:: set_modal_for + + | :sl:`Set the window as a modal for a parent window. This function is only supported on X11.` + | :sg:`set_modal_for(Window) -> None` + +.. class:: Texture + + | :sl:`pygame object that representing a Texture.` + | :sg:`Texture(renderer, size, depth=0, static=False, streaming=False, target=False) -> Texture` + + .. staticmethod:: from_surface + + | :sl:`Create a texture from an existing surface.` + | :sg:`from_surface(renderer, surface) -> Texture` + + .. attribute:: renderer + + | :sl:`Gets the renderer associated with the Texture. *Read-only*` + | :sg:`renderer -> Renderer` + + .. attribute:: width + + | :sl:`Gets the width of the Texture. *Read-only*` + | :sg:`width -> int` + + .. attribute:: height + + | :sl:`Gets the height of the Texture. *Read-only*` + | :sg:`height -> int` + + .. attribute:: alpha + + | :sl:`Gets and sets an additional alpha value multiplied into render copy operations.` + | :sg:`alpha -> int` + + .. attribute:: blend_mode + + | :sl:`Gets and sets the blend mode for the Texture.` + | :sg:`blend_mode -> int` + + .. attribute:: color + + | :sl:`Gets and sets an additional color value multiplied into render copy operations.` + | :sg:`color -> color` + + .. method:: get_rect + + | :sl:`Get the rectangular area of the texture.` + | :sg:`get_rect(**kwargs) -> Rect` + + .. method:: draw + + | :sl:`Copy a portion of the texture to the rendering target.` + | :sg:`draw(srcrect=None, dstrect=None, angle=0, origin=None, flip_x=False, flip_y=False) -> None` + + .. method:: update + + | :sl:`Update the texture with a Surface. WARNING: Slow operation, use sparingly.` + | :sg:`update(surface, area=None) -> None` + +.. class:: Image + + | :sl:`Easy way to use a portion of a Texture without worrying about srcrect all the time.` + | :sg:`Image(textureOrImage, srcrect=None) -> Image` + + .. method:: get_rect + + | :sl:`Get the rectangular area of the Image.` + | :sg:`get_rect() -> Rect` + + .. method:: draw + + | :sl:`Copy a portion of the Image to the rendering target.` + | :sg:`draw(srcrect=None, dstrect=None) -> None` + + .. attribute:: angle + + | :sl:`Gets and sets the angle the Image draws itself with.` + | :sg:`angle -> float` + + .. attribute:: origin + + | :sl:`Gets and sets the origin. Origin=None means the Image will be rotated around its center.` + | :sg:`origin -> (float, float) or None.` + + .. attribute:: flip_x + + | :sl:`Gets and sets whether the Image is flipped on the x axis.` + | :sg:`flip_x -> bool` + + .. attribute:: flip_y + + | :sl:`Gets and sets whether the Image is flipped on the y axis.` + | :sg:`flip_y -> bool` + + .. attribute:: color + + | :sl:`Gets and sets the Image color modifier.` + | :sg:`color -> Color` + + .. attribute:: alpha + + | :sl:`Gets and sets the Image alpha modifier.` + | :sg:`alpha -> float` + + .. attribute:: blend_mode + + | :sl:`Gets and sets the blend mode for the Image.` + | :sg:`blend_mode -> int` + + .. attribute:: texture + + | :sl:`Gets and sets the Texture the Image is based on.` + | :sg:`texture -> Texture` + + .. attribute:: srcrect + + | :sl:`Gets and sets the Rect the Image is based on.` + | :sg:`srcrect -> Rect` + +.. class:: Renderer + + | :sl:`Create a 2D rendering context for a window.` + | :sg:`Renderer(window, index=-1, accelerated=-1, vsync=False, target_texture=False) -> Renderer` + + .. classmethod:: from_window + + | :sl:`Easy way to create a Renderer.` + | :sg:`from_window(window) -> Renderer` + + .. attribute:: draw_blend_mode + + | :sl:`Gets and sets the blend mode used by the drawing functions.` + | :sg:`draw_blend_mode -> int` + + .. attribute:: draw_color + + | :sl:`Gets and sets the color used by the drawing functions.` + | :sg:`draw_color -> Color` + + .. method:: clear + + | :sl:`Clear the current rendering target with the drawing color.` + | :sg:`clear() -> None` + + .. method:: present + + | :sl:`Updates the screen with any new rendering since previous call.` + | :sg:`present() -> None` + + .. method:: get_viewport + + | :sl:`Returns the drawing area on the target.` + | :sg:`get_viewport() -> Rect` + + .. method:: set_viewport + + | :sl:`Set the drawing area on the target. If area is None, the entire target will be used.` + | :sg:`set_viewport(area) -> None` + + .. attribute:: logical_size + + | :sl:`Gets and sets the logical size.` + | :sg:`logical_size -> (int width, int height)` + + .. attribute:: scale + + | :sl:`Gets and sets the scale.` + | :sg:`scale -> (float x_scale, float y_scale)` + + .. attribute:: target + + | :sl:`Gets and sets the render target. None represents the default target (the renderer).` + | :sg:`target -> Texture or None` + + .. method:: blit + + | :sl:`For compatibility purposes. Textures created by different Renderers cannot be shared!` + | :sg:`blit(source, dest, area=None, special_flags=0)-> Rect` + + .. method:: draw_line + + | :sl:`Draws a line.` + | :sg:`draw_line(p1, p2) -> None` + + .. method:: draw_point + + | :sl:`Draws a point.` + | :sg:`draw_point(point) -> None` + + .. method:: draw_rect + + | :sl:`Draws a rectangle.` + | :sg:`draw_rect(rect)-> None` + + .. method:: fill_rect + + | :sl:`Fills a rectangle.` + | :sg:`fill_rect(rect)-> None` + + .. method:: to_surface + + | :sl:`Read pixels from current render target and create a pygame.Surface. WARNING: Slow operation, use sparingly.` + | :sg:`to_surface(surface=None, area=None)-> Surface` \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/sndarray.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/sndarray.rst.txt new file mode 100644 index 00000000..2a177649 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/sndarray.rst.txt @@ -0,0 +1,95 @@ +.. include:: common.txt + +:mod:`pygame.sndarray` +====================== + +.. module:: pygame.sndarray + :synopsis: pygame module for accessing sound sample data + +| :sl:`pygame module for accessing sound sample data` + +Functions to convert between NumPy arrays and Sound objects. This +module will only be functional when pygame can use the external NumPy +package. If NumPy can't be imported, ``surfarray`` becomes a ``MissingModule`` +object. + +Sound data is made of thousands of samples per second, and each sample is the +amplitude of the wave at a particular moment in time. For example, in 22-kHz +format, element number 5 of the array is the amplitude of the wave after +5/22000 seconds. + +The arrays are indexed by the ``X`` axis first, followed by the ``Y`` axis. +Each sample is an 8-bit or 16-bit integer, depending on the data format. A +stereo sound file has two values per sample, while a mono sound file only has +one. + +.. function:: array + + | :sl:`copy Sound samples into an array` + | :sg:`array(Sound) -> array` + + Creates a new array for the sound data and copies the samples. The array + will always be in the format returned from ``pygame.mixer.get_init()``. + + .. ## pygame.sndarray.array ## + +.. function:: samples + + | :sl:`reference Sound samples into an array` + | :sg:`samples(Sound) -> array` + + Creates a new array that directly references the samples in a Sound object. + Modifying the array will change the Sound. The array will always be in the + format returned from ``pygame.mixer.get_init()``. + + .. ## pygame.sndarray.samples ## + +.. function:: make_sound + + | :sl:`convert an array into a Sound object` + | :sg:`make_sound(array) -> Sound` + + Create a new playable Sound object from an array. The mixer module must be + initialized and the array format must be similar to the mixer audio format. + + .. ## pygame.sndarray.make_sound ## + +.. function:: use_arraytype + + | :sl:`Sets the array system to be used for sound arrays` + | :sg:`use_arraytype (arraytype) -> None` + + DEPRECATED: Uses the requested array type for the module functions. The + only supported arraytype is ``'numpy'``. Other values will raise ValueError. + Using this function will raise a ``DeprecationWarning``. + .. ## pygame.sndarray.use_arraytype ## + +.. function:: get_arraytype + + | :sl:`Gets the currently active array type.` + | :sg:`get_arraytype () -> str` + + DEPRECATED: Returns the currently active array type. This will be a value of the + ``get_arraytypes()`` tuple and indicates which type of array module is used + for the array creation. Using this function will raise a ``DeprecationWarning``. + + .. versionadded:: 1.8 + + .. ## pygame.sndarray.get_arraytype ## + +.. function:: get_arraytypes + + | :sl:`Gets the array system types currently supported.` + | :sg:`get_arraytypes () -> tuple` + + DEPRECATED: Checks, which array systems are available and returns them as a tuple of + strings. The values of the tuple can be used directly in the + :func:`pygame.sndarray.use_arraytype` () method. If no supported array + system could be found, None will be returned. Using this function will raise a + ``DeprecationWarning``. + + .. versionadded:: 1.8 + + .. ## pygame.sndarray.get_arraytypes ## + +.. ## pygame.sndarray ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/sprite.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/sprite.rst.txt new file mode 100644 index 00000000..82e8cbb1 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/sprite.rst.txt @@ -0,0 +1,895 @@ +.. include:: common.txt + +:mod:`pygame.sprite` +==================== + +.. module:: pygame.sprite + :synopsis: pygame module with basic game object classes + +| :sl:`pygame module with basic game object classes` + +This module contains several simple classes to be used within games. There is +the main Sprite class and several Group classes that contain Sprites. The use +of these classes is entirely optional when using pygame. The classes are fairly +lightweight and only provide a starting place for the code that is common to +most games. + +The Sprite class is intended to be used as a base class for the different types +of objects in the game. There is also a base Group class that simply stores +sprites. A game could create new types of Group classes that operate on +specially customized Sprite instances they contain. + +The basic Group class can draw the Sprites it contains to a Surface. The +``Group.draw()`` method requires that each Sprite have a ``Surface.image`` +attribute and a ``Surface.rect``. The ``Group.clear()`` method requires these +same attributes, and can be used to erase all the Sprites with background. +There are also more advanced Groups: ``pygame.sprite.RenderUpdates()`` and +``pygame.sprite.OrderedUpdates()``. + +Lastly, this module contains several collision functions. These help find +sprites inside multiple groups that have intersecting bounding rectangles. To +find the collisions, the Sprites are required to have a ``Surface.rect`` +attribute assigned. + +The groups are designed for high efficiency in removing and adding Sprites to +them. They also allow cheap testing to see if a Sprite already exists in a +Group. A given Sprite can exist in any number of groups. A game could use some +groups to control object rendering, and a completely separate set of groups to +control interaction or player movement. Instead of adding type attributes or +bools to a derived Sprite class, consider keeping the Sprites inside organized +Groups. This will allow for easier lookup later in the game. + +Sprites and Groups manage their relationships with the ``add()`` and +``remove()`` methods. These methods can accept a single or multiple targets for +membership. The default initializers for these classes also takes a single or +list of targets for initial membership. It is safe to repeatedly add and remove +the same Sprite from a Group. + +While it is possible to design sprite and group classes that don't derive from +the Sprite and AbstractGroup classes below, it is strongly recommended that you +extend those when you add a Sprite or Group class. + +Sprites are not thread safe. So lock them yourself if using threads. + +.. class:: Sprite + + | :sl:`Simple base class for visible game objects.` + | :sg:`Sprite(*groups) -> Sprite` + + The base class for visible game objects. Derived classes will want to + override the ``Sprite.update()`` and assign a ``Sprite.image`` and + ``Sprite.rect`` attributes. The initializer can accept any number of Group + instances to be added to. + + When subclassing the Sprite, be sure to call the base initializer before + adding the Sprite to Groups. For example: + + .. code-block:: python + + class Block(pygame.sprite.Sprite): + + # Constructor. Pass in the color of the block, + # and its x and y position + def __init__(self, color, width, height): + # Call the parent class (Sprite) constructor + pygame.sprite.Sprite.__init__(self) + + # Create an image of the block, and fill it with a color. + # This could also be an image loaded from the disk. + self.image = pygame.Surface([width, height]) + self.image.fill(color) + + # Fetch the rectangle object that has the dimensions of the image + # Update the position of this object by setting the values of rect.x and rect.y + self.rect = self.image.get_rect() + + .. method:: update + + | :sl:`method to control sprite behavior` + | :sg:`update(*args, **kwargs) -> None` + + The default implementation of this method does nothing; it's just a + convenient "hook" that you can override. This method is called by + ``Group.update()`` with whatever arguments you give it. + + There is no need to use this method if not using the convenience method + by the same name in the Group class. + + .. ## Sprite.update ## + + .. method:: add + + | :sl:`add the sprite to groups` + | :sg:`add(*groups) -> None` + + Any number of Group instances can be passed as arguments. The Sprite will + be added to the Groups it is not already a member of. + + .. ## Sprite.add ## + + .. method:: remove + + | :sl:`remove the sprite from groups` + | :sg:`remove(*groups) -> None` + + Any number of Group instances can be passed as arguments. The Sprite will + be removed from the Groups it is currently a member of. + + .. ## Sprite.remove ## + + .. method:: kill + + | :sl:`remove the Sprite from all Groups` + | :sg:`kill() -> None` + + The Sprite is removed from all the Groups that contain it. This won't + change anything about the state of the Sprite. It is possible to continue + to use the Sprite after this method has been called, including adding it + to Groups. + + .. ## Sprite.kill ## + + .. method:: alive + + | :sl:`does the sprite belong to any groups` + | :sg:`alive() -> bool` + + Returns True when the Sprite belongs to one or more Groups. + + .. ## Sprite.alive ## + + .. method:: groups + + | :sl:`list of Groups that contain this Sprite` + | :sg:`groups() -> group_list` + + Return a list of all the Groups that contain this Sprite. + + .. ## Sprite.groups ## + + .. ## pygame.sprite.Sprite ## + +.. class:: WeakSprite + + | :sl:`A subclass of Sprite that references its Groups weakly. This means that any group this belongs to that is not referenced anywhere else is garbage collected automatically.` + | :sg:`WeakSprite(*groups) -> WeakSprite` + +.. class:: DirtySprite + + | :sl:`A subclass of Sprite with more attributes and features.` + | :sg:`DirtySprite(*groups) -> DirtySprite` + + Extra DirtySprite attributes with their default values: + + dirty = 1 + + :: + + if set to 1, it is repainted and then set to 0 again + if set to 2 then it is always dirty ( repainted each frame, + flag is not reset) + 0 means that it is not dirty and therefore not repainted again + + blendmode = 0 + + :: + + its the special_flags argument of blit, blendmodes + + source_rect = None + + :: + + source rect to use, remember that it is relative to + topleft (0,0) of self.image + + visible = 1 + + :: + + normally 1, if set to 0 it will not be repainted + (you must set it dirty too to be erased from screen) + + layer = 0 + + :: + + (READONLY value, it is read when adding it to the + LayeredDirty, for details see doc of LayeredDirty) + + .. ## ## + + .. ## pygame.sprite.DirtySprite ## + +.. class:: Group + + | :sl:`A container class to hold and manage multiple Sprite objects.` + | :sg:`Group(*sprites) -> Group` + + A simple container for Sprite objects. This class can be inherited to create + containers with more specific behaviors. The constructor takes any number of + Sprite arguments to add to the Group. The group supports the following + standard Python operations: + + :: + + in test if a Sprite is contained + len the number of Sprites contained + bool test if any Sprites are contained + iter iterate through all the Sprites + + The Sprites in the Group are ordered only on python 3.6 and higher. + Below python 3.6 drawing and iterating over the Sprites is in no particular order. + + .. method:: sprites + + | :sl:`list of the Sprites this Group contains` + | :sg:`sprites() -> sprite_list` + + Return a list of all the Sprites this group contains. You can also get an + iterator from the group, but you cannot iterate over a Group while + modifying it. + + .. ## Group.sprites ## + + .. method:: copy + + | :sl:`duplicate the Group` + | :sg:`copy() -> Group` + + Creates a new Group with all the same Sprites as the original. If you + have subclassed Group, the new object will have the same (sub-)class as + the original. This only works if the derived class's constructor takes + the same arguments as the Group class's. + + .. ## Group.copy ## + + .. method:: add + + | :sl:`add Sprites to this Group` + | :sg:`add(*sprites) -> None` + + Add any number of Sprites to this Group. This will only add Sprites that + are not already members of the Group. + + Each sprite argument can also be a iterator containing Sprites. + + .. ## Group.add ## + + .. method:: remove + + | :sl:`remove Sprites from the Group` + | :sg:`remove(*sprites) -> None` + + Remove any number of Sprites from the Group. This will only remove + Sprites that are already members of the Group. + + Each sprite argument can also be a iterator containing Sprites. + + .. ## Group.remove ## + + .. method:: has + + | :sl:`test if a Group contains Sprites` + | :sg:`has(*sprites) -> bool` + + Return True if the Group contains all of the given sprites. This is + similar to using the "in" operator on the Group ("if sprite in group: + ..."), which tests if a single Sprite belongs to a Group. + + Each sprite argument can also be a iterator containing Sprites. + + .. ## Group.has ## + + .. method:: update + + | :sl:`call the update method on contained Sprites` + | :sg:`update(*args, **kwargs) -> None` + + Calls the ``update()`` method on all Sprites in the Group. The base + Sprite class has an update method that takes any number of arguments and + does nothing. The arguments passed to ``Group.update()`` will be passed + to each Sprite. + + There is no way to get the return value from the ``Sprite.update()`` + methods. + + .. ## Group.update ## + + .. method:: draw + + | :sl:`blit the Sprite images` + | :sg:`draw(Surface, bgsurf=None, special_flags=0) -> List[Rect]` + + Draws the contained Sprites to the Surface argument. This uses the + ``Sprite.image`` attribute for the source surface, and ``Sprite.rect`` + for the position. ``special_flags`` is passed to ``Surface.blit()``. + ``bgsurf`` is unused in this method but ``LayeredDirty.draw()`` uses + it. + + The Group does not keep sprites in any order, so the draw order is + arbitrary. + + .. ## Group.draw ## + + .. method:: clear + + | :sl:`draw a background over the Sprites` + | :sg:`clear(Surface_dest, background) -> None` + + Erases the Sprites used in the last ``Group.draw()`` call. The + destination Surface is cleared by filling the drawn Sprite positions with + the background. + + The background is usually a Surface image the same dimensions as the + destination Surface. However, it can also be a callback function that + takes two arguments; the destination Surface and an area to clear. The + background callback function will be called several times each clear. + + Here is an example callback that will clear the Sprites with solid red: + + :: + + def clear_callback(surf, rect): + color = 255, 0, 0 + surf.fill(color, rect) + + .. ## Group.clear ## + + .. method:: empty + + | :sl:`remove all Sprites` + | :sg:`empty() -> None` + + Removes all Sprites from this Group. + + .. ## Group.empty ## + + .. ## pygame.sprite.Group ## + +.. class:: WeakDirtySprite + + | :sl:`A subclass of WeakSprite and DirtySprite that combines the benefits of both classes.` + | :sg:`WeakDirtySprite(*groups) -> WeakDirtySprite` + +.. class:: RenderPlain + + | :sl:`Same as pygame.sprite.Group` + + This class is an alias to ``pygame.sprite.Group()``. It has no additional functionality. + + .. ## pygame.sprite.RenderClear ## + +.. class:: RenderClear + + | :sl:`Same as pygame.sprite.Group` + + This class is an alias to ``pygame.sprite.Group()``. It has no additional functionality. + + .. ## pygame.sprite.RenderClear ## + +.. class:: RenderUpdates + + | :sl:`Group sub-class that tracks dirty updates.` + | :sg:`RenderUpdates(*sprites) -> RenderUpdates` + + This class is derived from ``pygame.sprite.Group()``. It has an extended + ``draw()`` method that tracks the changed areas of the screen. + + .. method:: draw + + | :sl:`blit the Sprite images and track changed areas` + | :sg:`draw(surface, bgsurf=None, special_flags=0) -> Rect_list` + + Draws all the Sprites to the surface, the same as ``Group.draw()``. This + method also returns a list of Rectangular areas on the screen that have + been changed. The returned changes include areas of the screen that have + been affected by previous ``Group.clear()`` calls. ``special_flags`` is + passed to ``Surface.blit()``. + + The returned Rect list should be passed to ``pygame.display.update()``. + This will help performance on software driven display modes. This type of + updating is usually only helpful on destinations with non-animating + backgrounds. + + .. ## RenderUpdates.draw ## + + .. ## pygame.sprite.RenderUpdates ## + +.. function:: OrderedUpdates + + | :sl:`RenderUpdates sub-class that draws Sprites in order of addition.` + | :sg:`OrderedUpdates(*sprites) -> OrderedUpdates` + + This class derives from ``pygame.sprite.RenderUpdates()``. It maintains the + order in which the Sprites were added to the Group for rendering. This makes + adding and removing Sprites from the Group a little slower than regular + Groups. + + .. ## pygame.sprite.OrderedUpdates ## + +.. class:: LayeredUpdates + + | :sl:`LayeredUpdates is a sprite group that handles layers and draws like OrderedUpdates.` + | :sg:`LayeredUpdates(*sprites, **kwargs) -> LayeredUpdates` + + This group is fully compatible with :class:`pygame.sprite.Sprite`. + + You can set the default layer through kwargs using 'default_layer' and an + integer for the layer. The default layer is 0. + + If the sprite you add has an attribute _layer then that layer will be used. + If the \**kwarg contains 'layer' then the sprites passed will be added to + that layer (overriding the ``sprite.layer`` attribute). If neither sprite + has attribute layer nor \**kwarg then the default layer is used to add the + sprites. + + .. versionadded:: 1.8 + + .. method:: add + + | :sl:`add a sprite or sequence of sprites to a group` + | :sg:`add(*sprites, **kwargs) -> None` + + If the ``sprite(s)`` have an attribute layer then that is used for the + layer. If \**kwargs contains 'layer' then the ``sprite(s)`` will be added + to that argument (overriding the sprite layer attribute). If neither is + passed then the ``sprite(s)`` will be added to the default layer. + + .. ## LayeredUpdates.add ## + + .. method:: sprites + + | :sl:`returns a ordered list of sprites (first back, last top).` + | :sg:`sprites() -> sprites` + + .. ## LayeredUpdates.sprites ## + + .. method:: draw + + | :sl:`draw all sprites in the right order onto the passed surface.` + | :sg:`draw(surface, bgsurf=None, special_flags=0) -> Rect_list` + + .. ## LayeredUpdates.draw ## + + .. method:: get_sprites_at + + | :sl:`returns a list with all sprites at that position.` + | :sg:`get_sprites_at(pos) -> colliding_sprites` + + Bottom sprites first, top last. + + .. ## LayeredUpdates.get_sprites_at ## + + .. method:: get_sprite + + | :sl:`returns the sprite at the index idx from the groups sprites` + | :sg:`get_sprite(idx) -> sprite` + + Raises IndexOutOfBounds if the idx is not within range. + + .. ## LayeredUpdates.get_sprite ## + + .. method:: remove_sprites_of_layer + + | :sl:`removes all sprites from a layer and returns them as a list.` + | :sg:`remove_sprites_of_layer(layer_nr) -> sprites` + + .. ## LayeredUpdates.remove_sprites_of_layer ## + + .. method:: layers + + | :sl:`returns a list of layers defined (unique), sorted from bottom up.` + | :sg:`layers() -> layers` + + .. ## LayeredUpdates.layers ## + + .. method:: change_layer + + | :sl:`changes the layer of the sprite` + | :sg:`change_layer(sprite, new_layer) -> None` + + sprite must have been added to the renderer. It is not checked. + + .. ## LayeredUpdates.change_layer ## + + .. method:: get_layer_of_sprite + + | :sl:`returns the layer that sprite is currently in.` + | :sg:`get_layer_of_sprite(sprite) -> layer` + + If the sprite is not found then it will return the default layer. + + .. ## LayeredUpdates.get_layer_of_sprite ## + + .. method:: get_top_layer + + | :sl:`returns the top layer` + | :sg:`get_top_layer() -> layer` + + .. ## LayeredUpdates.get_top_layer ## + + .. method:: get_bottom_layer + + | :sl:`returns the bottom layer` + | :sg:`get_bottom_layer() -> layer` + + .. ## LayeredUpdates.get_bottom_layer ## + + .. method:: move_to_front + + | :sl:`brings the sprite to front layer` + | :sg:`move_to_front(sprite) -> None` + + Brings the sprite to front, changing sprite layer to topmost layer (added + at the end of that layer). + + .. ## LayeredUpdates.move_to_front ## + + .. method:: move_to_back + + | :sl:`moves the sprite to the bottom layer` + | :sg:`move_to_back(sprite) -> None` + + Moves the sprite to the bottom layer, moving it behind all other layers + and adding one additional layer. + + .. ## LayeredUpdates.move_to_back ## + + .. method:: get_top_sprite + + | :sl:`returns the topmost sprite` + | :sg:`get_top_sprite() -> Sprite` + + .. ## LayeredUpdates.get_top_sprite ## + + .. method:: get_sprites_from_layer + + | :sl:`returns all sprites from a layer, ordered by how they where added` + | :sg:`get_sprites_from_layer(layer) -> sprites` + + Returns all sprites from a layer, ordered by how they where added. It + uses linear search and the sprites are not removed from layer. + + .. ## LayeredUpdates.get_sprites_from_layer ## + + .. method:: switch_layer + + | :sl:`switches the sprites from layer1 to layer2` + | :sg:`switch_layer(layer1_nr, layer2_nr) -> None` + + The layers number must exist, it is not checked. + + .. ## LayeredUpdates.switch_layer ## + + .. ## pygame.sprite.LayeredUpdates ## + +.. class:: LayeredDirty + + | :sl:`LayeredDirty group is for DirtySprite objects. Subclasses LayeredUpdates.` + | :sg:`LayeredDirty(*sprites, **kwargs) -> LayeredDirty` + + This group requires :class:`pygame.sprite.DirtySprite` or any sprite that + has the following attributes: + + :: + + image, rect, dirty, visible, blendmode (see doc of DirtySprite). + + It uses the dirty flag technique and is therefore faster than the + :class:`pygame.sprite.RenderUpdates` if you have many static sprites. It + also switches automatically between dirty rect update and full screen + drawing, so you do not have to worry what would be faster. + + Same as for the :class:`pygame.sprite.Group`. You can specify some + additional attributes through kwargs: + + :: + + _use_update: True/False default is False + _default_layer: default layer where sprites without a layer are added. + _time_threshold: threshold time for switching between dirty rect mode + and fullscreen mode, defaults to 1000./80 == 1000./fps + + .. versionadded:: 1.8 + + .. method:: draw + + | :sl:`draw all sprites in the right order onto the passed surface.` + | :sg:`draw(surface, bgsurf=None, special_flags=None) -> Rect_list` + + You can pass the background too. If a background is already set, then the + bgsurf argument has no effect. If present, the ``special_flags`` argument is + always passed to ``Surface.blit()``, overriding ``DirtySprite.blendmode``. + If ``special_flags`` is not present, ``DirtySprite.blendmode`` is passed + to the ``Surface.blit()`` instead. + + .. ## LayeredDirty.draw ## + + .. method:: clear + + | :sl:`used to set background` + | :sg:`clear(surface, bgd) -> None` + + .. ## LayeredDirty.clear ## + + .. method:: repaint_rect + + | :sl:`repaints the given area` + | :sg:`repaint_rect(screen_rect) -> None` + + screen_rect is in screen coordinates. + + .. ## LayeredDirty.repaint_rect ## + + .. method:: set_clip + + | :sl:`clip the area where to draw. Just pass None (default) to reset the clip` + | :sg:`set_clip(screen_rect=None) -> None` + + .. ## LayeredDirty.set_clip ## + + .. method:: get_clip + + | :sl:`clip the area where to draw. Just pass None (default) to reset the clip` + | :sg:`get_clip() -> Rect` + + .. ## LayeredDirty.get_clip ## + + .. method:: change_layer + + | :sl:`changes the layer of the sprite` + | :sg:`change_layer(sprite, new_layer) -> None` + + sprite must have been added to the renderer. It is not checked. + + .. ## LayeredDirty.change_layer ## + + .. method:: set_timing_treshold + + | :sl:`sets the threshold in milliseconds` + | :sg:`set_timing_treshold(time_ms) -> None` + + DEPRECATED: Use set_timing_threshold() instead. + + .. deprecated:: 2.1.1 + + .. ## LayeredDirty.set_timing_treshold ## + + .. method:: set_timing_threshold + + | :sl:`sets the threshold in milliseconds` + | :sg:`set_timing_threshold(time_ms) -> None` + + Defaults to 1000.0 / 80.0. This means that the screen will be painted + using the flip method rather than the update method if the update + method is taking so long to update the screen that the frame rate falls + below 80 frames per second. + + .. versionadded:: 2.1.1 + + :raises TypeError: if ``time_ms`` is not int or float + + .. ## LayeredDirty.set_timing_threshold ## + + .. ## pygame.sprite.LayeredDirty ## + +.. function:: GroupSingle + + | :sl:`Group container that holds a single sprite.` + | :sg:`GroupSingle(sprite=None) -> GroupSingle` + + The GroupSingle container only holds a single Sprite. When a new Sprite is + added, the old one is removed. + + There is a special property, ``GroupSingle.sprite``, that accesses the + Sprite that this Group contains. It can be None when the Group is empty. The + property can also be assigned to add a Sprite into the GroupSingle + container. + + .. ## pygame.sprite.GroupSingle ## + +.. function:: spritecollide + + | :sl:`Find sprites in a group that intersect another sprite.` + | :sg:`spritecollide(sprite, group, dokill, collided = None) -> Sprite_list` + + Return a list containing all Sprites in a Group that intersect with another + Sprite. Intersection is determined by comparing the ``Sprite.rect`` + attribute of each Sprite. + + The dokill argument is a bool. If set to True, all Sprites that collide will + be removed from the Group. + + The collided argument is a callback function used to calculate if two + sprites are colliding. it should take two sprites as values, and return a + bool value indicating if they are colliding. If collided is not passed, all + sprites must have a "rect" value, which is a rectangle of the sprite area, + which will be used to calculate the collision. + + collided callables: + + :: + + collide_rect, collide_rect_ratio, collide_circle, + collide_circle_ratio, collide_mask + + Example: + + .. code-block:: python + + # See if the Sprite block has collided with anything in the Group block_list + # The True flag will remove the sprite in block_list + blocks_hit_list = pygame.sprite.spritecollide(player, block_list, True) + + # Check the list of colliding sprites, and add one to the score for each one + for block in blocks_hit_list: + score +=1 + + .. ## pygame.sprite.spritecollide ## + +.. function:: collide_rect + + | :sl:`Collision detection between two sprites, using rects.` + | :sg:`collide_rect(left, right) -> bool` + + Tests for collision between two sprites. Uses the pygame rect colliderect + function to calculate the collision. Intended to be passed as a collided + callback function to the \*collide functions. Sprites must have a "rect" + attributes. + + .. versionadded:: 1.8 + + .. ## pygame.sprite.collide_rect ## + +.. function:: collide_rect_ratio + + | :sl:`Collision detection between two sprites, using rects scaled to a ratio.` + | :sg:`collide_rect_ratio(ratio) -> collided_callable` + + A callable class that checks for collisions between two sprites, using a + scaled version of the sprites rects. + + Is created with a ratio, the instance is then intended to be passed as a + collided callback function to the \*collide functions. + + A ratio is a floating point number - 1.0 is the same size, 2.0 is twice as + big, and 0.5 is half the size. + + .. versionadded:: 1.8.1 + + .. ## pygame.sprite.collide_rect_ratio ## + +.. function:: collide_circle + + | :sl:`Collision detection between two sprites, using circles.` + | :sg:`collide_circle(left, right) -> bool` + + Tests for collision between two sprites, by testing to see if two circles + centered on the sprites overlap. If the sprites have a "radius" attribute, + that is used to create the circle, otherwise a circle is created that is big + enough to completely enclose the sprites rect as given by the "rect" + attribute. Intended to be passed as a collided callback function to the + \*collide functions. Sprites must have a "rect" and an optional "radius" + attribute. + + .. versionadded:: 1.8.1 + + .. ## pygame.sprite.collide_circle ## + +.. function:: collide_circle_ratio + + | :sl:`Collision detection between two sprites, using circles scaled to a ratio.` + | :sg:`collide_circle_ratio(ratio) -> collided_callable` + + A callable class that checks for collisions between two sprites, using a + scaled version of the sprites radius. + + Is created with a floating point ratio, the instance is then intended to be + passed as a collided callback function to the \*collide functions. + + A ratio is a floating point number - 1.0 is the same size, 2.0 is twice as + big, and 0.5 is half the size. + + The created callable tests for collision between two sprites, by testing to + see if two circles centered on the sprites overlap, after scaling the + circles radius by the stored ratio. If the sprites have a "radius" + attribute, that is used to create the circle, otherwise a circle is created + that is big enough to completely enclose the sprites rect as given by the + "rect" attribute. Intended to be passed as a collided callback function to + the \*collide functions. Sprites must have a "rect" and an optional "radius" + attribute. + + .. versionadded:: 1.8.1 + + .. ## pygame.sprite.collide_circle_ratio ## + +.. function:: collide_mask + + | :sl:`Collision detection between two sprites, using masks.` + | :sg:`collide_mask(sprite1, sprite2) -> (int, int)` + | :sg:`collide_mask(sprite1, sprite2) -> None` + + Tests for collision between two sprites, by testing if their bitmasks + overlap (uses :func:`pygame.mask.Mask.overlap`). If the sprites have a + ``mask`` attribute, it is used as the mask, otherwise a mask is created from + the sprite's ``image`` (uses :func:`pygame.mask.from_surface`). Sprites must + have a ``rect`` attribute; the ``mask`` attribute is optional. + + The first point of collision between the masks is returned. The collision + point is offset from ``sprite1``'s mask's topleft corner (which is always + (0, 0)). The collision point is a position within the mask and is not + related to the actual screen position of ``sprite1``. + + This function is intended to be passed as a ``collided`` callback function + to the group collide functions (see :meth:`spritecollide`, + :meth:`groupcollide`, :meth:`spritecollideany`). + + .. note:: + To increase performance, create and set a ``mask`` attribute for all + sprites that will use this function to check for collisions. Otherwise, + each time this function is called it will create new masks. + + .. note:: + A new mask needs to be recreated each time a sprite's image is changed + (e.g. if a new image is used or the existing image is rotated). + + :: + + # Example of mask creation for a sprite. + sprite.mask = pygame.mask.from_surface(sprite.image) + + :returns: first point of collision between the masks or ``None`` if no + collision + :rtype: tuple(int, int) or NoneType + + .. versionadded:: 1.8.0 + + .. ## pygame.sprite.collide_mask ## + +.. function:: groupcollide + + | :sl:`Find all sprites that collide between two groups.` + | :sg:`groupcollide(group1, group2, dokill1, dokill2, collided = None) -> Sprite_dict` + + This will find collisions between all the Sprites in two groups. + Collision is determined by comparing the ``Sprite.rect`` attribute of + each Sprite or by using the collided function if it is not None. + + Every Sprite inside group1 is added to the return dictionary. The value for + each item is the list of Sprites in group2 that intersect. + + If either dokill argument is True, the colliding Sprites will be removed + from their respective Group. + + The collided argument is a callback function used to calculate if two sprites are + colliding. It should take two sprites as values and return a bool value + indicating if they are colliding. If collided is not passed, then all + sprites must have a "rect" value, which is a rectangle of the sprite area, + which will be used to calculate the collision. + + .. ## pygame.sprite.groupcollide ## + +.. function:: spritecollideany + + | :sl:`Simple test if a sprite intersects anything in a group.` + | :sg:`spritecollideany(sprite, group, collided = None) -> Sprite` Collision with the returned sprite. + | :sg:`spritecollideany(sprite, group, collided = None) -> None` No collision + + If the sprite collides with any single sprite in the group, a single + sprite from the group is returned. On no collision None is returned. + + If you don't need all the features of the ``pygame.sprite.spritecollide()`` function, this + function will be a bit quicker. + + The collided argument is a callback function used to calculate if two sprites are + colliding. It should take two sprites as values and return a bool value + indicating if they are colliding. If collided is not passed, then all + sprites must have a "rect" value, which is a rectangle of the sprite area, + which will be used to calculate the collision. + + .. ## pygame.sprite.spritecollideany ## + +.. ## ## + +.. ## pygame.sprite ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/surface.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/surface.rst.txt new file mode 100644 index 00000000..82466fcc --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/surface.rst.txt @@ -0,0 +1,949 @@ +.. include:: common.txt + +:mod:`pygame.Surface` +===================== + +.. currentmodule:: pygame + +.. class:: Surface + + | :sl:`pygame object for representing images` + | :sg:`Surface((width, height), flags=0, depth=0, masks=None) -> Surface` + | :sg:`Surface((width, height), flags=0, Surface) -> Surface` + + A pygame Surface is used to represent any image. The Surface has a fixed + resolution and pixel format. Surfaces with 8-bit pixels use a color palette + to map to 24-bit color. + + Call :meth:`pygame.Surface()` to create a new image object. The Surface will + be cleared to all black. The only required arguments are the sizes. With no + additional arguments, the Surface will be created in a format that best + matches the display Surface. + + The pixel format can be controlled by passing the bit depth or an existing + Surface. The flags argument is a bitmask of additional features for the + surface. You can pass any combination of these flags: + + :: + + HWSURFACE (obsolete in pygame 2) creates the image in video memory + SRCALPHA the pixel format will include a per-pixel alpha + + Both flags are only a request, and may not be possible for all displays and + formats. + + Advance users can combine a set of bitmasks with a depth value. The masks + are a set of 4 integers representing which bits in a pixel will represent + each color. Normal Surfaces should not require the masks argument. + + Surfaces can have many extra attributes like alpha planes, colorkeys, source + rectangle clipping. These functions mainly effect how the Surface is blitted + to other Surfaces. The blit routines will attempt to use hardware + acceleration when possible, otherwise they will use highly optimized + software blitting methods. + + There are three types of transparency supported in pygame: colorkeys, + surface alphas, and pixel alphas. Surface alphas can be mixed with + colorkeys, but an image with per pixel alphas cannot use the other modes. + Colorkey transparency makes a single color value transparent. Any pixels + matching the colorkey will not be drawn. The surface alpha value is a single + value that changes the transparency for the entire image. A surface alpha of + 255 is opaque, and a value of 0 is completely transparent. + + Per pixel alphas are different because they store a transparency value for + every pixel. This allows for the most precise transparency effects, but it + also the slowest. Per pixel alphas cannot be mixed with surface alpha and + colorkeys. + + There is support for pixel access for the Surfaces. Pixel access on hardware + surfaces is slow and not recommended. Pixels can be accessed using the + :meth:`get_at()` and :meth:`set_at()` functions. These methods are fine for + simple access, but will be considerably slow when doing of pixel work with + them. If you plan on doing a lot of pixel level work, it is recommended to + use a :class:`pygame.PixelArray`, which gives an array like view of the + surface. For involved mathematical manipulations try the + :mod:`pygame.surfarray` module (It's quite quick, but requires NumPy.) + + Any functions that directly access a surface's pixel data will need that + surface to be lock()'ed. These functions can :meth:`lock()` and + :meth:`unlock()` the surfaces themselves without assistance. But, if a + function will be called many times, there will be a lot of overhead for + multiple locking and unlocking of the surface. It is best to lock the + surface manually before making the function call many times, and then + unlocking when you are finished. All functions that need a locked surface + will say so in their docs. Remember to leave the Surface locked only while + necessary. + + Surface pixels are stored internally as a single number that has all the + colors encoded into it. Use the :meth:`map_rgb()` and + :meth:`unmap_rgb()` to convert between individual red, green, and blue + values into a packed integer for that Surface. + + Surfaces can also reference sections of other Surfaces. These are created + with the :meth:`subsurface()` method. Any change to either Surface will + effect the other. + + Each Surface contains a clipping area. By default the clip area covers the + entire Surface. If it is changed, all drawing operations will only effect + the smaller area. + + .. method:: blit + + | :sl:`draw one image onto another` + | :sg:`blit(source, dest, area=None, special_flags=0) -> Rect` + + Draws a source Surface onto this Surface. The draw can be positioned with + the dest argument. The dest argument can either be a pair of coordinates representing the position of + the upper left corner of the blit or a Rect, where the upper left corner of the rectangle will be used as the + position for the blit. The size of the destination rectangle does not + effect the blit. + + An optional area rectangle can be passed as well. This represents a + smaller portion of the source Surface to draw. + + .. versionadded:: 1.8 + Optional ``special_flags``: ``BLEND_ADD``, ``BLEND_SUB``, + ``BLEND_MULT``, ``BLEND_MIN``, ``BLEND_MAX``. + + .. versionadded:: 1.8.1 + Optional ``special_flags``: ``BLEND_RGBA_ADD``, ``BLEND_RGBA_SUB``, + ``BLEND_RGBA_MULT``, ``BLEND_RGBA_MIN``, ``BLEND_RGBA_MAX`` + ``BLEND_RGB_ADD``, ``BLEND_RGB_SUB``, ``BLEND_RGB_MULT``, + ``BLEND_RGB_MIN``, ``BLEND_RGB_MAX``. + + .. versionadded:: 1.9.2 + Optional ``special_flags``: ``BLEND_PREMULTIPLIED`` + + .. versionadded:: 2.0.0 + Optional ``special_flags``: ``BLEND_ALPHA_SDL2`` - Uses the SDL2 blitter for alpha blending, + this gives different results than the default blitter, which is modelled after SDL1, due to + different approximations used for the alpha blending formula. The SDL2 blitter also supports + RLE on alpha blended surfaces which the pygame one does not. + + The return rectangle is the area of the affected pixels, excluding any + pixels outside the destination Surface, or outside the clipping area. + + Pixel alphas will be ignored when blitting to an 8 bit Surface. + + For a surface with colorkey or blanket alpha, a blit to self may give + slightly different colors than a non self-blit. + + .. ## Surface.blit ## + + .. method:: blits + + | :sl:`draw many images onto another` + | :sg:`blits(blit_sequence=((source, dest), ...), doreturn=1) -> [Rect, ...] or None` + | :sg:`blits(((source, dest, area), ...)) -> [Rect, ...]` + | :sg:`blits(((source, dest, area, special_flags), ...)) -> [Rect, ...]` + + Draws many surfaces onto this Surface. It takes a sequence as input, + with each of the elements corresponding to the ones of :meth:`blit()`. + It needs at minimum a sequence of (source, dest). + + :param blit_sequence: a sequence of surfaces and arguments to blit them, + they correspond to the :meth:`blit()` arguments + :param doreturn: if ``True``, return a list of rects of the areas changed, + otherwise return ``None`` + + :returns: a list of rects of the areas changed if ``doreturn`` is + ``True``, otherwise ``None`` + :rtype: list or None + + New in pygame 1.9.4. + + .. ## Surface.blits ## + + + .. method:: convert + + | :sl:`change the pixel format of an image` + | :sg:`convert(Surface=None) -> Surface` + | :sg:`convert(depth, flags=0) -> Surface` + | :sg:`convert(masks, flags=0) -> Surface` + + Creates a new copy of the Surface with the pixel format changed. The new + pixel format can be determined from another existing Surface. Otherwise + depth, flags, and masks arguments can be used, similar to the + :meth:`pygame.Surface()` call. + + If no arguments are passed the new Surface will have the same pixel + format as the display Surface. This is always the fastest format for + blitting. It is a good idea to convert all Surfaces before they are + blitted many times. + + The converted Surface will have no pixel alphas. They will be stripped if + the original had them. See :meth:`convert_alpha()` for preserving or + creating per-pixel alphas. + + The new copy will have the same class as the copied surface. This lets + as Surface subclass inherit this method without the need to override, + unless subclass specific instance attributes also need copying. + + .. ## Surface.convert ## + + .. method:: convert_alpha + + | :sl:`change the pixel format of an image including per pixel alphas` + | :sg:`convert_alpha(Surface) -> Surface` + | :sg:`convert_alpha() -> Surface` + + Creates a new copy of the surface with the desired pixel format. The new + surface will be in a format suited for quick blitting to the given format + with per pixel alpha. If no surface is given, the new surface will be + optimized for blitting to the current display. + + Unlike the :meth:`convert()` method, the pixel format for the new + image will not be exactly the same as the requested source, but it will + be optimized for fast alpha blitting to the destination. + + As with :meth:`convert()` the returned surface has the same class as + the converted surface. + + .. ## Surface.convert_alpha ## + + .. method:: copy + + | :sl:`create a new copy of a Surface` + | :sg:`copy() -> Surface` + + Makes a duplicate copy of a Surface. The new surface will have the same + pixel formats, color palettes, transparency settings, and class as the + original. If a Surface subclass also needs to copy any instance specific + attributes then it should override ``copy()``. + + .. ## Surface.copy ## + + .. method:: fill + + | :sl:`fill Surface with a solid color` + | :sg:`fill(color, rect=None, special_flags=0) -> Rect` + + Fill the Surface with a solid color. If no rect argument is given the + entire Surface will be filled. The rect argument will limit the fill to a + specific area. The fill will also be contained by the Surface clip area. + + The color argument can be either a ``RGB`` sequence, a ``RGBA`` sequence + or a mapped color index. If using ``RGBA``, the Alpha (A part of + ``RGBA``) is ignored unless the surface uses per pixel alpha (Surface has + the ``SRCALPHA`` flag). + + .. versionadded:: 1.8 + Optional ``special_flags``: ``BLEND_ADD``, ``BLEND_SUB``, + ``BLEND_MULT``, ``BLEND_MIN``, ``BLEND_MAX``. + + .. versionadded:: 1.8.1 + Optional ``special_flags``: ``BLEND_RGBA_ADD``, ``BLEND_RGBA_SUB``, + ``BLEND_RGBA_MULT``, ``BLEND_RGBA_MIN``, ``BLEND_RGBA_MAX`` + ``BLEND_RGB_ADD``, ``BLEND_RGB_SUB``, ``BLEND_RGB_MULT``, + ``BLEND_RGB_MIN``, ``BLEND_RGB_MAX``. + + This will return the affected Surface area. + + .. ## Surface.fill ## + + .. method:: scroll + + | :sl:`Shift the surface image in place` + | :sg:`scroll(dx=0, dy=0) -> None` + + Move the image by dx pixels right and dy pixels down. dx and dy may be + negative for left and up scrolls respectively. Areas of the surface that + are not overwritten retain their original pixel values. Scrolling is + contained by the Surface clip area. It is safe to have dx and dy values + that exceed the surface size. + + .. versionadded:: 1.9 + + .. ## Surface.scroll ## + + .. method:: set_colorkey + + | :sl:`Set the transparent colorkey` + | :sg:`set_colorkey(Color, flags=0) -> None` + | :sg:`set_colorkey(None) -> None` + + Set the current color key for the Surface. When blitting this Surface + onto a destination, any pixels that have the same color as the colorkey + will be transparent. The color can be an ``RGB`` color or a mapped color + integer. If ``None`` is passed, the colorkey will be unset. + + The colorkey will be ignored if the Surface is formatted to use per pixel + alpha values. The colorkey can be mixed with the full Surface alpha + value. + + The optional flags argument can be set to ``pygame.RLEACCEL`` to provide + better performance on non accelerated displays. An ``RLEACCEL`` Surface + will be slower to modify, but quicker to blit as a source. + + .. ## Surface.set_colorkey ## + + .. method:: get_colorkey + + | :sl:`Get the current transparent colorkey` + | :sg:`get_colorkey() -> RGB or None` + + Return the current colorkey value for the Surface. If the colorkey is not + set then ``None`` is returned. + + .. ## Surface.get_colorkey ## + + .. method:: set_alpha + + | :sl:`set the alpha value for the full Surface image` + | :sg:`set_alpha(value, flags=0) -> None` + | :sg:`set_alpha(None) -> None` + + Set the current alpha value for the Surface. When blitting this Surface + onto a destination, the pixels will be drawn slightly transparent. The + alpha value is an integer from 0 to 255, 0 is fully transparent and 255 + is fully opaque. If ``None`` is passed for the alpha value, then alpha + blending will be disabled, including per-pixel alpha. + + This value is different than the per pixel Surface alpha. For a surface + with per pixel alpha, blanket alpha is ignored and ``None`` is returned. + + .. versionchanged:: 2.0 per-surface alpha can be combined with per-pixel + alpha. + + The optional flags argument can be set to ``pygame.RLEACCEL`` to provide + better performance on non accelerated displays. An ``RLEACCEL`` Surface + will be slower to modify, but quicker to blit as a source. + + .. ## Surface.set_alpha ## + + .. method:: get_alpha + + | :sl:`get the current Surface transparency value` + | :sg:`get_alpha() -> int_value` + + Return the current alpha value for the Surface. + + .. ## Surface.get_alpha ## + + .. method:: lock + + | :sl:`lock the Surface memory for pixel access` + | :sg:`lock() -> None` + + Lock the pixel data of a Surface for access. On accelerated Surfaces, the + pixel data may be stored in volatile video memory or nonlinear compressed + forms. When a Surface is locked the pixel memory becomes available to + access by regular software. Code that reads or writes pixel values will + need the Surface to be locked. + + Surfaces should not remain locked for more than necessary. A locked + Surface can often not be displayed or managed by pygame. + + Not all Surfaces require locking. The :meth:`mustlock()` method can + determine if it is actually required. There is no performance penalty for + locking and unlocking a Surface that does not need it. + + All pygame functions will automatically lock and unlock the Surface data + as needed. If a section of code is going to make calls that will + repeatedly lock and unlock the Surface many times, it can be helpful to + wrap the block inside a lock and unlock pair. + + It is safe to nest locking and unlocking calls. The surface will only be + unlocked after the final lock is released. + + .. ## Surface.lock ## + + .. method:: unlock + + | :sl:`unlock the Surface memory from pixel access` + | :sg:`unlock() -> None` + + Unlock the Surface pixel data after it has been locked. The unlocked + Surface can once again be drawn and managed by pygame. See the + :meth:`lock()` documentation for more details. + + All pygame functions will automatically lock and unlock the Surface data + as needed. If a section of code is going to make calls that will + repeatedly lock and unlock the Surface many times, it can be helpful to + wrap the block inside a lock and unlock pair. + + It is safe to nest locking and unlocking calls. The surface will only be + unlocked after the final lock is released. + + .. ## Surface.unlock ## + + .. method:: mustlock + + | :sl:`test if the Surface requires locking` + | :sg:`mustlock() -> bool` + + Returns ``True`` if the Surface is required to be locked to access pixel + data. Usually pure software Surfaces do not require locking. This method + is rarely needed, since it is safe and quickest to just lock all Surfaces + as needed. + + All pygame functions will automatically lock and unlock the Surface data + as needed. If a section of code is going to make calls that will + repeatedly lock and unlock the Surface many times, it can be helpful to + wrap the block inside a lock and unlock pair. + + .. ## Surface.mustlock ## + + .. method:: get_locked + + | :sl:`test if the Surface is current locked` + | :sg:`get_locked() -> bool` + + Returns ``True`` when the Surface is locked. It doesn't matter how many + times the Surface is locked. + + .. ## Surface.get_locked ## + + .. method:: get_locks + + | :sl:`Gets the locks for the Surface` + | :sg:`get_locks() -> tuple` + + Returns the currently existing locks for the Surface. + + .. ## Surface.get_locks ## + + .. method:: get_at + + | :sl:`get the color value at a single pixel` + | :sg:`get_at((x, y)) -> Color` + + Return a copy of the ``RGBA`` Color value at the given pixel. If the + Surface has no per pixel alpha, then the alpha value will always be 255 + (opaque). If the pixel position is outside the area of the Surface an + ``IndexError`` exception will be raised. + + Getting and setting pixels one at a time is generally too slow to be used + in a game or realtime situation. It is better to use methods which + operate on many pixels at a time like with the blit, fill and draw + methods - or by using :mod:`pygame.surfarray`/:mod:`pygame.PixelArray`. + + This function will temporarily lock and unlock the Surface as needed. + + .. versionadded:: 1.9 + Returning a Color instead of tuple. Use ``tuple(surf.get_at((x,y)))`` + if you want a tuple, and not a Color. This should only matter if + you want to use the color as a key in a dict. + + .. ## Surface.get_at ## + + .. method:: set_at + + | :sl:`set the color value for a single pixel` + | :sg:`set_at((x, y), Color) -> None` + + Set the ``RGBA`` or mapped integer color value for a single pixel. If the + Surface does not have per pixel alphas, the alpha value is ignored. + Setting pixels outside the Surface area or outside the Surface clipping + will have no effect. + + Getting and setting pixels one at a time is generally too slow to be used + in a game or realtime situation. + + This function will temporarily lock and unlock the Surface as needed. + + .. note:: If the surface is palettized, the pixel color will be set to the + most similar color in the palette. + + .. ## Surface.set_at ## + + .. method:: get_at_mapped + + | :sl:`get the mapped color value at a single pixel` + | :sg:`get_at_mapped((x, y)) -> Color` + + Return the integer value of the given pixel. If the pixel position is + outside the area of the Surface an ``IndexError`` exception will be + raised. + + This method is intended for pygame unit testing. It unlikely has any use + in an application. + + This function will temporarily lock and unlock the Surface as needed. + + .. versionadded:: 1.9.2 + + .. ## Surface.get_at_mapped ## + + .. method:: get_palette + + | :sl:`get the color index palette for an 8-bit Surface` + | :sg:`get_palette() -> [RGB, RGB, RGB, ...]` + + Return a list of up to 256 color elements that represent the indexed + colors used in an 8-bit Surface. The returned list is a copy of the + palette, and changes will have no effect on the Surface. + + Returning a list of ``Color(with length 3)`` instances instead of tuples. + + .. versionadded:: 1.9 + + .. ## Surface.get_palette ## + + .. method:: get_palette_at + + | :sl:`get the color for a single entry in a palette` + | :sg:`get_palette_at(index) -> RGB` + + Returns the red, green, and blue color values for a single index in a + Surface palette. The index should be a value from 0 to 255. + + .. versionadded:: 1.9 + Returning ``Color(with length 3)`` instance instead of a tuple. + + .. ## Surface.get_palette_at ## + + .. method:: set_palette + + | :sl:`set the color palette for an 8-bit Surface` + | :sg:`set_palette([RGB, RGB, RGB, ...]) -> None` + + Set the full palette for an 8-bit Surface. This will replace the colors in + the existing palette. A partial palette can be passed and only the first + colors in the original palette will be changed. + + This function has no effect on a Surface with more than 8-bits per pixel. + + .. ## Surface.set_palette ## + + .. method:: set_palette_at + + | :sl:`set the color for a single index in an 8-bit Surface palette` + | :sg:`set_palette_at(index, RGB) -> None` + + Set the palette value for a single entry in a Surface palette. The index + should be a value from 0 to 255. + + This function has no effect on a Surface with more than 8-bits per pixel. + + .. ## Surface.set_palette_at ## + + .. method:: map_rgb + + | :sl:`convert a color into a mapped color value` + | :sg:`map_rgb(Color) -> mapped_int` + + Convert an ``RGBA`` color into the mapped integer value for this Surface. + The returned integer will contain no more bits than the bit depth of the + Surface. Mapped color values are not often used inside pygame, but can be + passed to most functions that require a Surface and a color. + + See the Surface object documentation for more information about colors + and pixel formats. + + .. ## Surface.map_rgb ## + + .. method:: unmap_rgb + + | :sl:`convert a mapped integer color value into a Color` + | :sg:`unmap_rgb(mapped_int) -> Color` + + Convert an mapped integer color into the ``RGB`` color components for + this Surface. Mapped color values are not often used inside pygame, but + can be passed to most functions that require a Surface and a color. + + See the Surface object documentation for more information about colors + and pixel formats. + + .. ## Surface.unmap_rgb ## + + .. method:: set_clip + + | :sl:`set the current clipping area of the Surface` + | :sg:`set_clip(rect) -> None` + | :sg:`set_clip(None) -> None` + + Each Surface has an active clipping area. This is a rectangle that + represents the only pixels on the Surface that can be modified. If + ``None`` is passed for the rectangle the full Surface will be available + for changes. + + The clipping area is always restricted to the area of the Surface itself. + If the clip rectangle is too large it will be shrunk to fit inside the + Surface. + + .. ## Surface.set_clip ## + + .. method:: get_clip + + | :sl:`get the current clipping area of the Surface` + | :sg:`get_clip() -> Rect` + + Return a rectangle of the current clipping area. The Surface will always + return a valid rectangle that will never be outside the bounds of the + image. If the Surface has had ``None`` set for the clipping area, the + Surface will return a rectangle with the full area of the Surface. + + .. ## Surface.get_clip ## + + .. method:: subsurface + + | :sl:`create a new surface that references its parent` + | :sg:`subsurface(Rect) -> Surface` + + Returns a new Surface that shares its pixels with its new parent. The new + Surface is considered a child of the original. Modifications to either + Surface pixels will effect each other. Surface information like clipping + area and color keys are unique to each Surface. + + The new Surface will inherit the palette, color key, and alpha settings + from its parent. + + It is possible to have any number of subsurfaces and subsubsurfaces on + the parent. It is also possible to subsurface the display Surface if the + display mode is not hardware accelerated. + + See :meth:`get_offset()` and :meth:`get_parent()` to learn more + about the state of a subsurface. + + A subsurface will have the same class as the parent surface. + + .. ## Surface.subsurface ## + + .. method:: get_parent + + | :sl:`find the parent of a subsurface` + | :sg:`get_parent() -> Surface` + + Returns the parent Surface of a subsurface. If this is not a subsurface + then ``None`` will be returned. + + .. ## Surface.get_parent ## + + .. method:: get_abs_parent + + | :sl:`find the top level parent of a subsurface` + | :sg:`get_abs_parent() -> Surface` + + Returns the parent Surface of a subsurface. If this is not a subsurface + then this surface will be returned. + + .. ## Surface.get_abs_parent ## + + .. method:: get_offset + + | :sl:`find the position of a child subsurface inside a parent` + | :sg:`get_offset() -> (x, y)` + + Get the offset position of a child subsurface inside of a parent. If the + Surface is not a subsurface this will return (0, 0). + + .. ## Surface.get_offset ## + + .. method:: get_abs_offset + + | :sl:`find the absolute position of a child subsurface inside its top level parent` + | :sg:`get_abs_offset() -> (x, y)` + + Get the offset position of a child subsurface inside of its top level + parent Surface. If the Surface is not a subsurface this will return (0, + 0). + + .. ## Surface.get_abs_offset ## + + .. method:: get_size + + | :sl:`get the dimensions of the Surface` + | :sg:`get_size() -> (width, height)` + + Return the width and height of the Surface in pixels. + + .. ## Surface.get_size ## + + .. method:: get_width + + | :sl:`get the width of the Surface` + | :sg:`get_width() -> width` + + Return the width of the Surface in pixels. + + .. ## Surface.get_width ## + + .. method:: get_height + + | :sl:`get the height of the Surface` + | :sg:`get_height() -> height` + + Return the height of the Surface in pixels. + + .. ## Surface.get_height ## + + .. method:: get_rect + + | :sl:`get the rectangular area of the Surface` + | :sg:`get_rect(\**kwargs) -> Rect` + + Returns a new rectangle covering the entire surface. This rectangle will + always start at (0, 0) with a width and height the same size as the image. + + You can pass keyword argument values to this function. These named values + will be applied to the attributes of the Rect before it is returned. An + example would be ``mysurf.get_rect(center=(100, 100))`` to create a + rectangle for the Surface centered at a given position. + + .. ## Surface.get_rect ## + + .. method:: get_bitsize + + | :sl:`get the bit depth of the Surface pixel format` + | :sg:`get_bitsize() -> int` + + Returns the number of bits used to represent each pixel. This value may + not exactly fill the number of bytes used per pixel. For example a 15 bit + Surface still requires a full 2 bytes. + + .. ## Surface.get_bitsize ## + + .. method:: get_bytesize + + | :sl:`get the bytes used per Surface pixel` + | :sg:`get_bytesize() -> int` + + Return the number of bytes used per pixel. + + .. ## Surface.get_bytesize ## + + .. method:: get_flags + + | :sl:`get the additional flags used for the Surface` + | :sg:`get_flags() -> int` + + Returns a set of current Surface features. Each feature is a bit in the + flags bitmask. Typical flags are ``RLEACCEL``, ``SRCALPHA``, and + ``SRCCOLORKEY``. + + Here is a more complete list of flags. A full list can be found in + ``SDL_video.h`` + + :: + + SWSURFACE 0x00000000 # Surface is in system memory + HWSURFACE 0x00000001 # (obsolete in pygame 2) Surface is in video memory + ASYNCBLIT 0x00000004 # (obsolete in pygame 2) Use asynchronous blits if possible + + See :func:`pygame.display.set_mode()` for flags exclusive to the + display surface. + + Used internally (read-only) + + :: + + HWACCEL 0x00000100 # Blit uses hardware acceleration + SRCCOLORKEY 0x00001000 # Blit uses a source color key + RLEACCELOK 0x00002000 # Private flag + RLEACCEL 0x00004000 # Surface is RLE encoded + SRCALPHA 0x00010000 # Blit uses source alpha blending + PREALLOC 0x01000000 # Surface uses preallocated memory + + .. ## Surface.get_flags ## + + .. method:: get_pitch + + | :sl:`get the number of bytes used per Surface row` + | :sg:`get_pitch() -> int` + + Return the number of bytes separating each row in the Surface. Surfaces + in video memory are not always linearly packed. Subsurfaces will also + have a larger pitch than their real width. + + This value is not needed for normal pygame usage. + + .. ## Surface.get_pitch ## + + .. method:: get_masks + + | :sl:`the bitmasks needed to convert between a color and a mapped integer` + | :sg:`get_masks() -> (R, G, B, A)` + + Returns the bitmasks used to isolate each color in a mapped integer. + + This value is not needed for normal pygame usage. + + .. ## Surface.get_masks ## + + .. method:: set_masks + + | :sl:`set the bitmasks needed to convert between a color and a mapped integer` + | :sg:`set_masks((r,g,b,a)) -> None` + + This is not needed for normal pygame usage. + + .. note:: Starting in pygame 2.0, the masks are read-only and + accordingly this method will raise a TypeError if called. + + .. deprecated:: 2.0.0 + + .. versionadded:: 1.8.1 + + .. ## Surface.set_masks ## + + .. method:: get_shifts + + | :sl:`the bit shifts needed to convert between a color and a mapped integer` + | :sg:`get_shifts() -> (R, G, B, A)` + + Returns the pixel shifts need to convert between each color and a mapped + integer. + + This value is not needed for normal pygame usage. + + .. ## Surface.get_shifts ## + + .. method:: set_shifts + + | :sl:`sets the bit shifts needed to convert between a color and a mapped integer` + | :sg:`set_shifts((r,g,b,a)) -> None` + + This is not needed for normal pygame usage. + + .. note:: Starting in pygame 2.0, the shifts are read-only and + accordingly this method will raise a TypeError if called. + + .. deprecated:: 2.0.0 + + .. versionadded:: 1.8.1 + + .. ## Surface.set_shifts ## + + .. method:: get_losses + + | :sl:`the significant bits used to convert between a color and a mapped integer` + | :sg:`get_losses() -> (R, G, B, A)` + + Return the least significant number of bits stripped from each color in a + mapped integer. + + This value is not needed for normal pygame usage. + + .. ## Surface.get_losses ## + + .. method:: get_bounding_rect + + | :sl:`find the smallest rect containing data` + | :sg:`get_bounding_rect(min_alpha = 1) -> Rect` + + Returns the smallest rectangular region that contains all the pixels in + the surface that have an alpha value greater than or equal to the minimum + alpha value. + + This function will temporarily lock and unlock the Surface as needed. + + .. versionadded:: 1.8 + + .. ## Surface.get_bounding_rect ## + + .. method:: get_view + + | :sl:`return a buffer view of the Surface's pixels.` + | :sg:`get_view(='2') -> BufferProxy` + + Return an object which exports a surface's internal pixel buffer as + a C level array struct, Python level array interface or a C level + buffer interface. The new buffer protocol is supported. + + The kind argument is the length 1 string '0', '1', '2', '3', + 'r', 'g', 'b', or 'a'. The letters are case insensitive; + 'A' will work as well. The argument can be either a Unicode or byte (char) + string. The default is '2'. + + '0' returns a contiguous unstructured bytes view. No surface shape + information is given. A ``ValueError`` is raised if the surface's pixels + are discontinuous. + + '1' returns a (surface-width * surface-height) array of continuous + pixels. A ``ValueError`` is raised if the surface pixels are + discontinuous. + + '2' returns a (surface-width, surface-height) array of raw pixels. + The pixels are surface-bytesize-d unsigned integers. The pixel format is + surface specific. The 3 byte unsigned integers of 24 bit surfaces are + unlikely accepted by anything other than other pygame functions. + + '3' returns a (surface-width, surface-height, 3) array of ``RGB`` color + components. Each of the red, green, and blue components are unsigned + bytes. Only 24-bit and 32-bit surfaces are supported. The color + components must be in either ``RGB`` or ``BGR`` order within the pixel. + + 'r' for red, 'g' for green, 'b' for blue, and 'a' for alpha return a + (surface-width, surface-height) view of a single color component within a + surface: a color plane. Color components are unsigned bytes. Both 24-bit + and 32-bit surfaces support 'r', 'g', and 'b'. Only 32-bit surfaces with + ``SRCALPHA`` support 'a'. + + The surface is locked only when an exposed interface is accessed. + For new buffer interface accesses, the surface is unlocked once the + last buffer view is released. For array interface and old buffer + interface accesses, the surface remains locked until the BufferProxy + object is released. + + .. versionadded:: 1.9.2 + + .. method:: get_buffer + + | :sl:`acquires a buffer object for the pixels of the Surface.` + | :sg:`get_buffer() -> BufferProxy` + + Return a buffer object for the pixels of the Surface. The buffer can be + used for direct pixel access and manipulation. Surface pixel data is + represented as an unstructured block of memory, with a start address + and length in bytes. The data need not be contiguous. Any gaps are + included in the length, but otherwise ignored. + + This method implicitly locks the Surface. The lock will be released when + the returned :mod:`pygame.BufferProxy` object is garbage collected. + + .. versionadded:: 1.8 + + .. ## Surface.get_buffer ## + + .. attribute:: _pixels_address + + | :sl:`pixel buffer address` + | :sg:`_pixels_address -> int` + + The starting address of the surface's raw pixel bytes. + + .. versionadded:: 1.9.2 + + .. method:: premul_alpha + + | :sl:`returns a copy of the surface with the RGB channels pre-multiplied by the alpha channel.` + | :sg:`premul_alpha() -> Surface` + + **Experimental:** feature still in development available for testing and feedback. It may change. + `Please leave premul_alpha feedback with authors `_ + + Returns a copy of the initial surface with the red, green and blue color channels multiplied + by the alpha channel. This is intended to make it easier to work with the BLEND_PREMULTIPLED + blend mode flag of the blit() method. Surfaces which have called this method will only look + correct after blitting if the BLEND_PREMULTIPLED special flag is used. + + It is worth noting that after calling this method, methods that return the colour of a pixel + such as get_at() will return the alpha multiplied colour values. It is not possible to fully + reverse an alpha multiplication of the colours in a surface as integer colour channel data + is generally reduced by the operation (e.g. 255 x 0 = 0, from there it is not possible to reconstruct + the original 255 from just the two remaining zeros in the colour and alpha channels). + + If you call this method, and then call it again, it will multiply the colour channels by the alpha channel + twice. There are many possible ways to obtain a surface with the colour channels pre-multiplied by the + alpha channel in pygame, and it is not possible to tell the difference just from the information in the pixels. + It is completely possible to have two identical surfaces - one intended for pre-multiplied alpha blending and + one intended for normal blending. For this reason we do not store state on surfaces intended for pre-multiplied + alpha blending. + + Surfaces without an alpha channel cannot use this method and will return an error if you use + it on them. It is best used on 32 bit surfaces (the default on most platforms) as the blitting + on these surfaces can be accelerated by SIMD versions of the pre-multiplied blitter. + + In general pre-multiplied alpha blitting is faster then 'straight alpha' blitting and produces + superior results when blitting an alpha surface onto another surface with alpha - assuming both + surfaces contain pre-multiplied alpha colours. + + .. versionadded:: 2.2.0 + + .. ## Surface.premul_alpha ## + + .. ## pygame.Surface ## + + diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/surfarray.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/surfarray.rst.txt new file mode 100644 index 00000000..c29723af --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/surfarray.rst.txt @@ -0,0 +1,337 @@ +.. include:: common.txt + +:mod:`pygame.surfarray` +======================= + +.. module:: pygame.surfarray + :synopsis: pygame module for accessing surface pixel data using array interfaces + +| :sl:`pygame module for accessing surface pixel data using array interfaces` + +Functions to convert between NumPy arrays and Surface objects. This module +will only be functional when pygame can use the external NumPy package. +If NumPy can't be imported, ``surfarray`` becomes a ``MissingModule`` object. + +Every pixel is stored as a single integer value to represent the red, green, +and blue colors. The 8-bit images use a value that looks into a colormap. Pixels +with higher depth use a bit packing process to place three or four values into +a single number. + +The arrays are indexed by the ``X`` axis first, followed by the ``Y`` axis. +Arrays that treat the pixels as a single integer are referred to as 2D arrays. +This module can also separate the red, green, and blue color values into +separate indices. These types of arrays are referred to as 3D arrays, and the +last index is 0 for red, 1 for green, and 2 for blue. + +The pixels of a 2D array as returned by :func:`array2d` and :func:`pixels2d` +are mapped to the specific surface. Use :meth:`pygame.Surface.unmap_rgb` +to convert to a color, and :meth:`pygame.Surface.map_rgb` to get the surface +specific pixel value of a color. Integer pixel values can only be used directly +between surfaces with matching pixel layouts (see :class:`pygame.Surface`). + +All functions that refer to "array" will copy the surface information to a new +numpy array. All functions that refer to "pixels" will directly reference the +pixels from the surface and any changes performed to the array will make changes +in the surface. As this last functions share memory with the surface, this one +will be locked during the lifetime of the array. + +.. function:: array2d + + | :sl:`Copy pixels into a 2d array` + | :sg:`array2d(Surface) -> array` + + Copy the :meth:`mapped ` (raw) pixels from a Surface + into a 2D array. + The bit depth of the surface will control the size of the integer values, + and will work for any type of pixel format. + + This function will temporarily lock the Surface as pixels are copied + (see the :meth:`pygame.Surface.lock` - lock the Surface memory for pixel + access method). + + .. ## pygame.surfarray.array2d ## + +.. function:: pixels2d + + | :sl:`Reference pixels into a 2d array` + | :sg:`pixels2d(Surface) -> array` + + Create a new 2D array that directly references the pixel values in a + Surface. Any changes to the array will affect the pixels in the Surface. + This is a fast operation since no data is copied. + + Pixels from a 24-bit Surface cannot be referenced, but all other Surface bit + depths can. + + The Surface this references will remain locked for the lifetime of the array, + since the array generated by this function shares memory with the surface. + See the :meth:`pygame.Surface.lock` - lock the Surface memory for pixel + access method. + + .. ## pygame.surfarray.pixels2d ## + +.. function:: array3d + + | :sl:`Copy pixels into a 3d array` + | :sg:`array3d(Surface) -> array` + + Copy the pixels from a Surface into a 3D array. The bit depth of the surface + will control the size of the integer values, and will work for any type of + pixel format. + + This function will temporarily lock the Surface as pixels are copied (see + the :meth:`pygame.Surface.lock` - lock the Surface memory for pixel + access method). + + .. ## pygame.surfarray.array3d ## + +.. function:: pixels3d + + | :sl:`Reference pixels into a 3d array` + | :sg:`pixels3d(Surface) -> array` + + Create a new 3D array that directly references the pixel values in a + Surface. Any changes to the array will affect the pixels in the Surface. + This is a fast operation since no data is copied. + + This will only work on Surfaces that have 24-bit or 32-bit formats. Lower + pixel formats cannot be referenced. + + The Surface this references will remain locked for the lifetime of the array, + since the array generated by this function shares memory with the surface. + See the :meth:`pygame.Surface.lock` - lock the Surface memory for pixel + access method. + + .. ## pygame.surfarray.pixels3d ## + +.. function:: array_alpha + + | :sl:`Copy pixel alphas into a 2d array` + | :sg:`array_alpha(Surface) -> array` + + Copy the pixel alpha values (degree of transparency) from a Surface into a + 2D array. This will work for any type of Surface format. Surfaces without a + pixel alpha will return an array with all opaque values. + + This function will temporarily lock the Surface as pixels are copied (see + the :meth:`pygame.Surface.lock` - lock the Surface memory for pixel + access method). + + .. ## pygame.surfarray.array_alpha ## + +.. function:: pixels_alpha + + | :sl:`Reference pixel alphas into a 2d array` + | :sg:`pixels_alpha(Surface) -> array` + + Create a new 2D array that directly references the alpha values (degree of + transparency) in a Surface. Any changes to the array will affect the pixels + in the Surface. This is a fast operation since no data is copied. + + This can only work on 32-bit Surfaces with a per-pixel alpha value. + + The Surface this references will remain locked for the lifetime of the array, + since the array generated by this function shares memory with the surface. + See the :meth:`pygame.Surface.lock` - lock the Surface memory for pixel + access method. + + .. ## pygame.surfarray.pixels_alpha ## + +.. function:: array_red + + | :sl:`Copy red pixels into a 2d array` + | :sg:`array_red(Surface) -> array` + + Copy the pixel red values from a Surface into a 2D array. This will work + for any type of Surface format. + + This function will temporarily lock the Surface as pixels are copied (see + the :meth:`pygame.Surface.lock` - lock the Surface memory for pixel + access method). + + .. versionadded:: 2.0.2 + + .. ## pygame.surfarray.array_red ## + +.. function:: pixels_red + + | :sl:`Reference pixel red into a 2d array.` + | :sg:`pixels_red (Surface) -> array` + + Create a new 2D array that directly references the red values in a Surface. + Any changes to the array will affect the pixels in the Surface. This is a + fast operation since no data is copied. + + This can only work on 24-bit or 32-bit Surfaces. + + The Surface this references will remain locked for the lifetime of the array, + since the array generated by this function shares memory with the surface. + See the :meth:`pygame.Surface.lock` - lock the Surface memory for pixel + access method. + + .. ## pygame.surfarray.pixels_red ## + +.. function:: array_green + + | :sl:`Copy green pixels into a 2d array` + | :sg:`array_green(Surface) -> array` + + Copy the pixel green values from a Surface into a 2D array. This will work + for any type of Surface format. + + This function will temporarily lock the Surface as pixels are copied (see + the :meth:`pygame.Surface.lock` - lock the Surface memory for pixel + access method). + + .. versionadded:: 2.0.2 + + .. ## pygame.surfarray.array_green ## + +.. function:: pixels_green + + | :sl:`Reference pixel green into a 2d array.` + | :sg:`pixels_green (Surface) -> array` + + Create a new 2D array that directly references the green values in a + Surface. Any changes to the array will affect the pixels in the Surface. + This is a fast operation since no data is copied. + + This can only work on 24-bit or 32-bit Surfaces. + + The Surface this references will remain locked for the lifetime of the array, + since the array generated by this function shares memory with the surface. + See the :meth:`pygame.Surface.lock` - lock the Surface memory for pixel + access method. + + .. ## pygame.surfarray.pixels_green ## + +.. function:: array_blue + + | :sl:`Copy blue pixels into a 2d array` + | :sg:`array_blue(Surface) -> array` + + Copy the pixel blue values from a Surface into a 2D array. This will work + for any type of Surface format. + + This function will temporarily lock the Surface as pixels are copied (see + the :meth:`pygame.Surface.lock` - lock the Surface memory for pixel + access method). + + .. versionadded:: 2.0.2 + + .. ## pygame.surfarray.array_blue ## + +.. function:: pixels_blue + + | :sl:`Reference pixel blue into a 2d array.` + | :sg:`pixels_blue (Surface) -> array` + + Create a new 2D array that directly references the blue values in a Surface. + Any changes to the array will affect the pixels in the Surface. This is a + fast operation since no data is copied. + + This can only work on 24-bit or 32-bit Surfaces. + + The Surface this references will remain locked for the lifetime of the array, + since the array generated by this function shares memory with the surface. + See the :meth:`pygame.Surface.lock` - lock the Surface memory for pixel + access method. + + .. ## pygame.surfarray.pixels_blue ## + +.. function:: array_colorkey + + | :sl:`Copy the colorkey values into a 2d array` + | :sg:`array_colorkey(Surface) -> array` + + Create a new array with the colorkey transparency value from each pixel. If + the pixel matches the colorkey it will be fully transparent; otherwise it + will be fully opaque. + + This will work on any type of Surface format. If the image has no colorkey a + solid opaque array will be returned. + + This function will temporarily lock the Surface as pixels are copied. + + .. ## pygame.surfarray.array_colorkey ## + +.. function:: make_surface + + | :sl:`Copy an array to a new surface` + | :sg:`make_surface(array) -> Surface` + + Create a new Surface that best resembles the data and format on the array. + The array can be 2D or 3D with any sized integer values. Function + make_surface uses the array struct interface to acquire array properties, + so is not limited to just NumPy arrays. See :mod:`pygame.pixelcopy`. + + New in pygame 1.9.2: array struct interface support. + + .. ## pygame.surfarray.make_surface ## + +.. function:: blit_array + + | :sl:`Blit directly from a array values` + | :sg:`blit_array(Surface, array) -> None` + + Directly copy values from an array into a Surface. This is faster than + converting the array into a Surface and blitting. The array must be the same + dimensions as the Surface and will completely replace all pixel values. Only + integer, ASCII character and record arrays are accepted. + + This function will temporarily lock the Surface as the new values are + copied. + + .. ## pygame.surfarray.blit_array ## + +.. function:: map_array + + | :sl:`Map a 3d array into a 2d array` + | :sg:`map_array(Surface, array3d) -> array2d` + + Convert a 3D array into a 2D array. This will use the given Surface format + to control the conversion. Palette surface formats are supported for NumPy + arrays. + + .. ## pygame.surfarray.map_array ## + +.. function:: use_arraytype + + | :sl:`Sets the array system to be used for surface arrays` + | :sg:`use_arraytype (arraytype) -> None` + + DEPRECATED: Uses the requested array type for the module functions. + The only supported arraytype is ``'numpy'``. Other values will raise + ValueError. Using this function will raise a ``DeprecationWarning``. + + .. ## pygame.surfarray.use_arraytype ## + +.. function:: get_arraytype + + | :sl:`Gets the currently active array type.` + | :sg:`get_arraytype () -> str` + + DEPRECATED: Returns the currently active array type. This will be a value of the + ``get_arraytypes()`` tuple and indicates which type of array module is used + for the array creation. Using this function will raise a ``DeprecationWarning``. + + .. versionadded:: 1.8 + + .. ## pygame.surfarray.get_arraytype ## + +.. function:: get_arraytypes + + | :sl:`Gets the array system types currently supported.` + | :sg:`get_arraytypes () -> tuple` + + DEPRECATED: Checks, which array systems are available and returns them as a tuple of + strings. The values of the tuple can be used directly in the + :func:`pygame.surfarray.use_arraytype` () method. If no supported array + system could be found, None will be returned. Using this function will raise a + ``DeprecationWarning``. + + .. versionadded:: 1.8 + + .. ## pygame.surfarray.get_arraytypes ## + +.. ## pygame.surfarray ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/tests.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/tests.rst.txt new file mode 100644 index 00000000..88184f6c --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/tests.rst.txt @@ -0,0 +1,113 @@ +.. include:: common.txt + +:mod:`pygame.tests` +=================== + +.. module:: pygame.tests + :synopsis: Pygame unit test suite package + +| :sl:`Pygame unit test suite package` + +A quick way to run the test suite package from the command line is to import +the go submodule with the Python -m option: + +:: + + python -m pygame.tests [] + +Command line option --help displays a usage message. Available options +correspond to the :func:`pygame.tests.run` arguments. + +The xxxx_test submodules of the tests package are unit test suites for +individual parts of pygame. Each can also be run as a main program. This is +useful if the test, such as cdrom_test, is interactive. + +For pygame development the test suite can be run from a pygame distribution +root directory. Program ``run_tests.py`` is provided for convenience, though +test/go.py can be run directly. + +Module level tags control which modules are included in a unit test run. Tags +are assigned to a unit test module with a corresponding _tags.py module. +The tags module has the global __tags__, a list of tag names. For example, +``cdrom_test.py`` has a tag file ``cdrom_tags.py`` containing a tags list that +has the 'interactive' string. The 'interactive' tag indicates ``cdrom_test.py`` +expects user input. It is excluded from a ``run_tests.py`` or +``pygame.tests.go`` run. + +Two other tags that are excluded are 'ignore' and 'subprocess_ignore'. These +two tags indicate unit tests that will not run on a particular platform, or +for which no corresponding pygame module is available. + +The test runner will list each excluded module along with the tag responsible. + +.. function:: run + + | :sl:`Run the pygame unit test suite` + | :sg:`run(*args, **kwds) -> tuple` + + Positional arguments (optional): + + :: + + The names of tests to include. If omitted then all tests are run. Test names + need not include the trailing '_test'. + + Keyword arguments: + + :: + + incomplete - fail incomplete tests (default False) + nosubprocess - run all test suites in the current process + (default False, use separate subprocesses) + dump - dump failures/errors as dict ready to eval (default False) + file - if provided, the name of a file into which to dump failures/errors + timings - if provided, the number of times to run each individual test to + get an average run time (default is run each test once) + exclude - A list of TAG names to exclude from the run + show_output - show silenced stderr/stdout on errors (default False) + all - dump all results, not just errors (default False) + randomize - randomize order of tests (default False) + seed - if provided, a seed randomizer integer + multi_thread - if provided, the number of THREADS in which to run + subprocessed tests + time_out - if subprocess is True then the time limit in seconds before + killing a test (default 30) + fake - if provided, the name of the fake tests package in the + run_tests__tests subpackage to run instead of the normal + pygame tests + python - the path to a python executable to run subprocessed tests + (default sys.executable) + + Return value: + + :: + + A tuple of total number of tests run, dictionary of error information. + The dictionary is empty if no errors were recorded. + + By default individual test modules are run in separate subprocesses. This + recreates normal pygame usage where ``pygame.init()`` and ``pygame.quit()`` + are called only once per program execution, and avoids unfortunate + interactions between test modules. + + A time limit is placed on test execution ensuring that any frozen tests + processes are killed when their time allotment is expired. Use the single + process option if threading is not working properly or if tests are taking + too long. It is not guaranteed that all tests will pass in single process + mode. + + Tests are run in a randomized order if the randomize argument is True or a + seed argument is provided. If no seed integer is provided then the system + time is used for the randomization seed value. + + Individual test modules may have a __tags__ attribute, a list of tag strings + used to selectively omit modules from a run. By default only 'interactive' + modules such as cdrom_test are ignored. An interactive module must be run + from the console as a Python program. + + This function can only be called once per Python session. It is not + reentrant. + + .. ## pygame.tests.run ## + +.. ## pygame.tests ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/time.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/time.rst.txt new file mode 100644 index 00000000..59e09997 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/time.rst.txt @@ -0,0 +1,165 @@ +.. include:: common.txt + +:mod:`pygame.time` +================== + +.. module:: pygame.time + :synopsis: pygame module for monitoring time + +| :sl:`pygame module for monitoring time` + +Times in pygame are represented in milliseconds (1/1000 seconds). Most +platforms have a limited time resolution of around 10 milliseconds. This +resolution, in milliseconds, is given in the ``TIMER_RESOLUTION`` constant. + +.. function:: get_ticks + + | :sl:`get the time in milliseconds` + | :sg:`get_ticks() -> milliseconds` + + Return the number of milliseconds since ``pygame.init()`` was called. Before + pygame is initialized this will always be 0. + + .. ## pygame.time.get_ticks ## + +.. function:: wait + + | :sl:`pause the program for an amount of time` + | :sg:`wait(milliseconds) -> time` + + Will pause for a given number of milliseconds. This function sleeps the + process to share the processor with other programs. A program that waits for + even a few milliseconds will consume very little processor time. It is + slightly less accurate than the ``pygame.time.delay()`` function. + + This returns the actual number of milliseconds used. + + .. ## pygame.time.wait ## + +.. function:: delay + + | :sl:`pause the program for an amount of time` + | :sg:`delay(milliseconds) -> time` + + Will pause for a given number of milliseconds. This function will use the + processor (rather than sleeping) in order to make the delay more accurate + than ``pygame.time.wait()``. + + This returns the actual number of milliseconds used. + + .. ## pygame.time.delay ## + +.. function:: set_timer + + | :sl:`repeatedly create an event on the event queue` + | :sg:`set_timer(event, millis) -> None` + | :sg:`set_timer(event, millis, loops=0) -> None` + + Set an event to appear on the event queue every given number of milliseconds. + The first event will not appear until the amount of time has passed. + + The ``event`` attribute can be a ``pygame.event.Event`` object or an integer + type that denotes an event. + + ``loops`` is an integer that denotes the number of events posted. If 0 (default) + then the events will keep getting posted, unless explicitly stopped. + + To disable the timer for such an event, call the function again with the same + event argument with ``millis`` argument set to 0. + + It is also worth mentioning that a particular event type can only be put on a + timer once. In other words, there cannot be two timers for the same event type. + Setting an event timer for a particular event discards the old one for that + event type. + + ``loops`` replaces the ``once`` argument, and this does not break backward + compatibility + + .. versionadded:: 2.0.0.dev3 once argument added. + .. versionchanged:: 2.0.1 event argument supports ``pygame.event.Event`` object + .. versionadded:: 2.0.1 added loops argument to replace once argument + + .. ## pygame.time.set_timer ## + +.. class:: Clock + + | :sl:`create an object to help track time` + | :sg:`Clock() -> Clock` + + Creates a new Clock object that can be used to track an amount of time. The + clock also provides several functions to help control a game's framerate. + + .. method:: tick + + | :sl:`update the clock` + | :sg:`tick(framerate=0) -> milliseconds` + + This method should be called once per frame. It will compute how many + milliseconds have passed since the previous call. + + If you pass the optional framerate argument the function will delay to + keep the game running slower than the given ticks per second. This can be + used to help limit the runtime speed of a game. By calling + ``Clock.tick(40)`` once per frame, the program will never run at more + than 40 frames per second. + + Note that this function uses SDL_Delay function which is not accurate on + every platform, but does not use much CPU. Use tick_busy_loop if you want + an accurate timer, and don't mind chewing CPU. + + .. ## Clock.tick ## + + .. method:: tick_busy_loop + + | :sl:`update the clock` + | :sg:`tick_busy_loop(framerate=0) -> milliseconds` + + This method should be called once per frame. It will compute how many + milliseconds have passed since the previous call. + + If you pass the optional framerate argument the function will delay to + keep the game running slower than the given ticks per second. This can be + used to help limit the runtime speed of a game. By calling + ``Clock.tick_busy_loop(40)`` once per frame, the program will never run at + more than 40 frames per second. + + Note that this function uses :func:`pygame.time.delay`, which uses lots + of CPU in a busy loop to make sure that timing is more accurate. + + .. versionadded:: 1.8 + + .. ## Clock.tick_busy_loop ## + + .. method:: get_time + + | :sl:`time used in the previous tick` + | :sg:`get_time() -> milliseconds` + + The number of milliseconds that passed between the previous two calls to + ``Clock.tick()``. + + .. ## Clock.get_time ## + + .. method:: get_rawtime + + | :sl:`actual time used in the previous tick` + | :sg:`get_rawtime() -> milliseconds` + + Similar to ``Clock.get_time()``, but does not include any time used + while ``Clock.tick()`` was delaying to limit the framerate. + + .. ## Clock.get_rawtime ## + + .. method:: get_fps + + | :sl:`compute the clock framerate` + | :sg:`get_fps() -> float` + + Compute your game's framerate (in frames per second). It is computed by + averaging the last ten calls to ``Clock.tick()``. + + .. ## Clock.get_fps ## + + .. ## pygame.time.Clock ## + +.. ## pygame.time ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/touch.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/touch.rst.txt new file mode 100644 index 00000000..320da056 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/touch.rst.txt @@ -0,0 +1,66 @@ +.. include:: common.txt + +:mod:`pygame._sdl2.touch` +========================= + +.. module:: pygame._sdl2.touch + :synopsis: pygame module to work with touch input + +| :sl:`pygame module to work with touch input` + +.. versionadded:: 2 This module requires SDL2. + +.. function:: get_num_devices + + | :sl:`get the number of touch devices` + | :sg:`get_num_devices() -> int` + + Return the number of available touch devices. + + .. ## pygame._sdl2.touch.get_num_devices ## + +.. function:: get_device + + | :sl:`get the a touch device id for a given index` + | :sg:`get_device(index) -> touchid` + + :param int index: This number is at least 0 and less than the + :func:`number of devices `. + + Return an integer id associated with the given ``index``. + + .. ## pygame._sdl2.touch.get_device ## + +.. function:: get_num_fingers + + | :sl:`the number of active fingers for a given touch device` + | :sg:`get_num_fingers(touchid) -> int` + + Return the number of fingers active for the touch device + whose id is `touchid`. + + .. ## pygame._sdl2.touch.get_num_fingers ## + +.. function:: get_finger + + | :sl:`get information about an active finger` + | :sg:`get_finger(touchid, index) -> int` + + :param int touchid: The touch device id. + :param int index: The index of the finger to return + information about, between 0 and the + :func:`number of active fingers `. + + Return a dict for the finger ``index`` active on ``touchid``. + The dict contains these keys: + + :: + + id the id of the finger (an integer). + x the normalized x position of the finger, between 0 and 1. + y the normalized y position of the finger, between 0 and 1. + pressure the amount of pressure applied by the finger, between 0 and 1. + + .. ## pygame._sdl2.touch.get_finger ## + +.. ## pygame._sdl2.touch ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/transform.rst.txt b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/transform.rst.txt new file mode 100644 index 00000000..91a3a8f6 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_sources/ref/transform.rst.txt @@ -0,0 +1,325 @@ +.. include:: common.txt + +:mod:`pygame.transform` +======================= + +.. module:: pygame.transform + :synopsis: pygame module to transform surfaces + +| :sl:`pygame module to transform surfaces` + +A Surface transform is an operation that moves or resizes the pixels. All these +functions take a Surface to operate on and return a new Surface with the +results. + +Some of the transforms are considered destructive. These means every time they +are performed they lose pixel data. Common examples of this are resizing and +rotating. For this reason, it is better to re-transform the original surface +than to keep transforming an image multiple times. (For example, suppose you +are animating a bouncing spring which expands and contracts. If you applied the +size changes incrementally to the previous images, you would lose detail. +Instead, always begin with the original image and scale to the desired size.) + +.. versionchanged:: 2.0.2 transform functions now support keyword arguments. + +.. function:: flip + + | :sl:`flip vertically and horizontally` + | :sg:`flip(surface, flip_x, flip_y) -> Surface` + + This can flip a Surface either vertically, horizontally, or both. + The arguments ``flip_x`` and ``flip_y`` are booleans that control whether + to flip each axis. Flipping a Surface is non-destructive and returns a new + Surface with the same dimensions. + + .. ## pygame.transform.flip ## + +.. function:: scale + + | :sl:`resize to new resolution` + | :sg:`scale(surface, size, dest_surface=None) -> Surface` + + Resizes the Surface to a new size, given as (width, height). + This is a fast scale operation that does not sample the results. + + An optional destination surface can be used, rather than have it create a + new one. This is quicker if you want to repeatedly scale something. However + the destination must be the same size as the size (width, height) passed in. Also + the destination surface must be the same format. + + .. ## pygame.transform.scale ## + +.. function:: scale_by + + | :sl:`resize to new resolution, using scalar(s)` + | :sg:`scale_by(surface, factor, dest_surface=None) -> Surface` + + **Experimental:** feature still in development available for testing and feedback. It may change. + `Please leave scale_by feedback with authors `_ + + Same as :func:`scale()`, but scales by some factor, rather than taking + the new size explicitly. For example, :code:`transform.scale_by(surf, 3)` + will triple the size of the surface in both dimensions. Optionally, the + scale factor can be a sequence of two numbers, controlling x and y scaling + separately. For example, :code:`transform.scale_by(surf, (2, 1))` doubles + the image width but keeps the height the same. + + .. versionadded:: 2.1.3 + + .. ## pygame.transform.scale_by ## + +.. function:: rotate + + | :sl:`rotate an image` + | :sg:`rotate(surface, angle) -> Surface` + + Unfiltered counterclockwise rotation. The angle argument represents degrees + and can be any floating point value. Negative angle amounts will rotate + clockwise. + + Unless rotating by 90 degree increments, the image will be padded larger to + hold the new size. If the image has pixel alphas, the padded area will be + transparent. Otherwise pygame will pick a color that matches the Surface + colorkey or the topleft pixel value. + + .. ## pygame.transform.rotate ## + +.. function:: rotozoom + + | :sl:`filtered scale and rotation` + | :sg:`rotozoom(surface, angle, scale) -> Surface` + + This is a combined scale and rotation transform. The resulting Surface will + be a filtered 32-bit Surface. The scale argument is a floating point value + that will be multiplied by the current resolution. The angle argument is a + floating point value that represents the counterclockwise degrees to rotate. + A negative rotation angle will rotate clockwise. + + .. ## pygame.transform.rotozoom ## + +.. function:: scale2x + + | :sl:`specialized image doubler` + | :sg:`scale2x(surface, dest_surface=None) -> Surface` + + This will return a new image that is double the size of the original. It + uses the AdvanceMAME Scale2X algorithm which does a 'jaggie-less' scale of + bitmap graphics. + + This really only has an effect on simple images with solid colors. On + photographic and antialiased images it will look like a regular unfiltered + scale. + + An optional destination surface can be used, rather than have it create a + new one. This is quicker if you want to repeatedly scale something. However + the destination must be twice the size of the source surface passed in. Also + the destination surface must be the same format. + + .. ## pygame.transform.scale2x ## + +.. function:: smoothscale + + | :sl:`scale a surface to an arbitrary size smoothly` + | :sg:`smoothscale(surface, size, dest_surface=None) -> Surface` + + Uses one of two different algorithms for scaling each dimension of the input + surface as required. For shrinkage, the output pixels are area averages of + the colors they cover. For expansion, a bilinear filter is used. For the + x86-64 and i686 architectures, optimized ``MMX`` routines are included and + will run much faster than other machine types. The size is a 2 number + sequence for (width, height). This function only works for 24-bit or 32-bit + surfaces. An exception will be thrown if the input surface bit depth is less + than 24. + + .. versionadded:: 1.8 + + .. ## pygame.transform.smoothscale ## + +.. function:: smoothscale_by + + | :sl:`resize to new resolution, using scalar(s)` + | :sg:`smoothscale_by(surface, factor, dest_surface=None) -> Surface` + + **Experimental:** feature still in development available for testing and feedback. It may change. + `Please leave smoothscale_by feedback with authors `_ + + Same as :func:`smoothscale()`, but scales by some factor, rather than + taking the new size explicitly. For example, + :code:`transform.smoothscale_by(surf, 3)` will triple the size of the + surface in both dimensions. Optionally, the scale factor can be a sequence + of two numbers, controlling x and y scaling separately. For example, + :code:`transform.smoothscale_by(surf, (2, 1))` doubles the image width but + keeps the height the same. + + .. versionadded:: 2.1.3 + + .. ## pygame.transform.smoothscale_by ## + +.. function:: get_smoothscale_backend + + | :sl:`return smoothscale filter version in use: 'GENERIC', 'MMX', or 'SSE'` + | :sg:`get_smoothscale_backend() -> string` + + Shows whether or not smoothscale is using ``MMX`` or ``SSE`` acceleration. + If no acceleration is available then "GENERIC" is returned. For a x86 + processor the level of acceleration to use is determined at runtime. + + This function is provided for pygame testing and debugging. + + .. ## pygame.transform.get_smoothscale_backend ## + +.. function:: set_smoothscale_backend + + | :sl:`set smoothscale filter version to one of: 'GENERIC', 'MMX', or 'SSE'` + | :sg:`set_smoothscale_backend(backend) -> None` + + Sets smoothscale acceleration. Takes a string argument. A value of 'GENERIC' + turns off acceleration. 'MMX' uses ``MMX`` instructions only. 'SSE' allows + ``SSE`` extensions as well. A value error is raised if type is not + recognized or not supported by the current processor. + + This function is provided for pygame testing and debugging. If smoothscale + causes an invalid instruction error then it is a pygame/SDL bug that should + be reported. Use this function as a temporary fix only. + + .. ## pygame.transform.set_smoothscale_backend ## + +.. function:: chop + + | :sl:`gets a copy of an image with an interior area removed` + | :sg:`chop(surface, rect) -> Surface` + + Extracts a portion of an image. All vertical and horizontal pixels + surrounding the given rectangle area are removed. The corner areas (diagonal + to the rect) are then brought together. (The original image is not altered + by this operation.) + + ``NOTE``: If you want a "crop" that returns the part of an image within a + rect, you can blit with a rect to a new surface or copy a subsurface. + + .. ## pygame.transform.chop ## + +.. function:: laplacian + + | :sl:`find edges in a surface` + | :sg:`laplacian(surface, dest_surface=None) -> Surface` + + Finds the edges in a surface using the laplacian algorithm. + + .. versionadded:: 1.8 + + .. ## pygame.transform.laplacian ## + +.. function:: average_surfaces + + | :sl:`find the average surface from many surfaces.` + | :sg:`average_surfaces(surfaces, dest_surface=None, palette_colors=1) -> Surface` + + Takes a sequence of surfaces and returns a surface with average colors from + each of the surfaces. + + palette_colors - if true we average the colors in palette, otherwise we + average the pixel values. This is useful if the surface is actually + greyscale colors, and not palette colors. + + Note, this function currently does not handle palette using surfaces + correctly. + + .. versionadded:: 1.8 + .. versionadded:: 1.9 ``palette_colors`` argument + + .. ## pygame.transform.average_surfaces ## + +.. function:: average_color + + | :sl:`finds the average color of a surface` + | :sg:`average_color(surface, rect=None, consider_alpha=False) -> Color` + + Finds the average color of a Surface or a region of a surface specified by a + Rect, and returns it as a Color. If consider_alpha is set to True, then alpha is + taken into account (removing the black artifacts). + + .. versionadded:: 2.1.2 ``consider_alpha`` argument + + .. ## pygame.transform.average_color ## + +.. function:: grayscale + + | :sl:`grayscale a surface` + | :sg:`grayscale(surface, dest_surface=None) -> Surface` + + Returns a grayscaled version of the original surface using the luminosity formula which weights red, green and blue according to their wavelengths. + + An optional destination surface can be passed which is faster than creating a new Surface. + This destination surface must have the same dimensions (width, height) and depth as the source Surface. + + .. ## pygame.transform.grayscale ## + +.. function:: threshold + + | :sl:`finds which, and how many pixels in a surface are within a threshold of a 'search_color' or a 'search_surf'.` + | :sg:`threshold(dest_surface, surface, search_color, threshold=(0,0,0,0), set_color=(0,0,0,0), set_behavior=1, search_surf=None, inverse_set=False) -> num_threshold_pixels` + + This versatile function can be used for find colors in a 'surf' close to a 'search_color' + or close to colors in a separate 'search_surf'. + + It can also be used to transfer pixels into a 'dest_surf' that match or don't match. + + By default it sets pixels in the 'dest_surf' where all of the pixels NOT within the + threshold are changed to set_color. If inverse_set is optionally set to True, + the pixels that ARE within the threshold are changed to set_color. + + If the optional 'search_surf' surface is given, it is used to threshold against + rather than the specified 'set_color'. That is, it will find each pixel in the + 'surf' that is within the 'threshold' of the pixel at the same coordinates + of the 'search_surf'. + + :param dest_surf: Surface we are changing. See 'set_behavior'. + Should be None if counting (set_behavior is 0). + :type dest_surf: pygame.Surface or None + + :param pygame.Surface surf: Surface we are looking at. + + :param pygame.Color search_color: Color we are searching for. + + :param pygame.Color threshold: Within this distance from search_color (or search_surf). + You can use a threshold of (r,g,b,a) where the r,g,b can have different + thresholds. So you could use an r threshold of 40 and a blue threshold of 2 + if you like. + + :param set_color: Color we set in dest_surf. + :type set_color: pygame.Color or None + + :param int set_behavior: + - set_behavior=1 (default). Pixels in dest_surface will be changed to 'set_color'. + - set_behavior=0 we do not change 'dest_surf', just count. Make dest_surf=None. + - set_behavior=2 pixels set in 'dest_surf' will be from 'surf'. + + :param search_surf: + - search_surf=None (default). Search against 'search_color' instead. + - search_surf=Surface. Look at the color in 'search_surf' rather than using 'search_color'. + :type search_surf: pygame.Surface or None + + :param bool inverse_set: + - False, default. Pixels outside of threshold are changed. + - True, Pixels within threshold are changed. + + :rtype: int + :returns: The number of pixels that are within the 'threshold' in 'surf' + compared to either 'search_color' or `search_surf`. + + :Examples: + + See the threshold tests for a full of examples: https://github.com/pygame/pygame/blob/main/test/transform_test.py + + .. literalinclude:: ../../../test/transform_test.py + :pyobject: TransformModuleTest.test_threshold_dest_surf_not_change + + + .. versionadded:: 1.8 + .. versionchanged:: 1.9.4 + Fixed a lot of bugs and added keyword arguments. Test your code. + + .. ## pygame.transform.threshold ## + +.. ## pygame.transform ## diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_static/basic.css b/.venv/Lib/site-packages/pygame/docs/generated/_static/basic.css new file mode 100644 index 00000000..bf18350b --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_static/basic.css @@ -0,0 +1,906 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 450px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a.brackets:before, +span.brackets > a:before{ + content: "["; +} + +a.brackets:after, +span.brackets > a:after { + content: "]"; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +table.footnote td, table.footnote th { + border: 0 !important; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +dl.footnote > dt, +dl.citation > dt { + float: left; + margin-right: 0.5em; +} + +dl.footnote > dd, +dl.citation > dd { + margin-bottom: 0em; +} + +dl.footnote > dd:after, +dl.citation > dd:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dt:after { + content: ":"; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_static/doctools.js b/.venv/Lib/site-packages/pygame/docs/generated/_static/doctools.js new file mode 100644 index 00000000..e1bfd708 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_static/doctools.js @@ -0,0 +1,358 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for all documentation. + * + * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/** + * select a different prefix for underscore + */ +$u = _.noConflict(); + +/** + * make the code below compatible with browsers without + * an installed firebug like debugger +if (!window.console || !console.firebug) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", + "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", + "profile", "profileEnd"]; + window.console = {}; + for (var i = 0; i < names.length; ++i) + window.console[names[i]] = function() {}; +} + */ + +/** + * small helper function to urldecode strings + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL + */ +jQuery.urldecode = function(x) { + if (!x) { + return x + } + return decodeURIComponent(x.replace(/\+/g, ' ')); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} + +/** + * Small JavaScript module for the documentation. + */ +var Documentation = { + + init : function() { + this.fixFirefoxAnchorBug(); + this.highlightSearchWords(); + this.initIndexTable(); + this.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS : {}, + PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, + LOCALE : 'unknown', + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext : function(string) { + var translated = Documentation.TRANSLATIONS[string]; + if (typeof translated === 'undefined') + return string; + return (typeof translated === 'string') ? translated : translated[0]; + }, + + ngettext : function(singular, plural, n) { + var translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated === 'undefined') + return (n == 1) ? singular : plural; + return translated[Documentation.PLURALEXPR(n)]; + }, + + addTranslations : function(catalog) { + for (var key in catalog.messages) + this.TRANSLATIONS[key] = catalog.messages[key]; + this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); + this.LOCALE = catalog.locale; + }, + + /** + * add context elements like header anchor links + */ + addContextElements : function() { + $('div[id] > :header:first').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this headline')). + appendTo(this); + }); + $('dt[id]').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this definition')). + appendTo(this); + }); + }, + + /** + * workaround a firefox stupidity + * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 + */ + fixFirefoxAnchorBug : function() { + if (document.location.hash && $.browser.mozilla) + window.setTimeout(function() { + document.location.href += ''; + }, 10); + }, + + /** + * highlight the search words provided in the url in the text + */ + highlightSearchWords : function() { + var params = $.getQueryParameters(); + var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; + if (terms.length) { + var body = $('div.body'); + if (!body.length) { + body = $('body'); + } + window.setTimeout(function() { + $.each(terms, function() { + body.highlightText(this.toLowerCase(), 'highlighted'); + }); + }, 10); + $('') + .appendTo($('#searchbox')); + } + }, + + /** + * init the domain index toggle buttons + */ + initIndexTable : function() { + var togglers = $('img.toggler').click(function() { + var src = $(this).attr('src'); + var idnum = $(this).attr('id').substr(7); + $('tr.cg-' + idnum).toggle(); + if (src.substr(-9) === 'minus.png') + $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); + else + $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); + }).css('display', ''); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { + togglers.click(); + } + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords : function() { + $('#searchbox .highlight-link').fadeOut(300); + $('span.highlighted').removeClass('highlighted'); + var url = new URL(window.location); + url.searchParams.delete('highlight'); + window.history.replaceState({}, '', url); + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar : function() { + $('input[name=q]').first().focus(); + }, + + /** + * make the url absolute + */ + makeURL : function(relativeURL) { + return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; + }, + + /** + * get the current relative url + */ + getCurrentURL : function() { + var path = document.location.pathname; + var parts = path.split(/\//); + $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { + if (this === '..') + parts.pop(); + }); + var url = parts.join('/'); + return path.substring(url.lastIndexOf('/') + 1, path.length - 1); + }, + + initOnKeyListeners: function() { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) + return; + + $(document).keydown(function(event) { + var activeElementType = document.activeElement.tagName; + // don't navigate when in search box, textarea, dropdown or button + if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT' + && activeElementType !== 'BUTTON') { + if (event.altKey || event.ctrlKey || event.metaKey) + return; + + if (!event.shiftKey) { + switch (event.key) { + case 'ArrowLeft': + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) + break; + var prevHref = $('link[rel="prev"]').prop('href'); + if (prevHref) { + window.location.href = prevHref; + return false; + } + break; + case 'ArrowRight': + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) + break; + var nextHref = $('link[rel="next"]').prop('href'); + if (nextHref) { + window.location.href = nextHref; + return false; + } + break; + case 'Escape': + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) + break; + Documentation.hideSearchWords(); + return false; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case '/': + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) + break; + Documentation.focusSearchBar(); + return false; + } + } + }); + } +}; + +// quick alias for translations +_ = Documentation.gettext; + +$(document).ready(function() { + Documentation.init(); +}); diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_static/documentation_options.js b/.venv/Lib/site-packages/pygame/docs/generated/_static/documentation_options.js new file mode 100644 index 00000000..5b465a30 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_static/documentation_options.js @@ -0,0 +1,14 @@ +var DOCUMENTATION_OPTIONS = { + URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), + VERSION: '2.5.2', + LANGUAGE: 'None', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_static/file.png b/.venv/Lib/site-packages/pygame/docs/generated/_static/file.png new file mode 100644 index 00000000..a858a410 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/docs/generated/_static/file.png differ diff --git a/.venv/Lib/site-packages/pygame/docs/generated/_static/jquery-3.5.1.js b/.venv/Lib/site-packages/pygame/docs/generated/_static/jquery-3.5.1.js new file mode 100644 index 00000000..50937333 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/_static/jquery-3.5.1.js @@ -0,0 +1,10872 @@ +/*! + * jQuery JavaScript Library v3.5.1 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2020-05-04T22:49Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var flat = arr.flat ? function( array ) { + return arr.flat.call( array ); +} : function( array ) { + return arr.concat.apply( [], array ); +}; + + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + return typeof obj === "function" && typeof obj.nodeType !== "number"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + +var document = window.document; + + + + var preservedScriptAttributes = { + type: true, + src: true, + nonce: true, + noModule: true + }; + + function DOMEval( code, node, doc ) { + doc = doc || document; + + var i, val, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.5.1", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + even: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return ( i + 1 ) % 2; + } ) ); + }, + + odd: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return i % 2; + } ) ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + copy = options[ name ]; + + // Prevent Object.prototype pollution + // Prevent never-ending loop + if ( name === "__proto__" || target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + src = target[ name ]; + + // Ensure proper type for the source value + if ( copyIsArray && !Array.isArray( src ) ) { + clone = []; + } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { + clone = {}; + } else { + clone = src; + } + copyIsArray = false; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a provided context; falls back to the global one + // if not specified. + globalEval: function( code, options, doc ) { + DOMEval( code, { nonce: options && options.nonce }, doc ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return flat( ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( _i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.5 + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://js.foundation/ + * + * Date: 2020-03-14 + */ +( function( window ) { +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + nonnativeSelectorCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ( {} ).hasOwnProperty, + arr = [], + pop = arr.pop, + pushNative = arr.push, + push = arr.push, + slice = arr.slice, + + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[ i ] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" + + "ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram + identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + + "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + + // "Attribute values must be CSS identifiers [capture 5] + // or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + + whitespace + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + + "*" ), + rdescend = new RegExp( whitespace + "|>" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rhtml = /HTML$/i, + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ), + funescape = function( escape, nonHex ) { + var high = "0x" + escape.slice( 1 ) - 0x10000; + + return nonHex ? + + // Strip the backslash prefix from a non-hex escape sequence + nonHex : + + // Replace a hexadecimal escape sequence with the encoded Unicode code point + // Support: IE <=11+ + // For values outside the Basic Multilingual Plane (BMP), manually construct a + // surrogate pair + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + inDisabledFieldset = addCombinator( + function( elem ) { + return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + ( arr = slice.call( preferredDoc.childNodes ) ), + preferredDoc.childNodes + ); + + // Support: Android<4.0 + // Detect silently failing push.apply + // eslint-disable-next-line no-unused-expressions + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + pushNative.apply( target, slice.call( els ) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + + // Can't trust NodeList.length + while ( ( target[ j++ ] = els[ i++ ] ) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + setDocument( context ); + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) { + + // ID selector + if ( ( m = match[ 1 ] ) ) { + + // Document context + if ( nodeType === 9 ) { + if ( ( elem = context.getElementById( m ) ) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && ( elem = newContext.getElementById( m ) ) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[ 2 ] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !nonnativeSelectorCache[ selector + " " ] && + ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) && + + // Support: IE 8 only + // Exclude object elements + ( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) { + + newSelector = selector; + newContext = context; + + // qSA considers elements outside a scoping root when evaluating child or + // descendant combinators, which is not what we want. + // In such cases, we work around the behavior by prefixing every selector in the + // list with an ID selector referencing the scope context. + // The technique has to be used as well when a leading combinator is used + // as such selectors are not recognized by querySelectorAll. + // Thanks to Andrew Dupont for this technique. + if ( nodeType === 1 && + ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) { + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + + // We can use :scope instead of the ID hack if the browser + // supports it & if we're not changing the context. + if ( newContext !== context || !support.scope ) { + + // Capture the context ID, setting it first if necessary + if ( ( nid = context.getAttribute( "id" ) ) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", ( nid = expando ) ); + } + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + + toSelector( groups[ i ] ); + } + newSelector = groups.join( "," ); + } + + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + nonnativeSelectorCache( selector, true ); + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return ( cache[ key + " " ] = value ); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement( "fieldset" ); + + try { + return !!fn( el ); + } catch ( e ) { + return false; + } finally { + + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split( "|" ), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[ i ] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( ( cur = cur.nextSibling ) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return ( name === "input" || name === "button" ) && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + inDisabledFieldset( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction( function( argument ) { + argument = +argument; + return markFunction( function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ ( j = matchIndexes[ i ] ) ] ) { + seed[ j ] = !( matches[ j ] = seed[ j ] ); + } + } + } ); + } ); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + var namespace = elem.namespaceURI, + docElem = ( elem.ownerDocument || elem ).documentElement; + + // Support: IE <=8 + // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes + // https://bugs.jquery.com/ticket/4833 + return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9 - 11+, Edge 12 - 18+ + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( preferredDoc != document && + ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + // Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only, + // Safari 4 - 5 only, Opera <=11.6 - 12.x only + // IE/Edge & older browsers don't support the :scope pseudo-class. + // Support: Safari 6.0 only + // Safari 6.0 supports :scope but it's an alias of :root there. + support.scope = assert( function( el ) { + docElem.appendChild( el ).appendChild( document.createElement( "div" ) ); + return typeof el.querySelectorAll !== "undefined" && + !el.querySelectorAll( ":scope fieldset div" ).length; + } ); + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert( function( el ) { + el.className = "i"; + return !el.getAttribute( "className" ); + } ); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert( function( el ) { + el.appendChild( document.createComment( "" ) ); + return !el.getElementsByTagName( "*" ).length; + } ); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert( function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + } ); + + // ID filter and find + if ( support.getById ) { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute( "id" ) === attrId; + }; + }; + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode( "id" ); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( ( elem = elems[ i++ ] ) ) { + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find[ "TAG" ] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) { + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert( function( el ) { + + var input; + + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll( "[selected]" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push( "~=" ); + } + + // Support: IE 11+, Edge 15 - 18+ + // IE 11/Edge don't find elements on a `[name='']` query in some cases. + // Adding a temporary attribute to the document before the selection works + // around the issue. + // Interestingly, IE 10 & older don't seem to have the issue. + input = document.createElement( "input" ); + input.setAttribute( "name", "" ); + el.appendChild( input ); + if ( !el.querySelectorAll( "[name='']" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + + whitespace + "*(?:''|\"\")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll( ":checked" ).length ) { + rbuggyQSA.push( ":checked" ); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push( ".#.+[+~]" ); + } + + // Support: Firefox <=3.6 - 5 only + // Old Firefox doesn't throw on a badly-escaped identifier. + el.querySelectorAll( "\\\f" ); + rbuggyQSA.push( "[\\r\\n\\f]" ); + } ); + + assert( function( el ) { + el.innerHTML = "" + + ""; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement( "input" ); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll( "[name=d]" ).length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll( ":enabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: Opera 10 - 11 only + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll( "*,:x" ); + rbuggyQSA.push( ",.*:" ); + } ); + } + + if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector ) ) ) ) { + + assert( function( el ) { + + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + } ); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + ) ); + } : + function( a, b ) { + if ( b ) { + while ( ( b = b.parentNode ) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) { + + // Choose the first element that is related to our preferred document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( a == document || a.ownerDocument == preferredDoc && + contains( preferredDoc, a ) ) { + return -1; + } + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( b == document || b.ownerDocument == preferredDoc && + contains( preferredDoc, b ) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + return a == document ? -1 : + b == document ? 1 : + /* eslint-enable eqeqeq */ + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( ( cur = cur.parentNode ) ) { + ap.unshift( cur ); + } + cur = b; + while ( ( cur = cur.parentNode ) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[ i ] === bp[ i ] ) { + i++; + } + + return i ? + + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[ i ], bp[ i ] ) : + + // Otherwise nodes in our document sort first + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + ap[ i ] == preferredDoc ? -1 : + bp[ i ] == preferredDoc ? 1 : + /* eslint-enable eqeqeq */ + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + setDocument( elem ); + + if ( support.matchesSelector && documentIsHTML && + !nonnativeSelectorCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch ( e ) { + nonnativeSelectorCache( expr, true ); + } + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( context.ownerDocument || context ) != document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( elem.ownerDocument || elem ) != document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return ( sel + "" ).replace( rcssescape, fcssescape ); +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + + // If no nodeType, this is expected to be an array + while ( ( node = elem[ i++ ] ) ) { + + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[ 1 ] = match[ 1 ].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[ 3 ] = ( match[ 3 ] || match[ 4 ] || + match[ 5 ] || "" ).replace( runescape, funescape ); + + if ( match[ 2 ] === "~=" ) { + match[ 3 ] = " " + match[ 3 ] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[ 1 ] = match[ 1 ].toLowerCase(); + + if ( match[ 1 ].slice( 0, 3 ) === "nth" ) { + + // nth-* requires argument + if ( !match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[ 4 ] = +( match[ 4 ] ? + match[ 5 ] + ( match[ 6 ] || 1 ) : + 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) ); + match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); + + // other types prohibit arguments + } else if ( match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[ 6 ] && match[ 2 ]; + + if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[ 3 ] ) { + match[ 2 ] = match[ 4 ] || match[ 5 ] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + + // Get excess from tokenize (recursively) + ( excess = tokenize( unquoted, true ) ) && + + // advance to the next closing parenthesis + ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) { + + // excess is a negative index + match[ 0 ] = match[ 0 ].slice( 0, excess ); + match[ 2 ] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { + return true; + } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + ( pattern = new RegExp( "(^|" + whitespace + + ")" + className + "(" + whitespace + "|$)" ) ) && classCache( + className, function( elem ) { + return pattern.test( + typeof elem.className === "string" && elem.className || + typeof elem.getAttribute !== "undefined" && + elem.getAttribute( "class" ) || + "" + ); + } ); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + /* eslint-disable max-len */ + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + /* eslint-enable max-len */ + + }; + }, + + "CHILD": function( type, what, _argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, _context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( ( node = node[ dir ] ) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( ( node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + + // Use previously-cached element index if available + if ( useCache ) { + + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + + // Use the same loop as above to seek `elem` from the start + while ( ( node = ++nodeIndex && node && node[ dir ] || + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || + ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction( function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[ i ] ); + seed[ idx ] = !( matches[ idx ] = matched[ i ] ); + } + } ) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + + // Potentially complex pseudos + "not": markFunction( function( selector ) { + + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction( function( seed, matches, _context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( ( elem = unmatched[ i ] ) ) { + seed[ i ] = !( matches[ i ] = elem ); + } + } + } ) : + function( elem, _context, xml ) { + input[ 0 ] = elem; + matcher( input, null, xml, results ); + + // Don't keep the element (issue #299) + input[ 0 ] = null; + return !results.pop(); + }; + } ), + + "has": markFunction( function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + } ), + + "contains": markFunction( function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; + }; + } ), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + + // lang value must be a valid identifier + if ( !ridentifier.test( lang || "" ) ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( ( elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 ); + return false; + }; + } ), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && + ( !document.hasFocus || document.hasFocus() ) && + !!( elem.type || elem.href || ~elem.tabIndex ); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return ( nodeName === "input" && !!elem.checked ) || + ( nodeName === "option" && !!elem.selected ); + }, + + "selected": function( elem ) { + + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + // eslint-disable-next-line no-unused-expressions + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos[ "empty" ]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( ( attr = elem.getAttribute( "type" ) ) == null || + attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo( function() { + return [ 0 ]; + } ), + + "last": createPositionalPseudo( function( _matchIndexes, length ) { + return [ length - 1 ]; + } ), + + "eq": createPositionalPseudo( function( _matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + } ), + + "even": createPositionalPseudo( function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "odd": createPositionalPseudo( function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "lt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? + argument + length : + argument > length ? + length : + argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "gt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ) + } +}; + +Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || ( match = rcomma.exec( soFar ) ) ) { + if ( match ) { + + // Don't consume trailing commas as valid + soFar = soFar.slice( match[ 0 ].length ) || soFar; + } + groups.push( ( tokens = [] ) ); + } + + matched = false; + + // Combinators + if ( ( match = rcombinators.exec( soFar ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + + // Cast descendant combinators to space + type: match[ 0 ].replace( rtrim, " " ) + } ); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] || + ( match = preFilters[ type ]( match ) ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + type: type, + matches: match + } ); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[ i ].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || ( elem[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || + ( outerCache[ elem.uniqueID ] = {} ); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( ( oldCache = uniqueCache[ key ] ) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return ( newCache[ 2 ] = oldCache[ 2 ] ); + } else { + + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { + return true; + } + } + } + } + } + return false; + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[ i ]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[ 0 ]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[ i ], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( ( elem = unmatched[ i ] ) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction( function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( + selector || "*", + context.nodeType ? [ context ] : context, + [] + ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( ( elem = temp[ i ] ) ) { + matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem ); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) ) { + + // Restore matcherIn since elem is not yet a final match + temp.push( ( matcherIn[ i ] = elem ) ); + } + } + postFinder( null, ( matcherOut = [] ), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) && + ( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) { + + seed[ temp ] = !( results[ temp ] = elem ); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + } ); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[ 0 ].type ], + implicitRelative = leadingRelative || Expr.relative[ " " ], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + ( checkContext = context ).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[ j ].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens + .slice( 0, i - 1 ) + .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ), + + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ), + len = elems.length; + + if ( outermost ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + outermostContext = context == document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( !context && elem.ownerDocument != document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( ( matcher = elementMatchers[ j++ ] ) ) { + if ( matcher( elem, context || document, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + + // They will have gone through all possible matchers + if ( ( elem = !matcher && elem ) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( ( matcher = setMatchers[ j++ ] ) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !( unmatched[ i ] || setMatched[ i ] ) ) { + setMatched[ i ] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[ i ] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( + selector, + matcherFromGroupMatchers( elementMatchers, setMatchers ) + ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( ( selector = compiled.selector || selector ) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[ 0 ] = match[ 0 ].slice( 0 ); + if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { + + context = ( Expr.find[ "ID" ]( token.matches[ 0 ] + .replace( runescape, funescape ), context ) || [] )[ 0 ]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[ i ]; + + // Abort if we hit a combinator + if ( Expr.relative[ ( type = token.type ) ] ) { + break; + } + if ( ( find = Expr.find[ type ] ) ) { + + // Search, expanding context for leading sibling combinators + if ( ( seed = find( + token.matches[ 0 ].replace( runescape, funescape ), + rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) || + context + ) ) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert( function( el ) { + + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1; +} ); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert( function( el ) { + el.innerHTML = ""; + return el.firstChild.getAttribute( "href" ) === "#"; +} ) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + } ); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert( function( el ) { + el.innerHTML = ""; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; +} ) ) { + addHandle( "value", function( elem, _name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + } ); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert( function( el ) { + return el.getAttribute( "disabled" ) == null; +} ) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; + } + } ); +} + +return Sizzle; + +} )( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +}; +var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + + + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, _i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, _i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, _i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( elem.contentDocument != null && + + // Support: IE 11+ + // elements with no `data` attribute has an object + // `contentDocument` with a `null` prototype. + getProto( elem.contentDocument ) ) { + + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( _i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the master Deferred + master = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + master.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( master.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return master.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); + } + + return master.promise(); + } +} ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + + + + +// The deferred used on DOM ready +var readyList = jQuery.Deferred(); + +jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); + +jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, _key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + + +// Matches dashed string for camelizing +var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() +function fcamelCase( _all, letter ) { + return letter.toUpperCase(); +} + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var documentElement = document.documentElement; + + + + var isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ); + }, + composed = { composed: true }; + + // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only + // Check attachment across shadow DOM boundaries when possible (gh-3504) + // Support: iOS 10.0-10.2 only + // Early iOS 10 versions support `attachShadow` but not `getRootNode`, + // leading to errors. We need to check for `getRootNode`. + if ( documentElement.getRootNode ) { + isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ) || + elem.getRootNode( composed ) === elem.ownerDocument; + }; + } +var isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + isAttached( elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = elem.nodeType && + ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +var defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); + +var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + + // Support: IE <=9 only + // IE <=9 replaces "; + support.option = !!div.lastChild; +} )(); + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "
" ], + col: [ 2, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + _default: [ 0, "", "" ] +}; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// Support: IE <=9 only +if ( !support.option ) { + wrapMap.optgroup = wrapMap.option = [ 1, "" ]; +} + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, attached, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + attached = isAttached( elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( attached ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 - 11+ +// focus() and blur() are asynchronous, except when they are no-op. +// So expect focus to be synchronous when the element is already active, +// and blur to be synchronous when the element is not already active. +// (focus and blur are always synchronous in other supported browsers, +// this just defines when we can count on it). +function expectSync( elem, type ) { + return ( elem === safeActiveElement() ) === ( type === "focus" ); +} + +// Support: IE <=9 only +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Only attach events to objects that accept data + if ( !acceptData( elem ) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = Object.create( null ); + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( nativeEvent ), + + handlers = ( + dataPriv.get( this, "events" ) || Object.create( null ) + )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // If the event is namespaced, then each handler is only invoked if it is + // specially universal or its namespaces are a superset of the event's. + if ( !event.rnamespace || handleObj.namespace === false || + event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { + + // Utilize native event to ensure correct state for checkable inputs + setup: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Claim the first handler + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + // dataPriv.set( el, "click", ... ) + leverageNative( el, "click", returnTrue ); + } + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Force setup before triggering a click + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + leverageNative( el, "click" ); + } + + // Return non-false to allow normal event-path propagation + return true; + }, + + // For cross-browser consistency, suppress native .click() on links + // Also prevent it if we're currently inside a leveraged native-event stack + _default: function( event ) { + var target = event.target; + return rcheckableType.test( target.type ) && + target.click && nodeName( target, "input" ) && + dataPriv.get( target, "click" ) || + nodeName( target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +// Ensure the presence of an event listener that handles manually-triggered +// synthetic events by interrupting progress until reinvoked in response to +// *native* events that it fires directly, ensuring that state changes have +// already occurred before other listeners are invoked. +function leverageNative( el, type, expectSync ) { + + // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add + if ( !expectSync ) { + if ( dataPriv.get( el, type ) === undefined ) { + jQuery.event.add( el, type, returnTrue ); + } + return; + } + + // Register the controller as a special universal handler for all event namespaces + dataPriv.set( el, type, false ); + jQuery.event.add( el, type, { + namespace: false, + handler: function( event ) { + var notAsync, result, + saved = dataPriv.get( this, type ); + + if ( ( event.isTrigger & 1 ) && this[ type ] ) { + + // Interrupt processing of the outer synthetic .trigger()ed event + // Saved data should be false in such cases, but might be a leftover capture object + // from an async native handler (gh-4350) + if ( !saved.length ) { + + // Store arguments for use when handling the inner native event + // There will always be at least one argument (an event object), so this array + // will not be confused with a leftover capture object. + saved = slice.call( arguments ); + dataPriv.set( this, type, saved ); + + // Trigger the native event and capture its result + // Support: IE <=9 - 11+ + // focus() and blur() are asynchronous + notAsync = expectSync( this, type ); + this[ type ](); + result = dataPriv.get( this, type ); + if ( saved !== result || notAsync ) { + dataPriv.set( this, type, false ); + } else { + result = {}; + } + if ( saved !== result ) { + + // Cancel the outer synthetic event + event.stopImmediatePropagation(); + event.preventDefault(); + return result.value; + } + + // If this is an inner synthetic event for an event with a bubbling surrogate + // (focus or blur), assume that the surrogate already propagated from triggering the + // native event and prevent that from happening again here. + // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the + // bubbling surrogate propagates *after* the non-bubbling base), but that seems + // less bad than duplication. + } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { + event.stopPropagation(); + } + + // If this is a native event triggered above, everything is now in order + // Fire an inner synthetic event with the original arguments + } else if ( saved.length ) { + + // ...and capture the result + dataPriv.set( this, type, { + value: jQuery.event.trigger( + + // Support: IE <=9 - 11+ + // Extend with the prototype to reset the above stopImmediatePropagation() + jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), + saved.slice( 1 ), + this + ) + } ); + + // Abort handling of the native event + event.stopImmediatePropagation(); + } + } + } ); +} + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + code: true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + + which: function( event ) { + var button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + if ( button & 1 ) { + return 1; + } + + if ( button & 2 ) { + return 3; + } + + if ( button & 4 ) { + return 2; + } + + return 0; + } + + return event.which; + } +}, jQuery.event.addProp ); + +jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + jQuery.event.special[ type ] = { + + // Utilize native event if possible so blur/focus sequence is correct + setup: function() { + + // Claim the first handler + // dataPriv.set( this, "focus", ... ) + // dataPriv.set( this, "blur", ... ) + leverageNative( this, type, expectSync ); + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function() { + + // Force setup before trigger + leverageNative( this, type ); + + // Return non-false to allow normal event-path propagation + return true; + }, + + delegateType: delegateType + }; +} ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; + +// Prefer a tbody over its parent table for containing new rows +function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.get( src ); + events = pdataOld.events; + + if ( events ) { + dataPriv.remove( dest, "handle events" ); + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = flat( args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl && !node.noModule ) { + jQuery._evalUrl( node.src, { + nonce: node.nonce || node.getAttribute( "nonce" ) + }, doc ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && isAttached( node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html; + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = isAttached( elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + +var swap = function( elem, options, callback ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + +var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); + + + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; + div.style.cssText = + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + + "margin:auto;border:1px;padding:1px;" + + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; + + // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 + // Some styles come back with percentage values, even though they shouldn't + div.style.right = "60%"; + pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + // Support: Chrome <=64 + // Don't get tricked when zoom affects offsetWidth (gh-4029) + div.style.position = "absolute"; + scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + function roundPixelMeasures( measure ) { + return Math.round( parseFloat( measure ) ); + } + + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, + reliableTrDimensionsVal, reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + jQuery.extend( support, { + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function() { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; + }, + + // Support: IE 9 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Behavior in IE 9 is more subtle than in newer versions & it passes + // some versions of this test; make sure not to make it pass there! + reliableTrDimensions: function() { + var table, tr, trChild, trStyle; + if ( reliableTrDimensionsVal == null ) { + table = document.createElement( "table" ); + tr = document.createElement( "tr" ); + trChild = document.createElement( "div" ); + + table.style.cssText = "position:absolute;left:-11111px"; + tr.style.height = "1px"; + trChild.style.height = "9px"; + + documentElement + .appendChild( table ) + .appendChild( tr ) + .appendChild( trChild ); + + trStyle = window.getComputedStyle( tr ); + reliableTrDimensionsVal = parseInt( trStyle.height ) > 3; + + documentElement.removeChild( table ); + } + return reliableTrDimensionsVal; + } + } ); +} )(); + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !isAttached( elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + + +var cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style, + vendorProps = {}; + +// Return a vendor-prefixed property or undefined +function vendorPropName( name ) { + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +// Return a potentially-mapped jQuery.cssProps or vendor prefixed property +function finalPropName( name ) { + var final = jQuery.cssProps[ name ] || vendorProps[ name ]; + + if ( final ) { + return final; + } + if ( name in emptyStyle ) { + return name; + } + return vendorProps[ name ] = vendorPropName( name ) || name; +} + + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }; + +function setPositiveNumber( _elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { + var i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0; + + // Adjustment may not be necessary + if ( box === ( isBorderBox ? "border" : "content" ) ) { + return 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin + if ( box === "margin" ) { + delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + } + + // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + if ( !isBorderBox ) { + + // Add padding + delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // For "border" or "margin", add border + if ( box !== "padding" ) { + delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + + // But still keep track of it otherwise + } else { + extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + + // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + } else { + + // For "content", subtract padding + if ( box === "content" ) { + delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // For "content" or "padding", subtract border + if ( box !== "margin" ) { + delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + // Account for positive content-box scroll gutter when requested by providing computedVal + if ( !isBorderBox && computedVal >= 0 ) { + + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max( 0, Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + computedVal - + delta - + extra - + 0.5 + + // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter + // Use an explicit zero to avoid NaN (gh-3964) + ) ) || 0; + } + + return delta; +} + +function getWidthOrHeight( elem, dimension, extra ) { + + // Start with computed style + var styles = getStyles( elem ), + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). + // Fake content-box until we know it's needed to know the true value. + boxSizingNeeded = !support.boxSizingReliable() || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox, + + val = curCSS( elem, dimension, styles ), + offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); + + // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + if ( rnumnonpx.test( val ) ) { + if ( !extra ) { + return val; + } + val = "auto"; + } + + + // Support: IE 9 - 11 only + // Use offsetWidth/offsetHeight for when box sizing is unreliable. + // In those cases, the computed value can be trusted to be border-box. + if ( ( !support.boxSizingReliable() && isBorderBox || + + // Support: IE 10 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Interestingly, in some cases IE 9 doesn't suffer from this issue. + !support.reliableTrDimensions() && nodeName( elem, "tr" ) || + + // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + val === "auto" || + + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && + + // Make sure the element is visible & connected + elem.getClientRects().length ) { + + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // Where available, offsetWidth/offsetHeight approximate border box dimensions. + // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the + // retrieved value as a content box dimension. + valueIsBorderBox = offsetProp in elem; + if ( valueIsBorderBox ) { + val = elem[ offsetProp ]; + } + } + + // Normalize "" and auto + val = parseFloat( val ) || 0; + + // Adjust for the element's box model + return ( val + + boxModelAdjustment( + elem, + dimension, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles, + + // Provide the current computed size to request scroll gutter calculation (gh-3589) + val + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "gridArea": true, + "gridColumn": true, + "gridColumnEnd": true, + "gridColumnStart": true, + "gridRow": true, + "gridRowEnd": true, + "gridRowStart": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append + // "px" to a few hardcoded values. + if ( type === "number" && !isCustomProp ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( _i, dimension ) { + jQuery.cssHooks[ dimension ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = getStyles( elem ), + + // Only read styles.position if the test has a chance to fail + // to avoid forcing a reflow. + scrollboxSizeBuggy = !support.scrollboxSize() && + styles.position === "absolute", + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) + boxSizingNeeded = scrollboxSizeBuggy || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra ? + boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ) : + 0; + + // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + if ( isBorderBox && scrollboxSizeBuggy ) { + subtract -= Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + parseFloat( styles[ dimension ] ) - + boxModelAdjustment( elem, dimension, "border", false, styles ) - + 0.5 + ); + } + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ dimension ] = value; + value = jQuery.css( elem, dimension ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( prefix !== "margin" ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && ( + jQuery.cssHooks[ tween.prop ] || + tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = Date.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 15 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY and Edge just mirrors + // the overflowX value there. + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + result.stop.bind( result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = Date.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); +}; + +jQuery.fx.stop = function() { + inProgress = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + + +( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + + +var boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + + + + + // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + function stripAndCollapse( value ) { + var tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +function classesToArray( value ) { + if ( Array.isArray( value ) ) { + return value; + } + if ( typeof value === "string" ) { + return value.match( rnothtmlwhite ) || []; + } + return []; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isValidValue = type === "string" || Array.isArray( value ); + + if ( typeof stateVal === "boolean" && isValidValue ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( isValidValue ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = classesToArray( value ); + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } +} ); + + + + +var rreturn = /\r/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, valueIsFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( valueIsFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + + + + +// Return jQuery for attributes-only inclusion + + +support.focusin = "onfocusin" in window; + + +var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function( e ) { + e.stopPropagation(); + }; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = lastElement = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + lastElement = cur; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( + dataPriv.get( cur, "events" ) || Object.create( null ) + )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + + if ( event.isPropagationStopped() ) { + lastElement.addEventListener( type, stopPropagationCallback ); + } + + elem[ type ](); + + if ( event.isPropagationStopped() ) { + lastElement.removeEventListener( type, stopPropagationCallback ); + } + + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + + // Handle: regular nodes (via `this.ownerDocument`), window + // (via `this.document`) & document (via `this`). + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} +var location = window.location; + +var nonce = { guid: Date.now() }; + +var rquery = ( /\?/ ); + + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) { + xml = undefined; + } + + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + + +var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && toType( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + if ( a == null ) { + return ""; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ) + .filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ) + .map( function( _i, elem ) { + var val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + + +var + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() + " " ] = + ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) + .concat( match[ 2 ] ); + } + } + match = responseHeaders[ key.toLowerCase() + " " ]; + } + return match == null ? null : match.join( ", " ); + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 15 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available and should be processed, append data to url + if ( s.data && ( s.processData || typeof s.data === "string" ) ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) + + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Use a noop converter for missing script + if ( !isSuccess && jQuery.inArray( "script", s.dataTypes ) > -1 ) { + s.converters[ "text script" ] = function() {}; + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( _i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + +jQuery.ajaxPrefilter( function( s ) { + var i; + for ( i in s.headers ) { + if ( i.toLowerCase() === "content-type" ) { + s.contentType = s.headers[ i ] || ""; + } + } +} ); + + +jQuery._evalUrl = function( url, options, doc ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + + // Only evaluate the response if it is successful (gh-4126) + // dataFilter is not invoked for failure responses, so using it instead + // of the default converter is kludgy but it works. + converters: { + "text script": function() {} + }, + dataFilter: function( response ) { + jQuery.globalEval( response, options, doc ); + } + } ); +}; + + +jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var htmlIsFunction = isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + + + + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.ontimeout = + xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain or forced-by-attrs requests + if ( s.crossDomain || s.scriptAttrs ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( " + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/c_api/base.html b/.venv/Lib/site-packages/pygame/docs/generated/c_api/base.html new file mode 100644 index 00000000..bb6fc200 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/c_api/base.html @@ -0,0 +1,359 @@ + + + + + + + + + High level API exported by pygame.base — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

src_c/base.c

+

This extension module defines general purpose routines for starting and stopping +SDL as well as various conversion routines uses elsewhere in pygame.

+

C header: src_c/include/pygame.h

+
+
+PyObject *pgExc_SDLError
+

This is pygame.error, the exception type used to raise SDL errors.

+
+ +
+
+int pg_mod_autoinit(const char *modname)
+

Inits a pygame module, which has the name modname +Return 1 on success, 0 on error, with python +error set.

+
+ +
+
+void pg_mod_autoquit(const char *modname)
+

Quits a pygame module, which has the name modname

+
+ +
+
+void pg_RegisterQuit(void (*f)(void))
+

Register function f as a callback on Pygame termination. +Multiple functions can be registered. +Functions are called in the reverse order they were registered.

+
+ +
+
+int pg_IntFromObj(PyObject *obj, int *val)
+

Convert number like object obj to C int and place in argument val. +Return 1 on success, else 0. +No Python exceptions are raised.

+
+ +
+
+int pg_IntFromObjIndex(PyObject *obj, int index, int *val)
+

Convert number like object at position i in sequence obj +to C int and place in argument val. +Return 1 on success, 0 on failure. +No Python exceptions are raised.

+
+ +
+
+int pg_TwoIntsFromObj(PyObject *obj, int *val1, int *v2)
+

Convert the two number like objects in length 2 sequence obj +to C int and place in arguments val1 and val2 respectively. +Return 1 on success, 0 on failure. +No Python exceptions are raised.

+
+ +
+
+int pg_FloatFromObj(PyObject *obj, float *val)
+

Convert number like object obj to C float and place in argument val. +Returns 1 on success, 0 on failure. +No Python exceptions are raised.

+
+ +
+
+int pg_FloatFromObjIndex(PyObject *obj, int index, float *val)
+

Convert number like object at position i in sequence obj +to C float and place in argument val. +Return 1 on success, else 0. +No Python exceptions are raised.

+
+ +
+
+int pg_TwoFloatsFromObj(PyObject *obj, float *val1, float *val2)
+

Convert the two number like objects in length 2 sequence obj +to C float and place in arguments val1 and val2 respectively. +Return 1 on success, else 0. +No Python exceptions are raised.

+
+ +
+
+int pg_UintFromObj(PyObject *obj, Uint32 *val)
+

Convert number like object obj to unsigned 32 bit integer and place +in argument val. +Return 1 on success, else 0. +No Python exceptions are raised.

+
+ +
+
+int pg_UintFromObjIndex(PyObject *obj, int _index, Uint32 *val)
+

Convert number like object at position i in sequence obj +to unsigned 32 bit integer and place in argument val. +Return 1 on success, else 0. +No Python exceptions are raised.

+
+ +
+
+int pg_RGBAFromObj(PyObject *obj, Uint8 *RGBA)
+

Convert the color represented by object obj into a red, green, blue, alpha +length 4 C array RGBA. +The object must be a length 3 or 4 sequence of numbers having values +between 0 and 255 inclusive. +For a length 3 sequence an alpha value of 255 is assumed. +Return 1 on success, 0 otherwise. +No Python exceptions are raised.

+
+ +
+
+type pg_buffer
+
+
+Py_buffer view
+

A standard buffer description

+
+ +
+
+PyObject *consumer
+

The object holding the buffer

+
+ +
+
+pybuffer_releaseproc release_buffer
+

A buffer release callback.

+
+ +
+ +
+
+PyObject *pgExc_BufferError
+

Python exception type raised for any pg_buffer related errors.

+
+ +
+
+PyObject *pgBuffer_AsArrayInterface(Py_buffer *view_p)
+

Return a Python array interface object representation of buffer view_p. +On failure raise a Python exception and return NULL.

+
+ +
+
+PyObject *pgBuffer_AsArrayStruct(Py_buffer *view_p)
+

Return a Python array struct object representation of buffer view_p. +On failure raise a Python exception and return NULL.

+
+ +
+
+int pgObject_GetBuffer(PyObject *obj, pg_buffer *pg_view_p, int flags)
+

Request a buffer for object obj. +Argument flags are PyBUF options. +Return the buffer description in pg_view_p. +An object may support the Python buffer interface, the NumPy array interface, +or the NumPy array struct interface. +Return 0 on success, raise a Python exception and return -1 on failure.

+
+ +
+
+void pgBuffer_Release(Pg_buffer *pg_view_p)
+

Release the Pygame pg_view_p buffer.

+
+ +
+
+int pgDict_AsBuffer(Pg_buffer *pg_view_p, PyObject *dict, int flags)
+

Write the array interface dictionary buffer description dict into a Pygame +buffer description struct pg_view_p. +The flags PyBUF options describe the view type requested. +Return 0 on success, or raise a Python exception and return -1 on failure.

+
+ +
+
+void import_pygame_base()
+

Import the pygame.base module C API into an extension module. +On failure raise a Python exception.

+
+ +
+
+SDL_Window *pg_GetDefaultWindow(void)
+

Return the Pygame default SDL window created by a +pygame.display.set_mode() call, or NULL.

+
+ +
+
+void pg_SetDefaultWindow(SDL_Window *win)
+

Replace the Pygame default window with win. +The previous window, if any, is destroyed. +Argument win may be NULL. +This function is called by pygame.display.set_mode().

+
+ +
+
+pgSurfaceObject *pg_GetDefaultWindowSurface(void)
+

Return a borrowed reference to the Pygame default window display surface, +or NULL if no default window is open.

+
+ +
+
+void pg_SetDefaultWindowSurface(pgSurfaceObject *screen)
+

Replace the Pygame default display surface with object screen. +The previous surface object, if any, is invalidated. +Argument screen may be NULL. +This functions is called by pygame.display.set_mode().

+
+ +
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/c_api/bufferproxy.html b/.venv/Lib/site-packages/pygame/docs/generated/c_api/bufferproxy.html new file mode 100644 index 00000000..4e21f883 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/c_api/bufferproxy.html @@ -0,0 +1,181 @@ + + + + + + + + + Class BufferProxy API exported by pygame.bufferproxy — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

src_c/bufferproxy.c

+

This extension module defines Python type pygame.BufferProxypygame object to export a surface buffer through an array protocol.

+

Header file: src_c/include/pygame_bufferproxy.h

+
+
+PyTypeObject *pgBufproxy_Type
+

The pygame buffer proxy object type pygame.BufferProxy.

+
+ +
+
+int pgBufproxy_Check(PyObject *x)
+

Return true if Python object x is a pygame.BufferProxy instance, +false otherwise. +This will return false on pygame.BufferProxy subclass instances as well.

+
+ +
+
+PyObject *pgBufproxy_New(PyObject *obj, getbufferproc get_buffer)
+

Return a new pygame.BufferProxy instance. +Argument obj is the Python object that has its data exposed. +It may be NULL. +Argument get_buffer is the pg_buffer get callback. +It must not be NULL. +On failure raise a Python error and return NULL.

+
+ +
+
+PyObject *pgBufproxy_GetParent(PyObject *obj)
+

Return the Python object wrapped by buffer proxy obj. +Argument obj must not be NULL. +On failure, raise a Python error and return NULL.

+
+ +
+
+int pgBufproxy_Trip(PyObject *obj)
+

Cause the buffer proxy object obj to create a pg_buffer view of its parent. +Argument obj must not be NULL. +Return 0 on success, otherwise raise a Python error and return -1.

+
+ +
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/c_api/color.html b/.venv/Lib/site-packages/pygame/docs/generated/c_api/color.html new file mode 100644 index 00000000..81115fe6 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/c_api/color.html @@ -0,0 +1,170 @@ + + + + + + + + + Class Color API exported by pygame.color — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

src_c/color.c

+

This extension module defines the Python type pygame.Colorpygame object for color representations.

+

Header file: src_c/include/pygame.h

+
+
+PyTypeObject *pgColor_Type
+

The Pygame color object type pygame.Color.

+
+ +
+
+int pgColor_Check(PyObject *obj)
+

Return true if obj is an instance of type pgColor_Type, +but not a pgColor_Type subclass instance. +This macro does not check if obj is not NULL or indeed a Python type.

+
+ +
+
+PyObject *pgColor_New(Uint8 rgba[])
+

Return a new pygame.Color instance for the the four element array rgba. +On failure, raise a Python exception and return NULL.

+
+ +
+
+PyObject *pgColor_NewLength(Uint8 rgba[], Uint8 length)
+

Return a new pygame.Color instance having length elements, +with element values taken from the first length elements of array rgba. +Argument length must be between 1 and 4 inclusive. +On failure, raise a Python exception and return NULL.

+
+ +
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/c_api/display.html b/.venv/Lib/site-packages/pygame/docs/generated/c_api/display.html new file mode 100644 index 00000000..4d175a55 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/c_api/display.html @@ -0,0 +1,175 @@ + + + + + + + + + API exported by pygame.display — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

src_c/display.c

+

This is the pygame.displaypygame module to control the display window and screen extension module.

+

Header file: src_c/include/pygame.h

+
+
+type pgVidInfoObject
+

A pygame object that wraps an SDL_VideoInfo struct. +The object returned by pygame.display.Info().

+
+ +
+
+PyTypeObject *pgVidInfo_Type
+

The pgVidInfoObject object Python type.

+
+ +
+
+SDL_VideoInfo pgVidInfo_AsVidInfo(PyObject *obj)
+

Return the SDL_VideoInfo field of obj, a pgVidInfo_Type instance. +This macro does not check that obj is not NULL or an actual pgVidInfoObject object.

+
+ +
+
+PyObject *pgVidInfo_New(SDL_VideoInfo *i)
+

Return a new pgVidInfoObject object for the SDL_VideoInfo i. +On failure, raise a Python exception and return NULL.

+
+ +
+
+int pgVidInfo_Check(PyObject *x)
+

Return true if x is a pgVidInfo_Type instance

+

Will return false if x is a subclass of pgVidInfo_Type. +This macro does not check that x is not NULL.

+
+ +
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/c_api/event.html b/.venv/Lib/site-packages/pygame/docs/generated/c_api/event.html new file mode 100644 index 00000000..67cc5f6e --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/c_api/event.html @@ -0,0 +1,190 @@ + + + + + + + + + API exported by pygame.event — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

src_c/event.c

+

The extension module pygame.eventpygame module for interacting with events and queues.

+

Header file: src_c/include/pygame.h

+
+
+type pgEventObject
+

The pygame.event.EventType object C struct.

+
+
+int type
+

The event type code.

+
+ +
+ +
+
+type pgEvent_Type
+

The pygame event object type pygame.event.EventType.

+
+ +
+
+int pgEvent_Check(PyObject *x)
+

Return true if x is a pygame event instance

+

Will return false if x is a subclass of event. +This is a macro. No check is made that x is not NULL.

+
+ +
+
+PyObject *pgEvent_New(SDL_Event *event)
+

Return a new pygame event instance for the SDL event. +If event is NULL then create an empty event object. +On failure raise a Python exception and return NULL.

+
+ +
+
+PyObject *pgEvent_New2(int type, PyObject *dict)
+

Return a new pygame event instance of SDL type and with +attribute dictionary dict. +If dict is NULL an empty attribute dictionary is created. +On failure raise a Python exception and return NULL.

+
+ +
+
+int pgEvent_FillUserEvent(pgEventObject *e, SDL_Event *event)
+

Fill SDL event event with information from pygame user event instance e. +Return 0 on success, -1 otherwise.

+
+ +
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/c_api/freetype.html b/.venv/Lib/site-packages/pygame/docs/generated/c_api/freetype.html new file mode 100644 index 00000000..9f4eca8a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/c_api/freetype.html @@ -0,0 +1,178 @@ + + + + + + + + + API exported by pygame._freetype — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

src_c/_freetype.c

+

This extension module defines Python type pygame.freetype.FontCreate a new Font instance from a supported font file..

+

Header file: src_c/include/pygame_freetype.h

+
+
+type pgFontObject
+

The pygame.freetype.Font instance C struct.

+
+ +
+
+type pgFont_Type
+

The pygame.freetype.Font Python type.

+
+ +
+
+PyObject *pgFont_New(const char *filename, long font_index)
+

Open the font file with path filename and return a new +new pygame.freetype.Font instance for that font. +Set font_index to 0 unless the file contains multiple, indexed, fonts. +On error raise a Python exception and return NULL.

+
+ +
+
+int pgFont_Check(PyObject *x)
+

Return true if x is a pygame.freetype.Font instance. +Will return false for a subclass of Font. +This is a macro. No check is made that x is not NULL.

+
+ +
+
+int pgFont_IS_ALIVE(PyObject *o)
+

Return true if pygame.freetype.Font object o +is an open font file. +This is a macro. No check is made that o is not NULL +or not a Font instance.

+
+ +
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/c_api/mixer.html b/.venv/Lib/site-packages/pygame/docs/generated/c_api/mixer.html new file mode 100644 index 00000000..c7563c7b --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/c_api/mixer.html @@ -0,0 +1,211 @@ + + + + + + + + + API exported by pygame.mixer — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

src_c/mixer.c

+

Python types and module startup/shutdown functions defined in the +pygame.mixerpygame module for loading and playing sounds extension module.

+

Header file: src_c/include/pygame_mixer.h

+
+
+type pgSoundObject
+

The pygame.mixer.Sound instance C structure.

+
+ +
+
+PyTypeObject *pgSound_Type
+

The pygame.mixer.Sound Python type.

+
+ +
+
+PyObject *pgSound_New(Mix_Chunk *chunk)
+

Return a new pygame.mixer.Sound instance for the SDL mixer chunk chunk. +On failure, raise a Python exception and return NULL.

+
+ +
+
+int pgSound_Check(PyObject *obj)
+

Return true if obj is an instance of type pgSound_Type, +but not a pgSound_Type subclass instance. +A macro.

+
+ +
+
+Mix_Chunk *pgSound_AsChunk(PyObject *x)
+

Return the SDL Mix_Chunk struct associated with the +pgSound_Type instance x. +A macro that does no NULL or Python type check on x.

+
+ +
+
+type pgChannelObject
+

The pygame.mixer.Channel instance C structure.

+
+ +
+
+PyTypeObject *pgChannel_Type
+

The pygame.mixer.Channel Python type.

+
+ +
+
+PyObject *pgChannel_New(int channelnum)
+

Return a new pygame.mixer.Channel instance for the SDL mixer +channel channelnum. +On failure, raise a Python exception and return NULL.

+
+ +
+
+int pgChannel_Check(PyObject *obj)
+

Return true if obj is an instance of type pgChannel_Type, +but not a pgChannel_Type subclass instance. +A macro.

+
+ +
+
+int pgChannel_AsInt(PyObject *x)
+

Return the SDL mixer music channel number associated with pgChannel_Type instance x. +A macro that does no NULL or Python type check on x.

+
+ +
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/c_api/rect.html b/.venv/Lib/site-packages/pygame/docs/generated/c_api/rect.html new file mode 100644 index 00000000..f8040aa8 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/c_api/rect.html @@ -0,0 +1,206 @@ + + + + + + + + + Class Rect API exported by pygame.rect — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

src_c/rect.c

+

This extension module defines Python type pygame.Rectpygame object for storing rectangular coordinates.

+

Header file: src_c/include/pygame.h

+
+
+type pgRectObject
+
+
+SDL_Rect r
+
+ +

The Pygame rectangle type instance.

+
+ +
+
+PyTypeObject *pgRect_Type
+

The Pygame rectangle object type pygame.Rect.

+
+ +
+
+SDL_Rect pgRect_AsRect(PyObject *obj)
+

A macro to access the SDL_Rect field of a pygame.Rect instance.

+
+ +
+
+PyObject *pgRect_New(SDL_Rect *r)
+

Return a new pygame.Rect instance from the SDL_Rect r. +On failure, raise a Python exception and return NULL.

+
+ +
+
+PyObject *pgRect_New4(int x, int y, int w, int h)
+

Return a new pygame.Rect instance with position (x, y) and +size (w, h). +On failure raise a Python exception and return NULL.

+
+ +
+
+SDL_Rect *pgRect_FromObject(PyObject *obj, SDL_Rect *temp)
+

Translate a Python rectangle representation as a Pygame SDL_Rect. +A rectangle can be a length 4 sequence integers (x, y, w, h), +or a length 2 sequence of position (x, y) and size (w, h), +or a length 1 tuple containing a rectangle representation, +or have a method rect that returns a rectangle. +Pass a pointer to a locally declared SDL_Rect as temp. +Do not rely on this being filled in; use the function's return value instead. +On success, return a pointer to a SDL_Rect representation +of the rectangle, else return NULL. +No Python exceptions are raised.

+
+ +
+
+void pgRect_Normalize(SDL_Rect *rect)
+

Normalize the given rect. A rect with a negative size (negative width and/or +height) will be adjusted to have a positive size.

+
+ +
+
+int pgRect_Check(PyObject *obj)
+

A macro to check if obj is a pygame.Rect instance.

+
+ +
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/c_api/rwobject.html b/.venv/Lib/site-packages/pygame/docs/generated/c_api/rwobject.html new file mode 100644 index 00000000..3641eec7 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/c_api/rwobject.html @@ -0,0 +1,202 @@ + + + + + + + + + API exported by pygame.rwobject — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

src_c/rwobject.c

+

This extension module implements functions for wrapping a Python file like +object in a SDL_RWops struct for SDL file access.

+

Header file: src_c/include/pygame.h

+
+
+SDL_RWops *pgRWops_FromObject(PyObject *obj, char **extptr)
+

Return a SDL_RWops struct filled to access obj. +If obj is a string then let SDL open the file it names. +Otherwise, if obj is a Python file-like object then use its read, write, +seek, tell, and close methods. If threads are available, +the Python GIL is acquired before calling any of the obj methods. +If you want to see the file extension, you can pass in a char double pointer +that will be populated to a dynamically allocated string or NULL. Caller is +responsible for freeing the extension string. It is safe to pass NULL if you +don't care about the file extension. On error raise a Python exception and +return NULL. If NULL is returned, the extptr will not be populated with +dynamic memory, it is not necessary to free in that error handling.

+
+ +
+
+SDL_RWops *pgRWops_FromFileObject(PyObject *obj)
+

Return a SDL_RWops struct filled to access the Python file-like object obj. +Uses its read, write, seek, tell, and close methods. +If threads are available, the Python GIL is acquired before calling any of the obj methods. +On error raise a Python exception and return NULL.

+
+ +
+
+int pgRWops_IsFileObject(SDL_RWops *rw)
+

Return true if rw is a Python file-like object wrapper returned by pgRWops_FromObject() +or pgRWops_FromFileObject().

+
+ +
+
+int pgRWops_ReleaseObject(SDL_RWops *context)
+

Free a SDL_RWops struct. If it is attached to a Python file-like object, decrement its +refcount. Otherwise, close the file handle. +Return 0 on success. On error, raise a Python exception and return a negative value.

+
+ +
+
+PyObject *pg_EncodeFilePath(PyObject *obj, PyObject *eclass)
+

Return the file path obj as a byte string properly encoded for the OS. +Null bytes are forbidden in the encoded file path. +On error raise a Python exception and return NULL, +using eclass as the exception type if it is not NULL. +If obj is NULL assume an exception was already raised and pass it on.

+
+ +
+
+PyObject *pg_EncodeString(PyObject *obj, const char *encoding, const char *errors, PyObject *eclass)
+

Return string obj as an encoded byte string. +The C string arguments encoding and errors are the same as for +PyUnicode_AsEncodedString(). +On error raise a Python exception and return NULL, +using eclass as the exception type if it is not NULL. +If obj is NULL assume an exception was already raised and pass it on.

+
+ +
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/c_api/slots.html b/.venv/Lib/site-packages/pygame/docs/generated/c_api/slots.html new file mode 100644 index 00000000..8adfd1d8 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/c_api/slots.html @@ -0,0 +1,155 @@ + + + + + + + + + Slots and c_api - Making functions and data available from other modules — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+

One example is pg_RGBAFromObj where the implementation is defined in base.c, and also exported in base.c (and _pygame.h).

+

base.c has this exposing the pg_RGBAFromObj function to the c_api structure:

+
+

c_api[12] = pg_RGBAFromObj;

+
+

Then in src_c/include/_pygame.h there is an

+
+

#define pg_RGBAFromObj.

+
+

Also in _pygame.h, it needs to define the number of slots the base module uses. This is PYGAMEAPI_BASE_NUMSLOTS. So if you were adding another function, you need to increment this PYGAMEAPI_BASE_NUMSLOTS number.

+

Then to use the pg_RGBAFromObj in other files,

+
    +
  1. include the "pygame.h" file,

  2. +
  3. they have to make sure base is imported with:

    +
    +

    import_pygame_base();

    +
    +
  4. +
+

Examples that use pg_RGBAFromObj are: _freetype.c, color.c, gfxdraw.c, and surface.c.

+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/c_api/surface.html b/.venv/Lib/site-packages/pygame/docs/generated/c_api/surface.html new file mode 100644 index 00000000..3d0cf57f --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/c_api/surface.html @@ -0,0 +1,200 @@ + + + + + + + + + Class Surface API exported by pygame.surface — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

src_c/surface.c

+

This extension module defines Python type pygame.Surfacepygame object for representing images.

+

Header file: src_c/include/pygame.h

+
+
+type pgSurfaceObject
+

A pygame.Surface instance.

+
+ +
+
+PyTypeObject *pgSurface_Type
+

The pygame.Surface Python type.

+
+ +
+
+int pgSurface_Check(PyObject *x)
+

Return true if x is a pygame.Surface instance

+

Will return false if x is a subclass of Surface. +This is a macro. No check is made that x is not NULL.

+
+ +
+
+pgSurfaceObject *pgSurface_New(SDL_Surface *s)
+

Return a new new pygame surface instance for SDL surface s. +Return NULL on error.

+
+ +
+
+pgSurfaceObject *pgSurface_New2(SDL_Surface *s, int owner)
+

Return a new new pygame surface instance for SDL surface s. +If owner is true, the surface will be freed when the python object is destroyed. +Return NULL on error.

+
+ +
+
+SDL_Surface *pgSurface_AsSurface(PyObject *x)
+

Return a pointer the SDL surface represented by the pygame Surface instance +x.

+

This is a macro. Argument x is assumed to be a Surface, or subclass of +Surface, instance.

+
+ +
+
+int pgSurface_Blit(PyObject *dstobj, PyObject *srcobj, SDL_Rect *dstrect, SDL_Rect *srcrect, int the_args)
+

Blit the srcrect portion of Surface srcobj onto Surface dstobj at srcobj

+

Argument the_args indicates the type of blit to perform: +Normal blit (0), PYGAME_BLEND_ADD, PYGAME_BLEND_SUB, +PYGAME_BLEND_SUB, PYGAME_BLEND_MULT, PYGAME_BLEND_MIN, +PYGAME_BLEND_MAX, PYGAME_BLEND_RGBA_ADD, PYGAME_BLEND_RGBA_SUB, +PYGAME_BLEND_RGBA_MULT, PYGAME_BLEND_RGBA_MIN, +PYGAME_BLEND_RGBA_MAX, PYGAME_BLEND_ALPHA_SDL2 and PYGAME_BLEND_PREMULTIPLIED. +Argument dstrect is updated to the actual area on dstobj affected +by the blit.

+

The C version of the pygame.Surface.blit() method. +Return 0 on success, -1 or -2` on an exception.

+
+ +
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/c_api/surflock.html b/.venv/Lib/site-packages/pygame/docs/generated/c_api/surflock.html new file mode 100644 index 00000000..5d1df075 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/c_api/surflock.html @@ -0,0 +1,229 @@ + + + + + + + + + API exported by pygame.surflock — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

src_c/surflock.c

+

This extension module implements SDL surface locking for the +pygame.Surfacepygame object for representing images type.

+

Header file: src_c/include/pygame.h

+
+
+type pgLifetimeLockObject
+
+
+PyObject *surface
+

An SDL locked pygame surface.

+
+ +
+
+PyObject *lockobj
+

The Python object which owns the lock on the surface. +This field does not own a reference to the object.

+
+ +

The lifetime lock type instance. +A lifetime lock pairs a locked pygame surface with +the Python object that locked the surface for modification. +The lock is removed automatically when the lifetime lock instance +is garbage collected.

+
+ +
+
+PyTypeObject *pgLifetimeLock_Type
+

The pygame internal surflock lifetime lock object type.

+
+ +
+
+int pgLifetimeLock_Check(PyObject *x)
+

Return true if Python object x is a pgLifetimeLock_Type instance, +false otherwise. +This will return false on pgLifetimeLock_Type subclass instances as well.

+
+ +
+
+void pgSurface_Prep(pgSurfaceObject *surfobj)
+

If surfobj is a subsurface, then lock the parent surface with surfobj +the owner of the lock.

+
+ +
+
+void pgSurface_Unprep(pgSurfaceObject *surfobj)
+

If surfobj is a subsurface, then release its lock on the parent surface.

+
+ +
+
+int pgSurface_Lock(pgSurfaceObject *surfobj)
+

Lock pygame surface surfobj, with surfobj owning its own lock.

+
+ +
+
+int pgSurface_LockBy(pgSurfaceObject *surfobj, PyObject *lockobj)
+

Lock pygame surface surfobj with Python object lockobj the owning +the lock.

+

The surface will keep a weak reference to object lockobj, +and eventually remove the lock on itself if lockobj is garbage collected. +However, it is best if lockobj also keep a reference to the locked surface +and call to pgSurface_UnLockBy() when finished with the surface.

+
+ +
+
+int pgSurface_UnLock(pgSurfaceObject *surfobj)
+

Remove the pygame surface surfobj object's lock on itself.

+
+ +
+
+int pgSurface_UnLockBy(pgSurfaceObject *surfobj, PyObject *lockobj)
+

Remove the lock on pygame surface surfobj owned by Python object lockobj.

+
+ +
+
+PyObject *pgSurface_LockLifetime(PyObject *surfobj, PyObject *lockobj)
+

Lock pygame surface surfobj for Python object lockobj and return a +new pgLifetimeLock_Type instance for the lock.

+

This function is not called anywhere within pygame. +It and pgLifetimeLock_Type are candidates for removal.

+
+ +
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/c_api/version.html b/.venv/Lib/site-packages/pygame/docs/generated/c_api/version.html new file mode 100644 index 00000000..4f06daf1 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/c_api/version.html @@ -0,0 +1,171 @@ + + + + + + + + + API exported by pygame.version — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

src_py/version.py

+

Header file: src_c/include/pygame.h

+

Version information can be retrieved at compile-time using these macros.

+
+

New in pygame 1.9.5.

+
+
+
+PG_MAJOR_VERSION
+
+ +
+
+PG_MINOR_VERSION
+
+ +
+
+PG_PATCH_VERSION
+
+ +
+
+PG_VERSIONNUM(MAJOR, MINOR, PATCH)
+

Returns an integer representing the given version.

+
+ +
+
+PG_VERSION_ATLEAST(MAJOR, MINOR, PATCH)
+

Returns true if the current version is at least equal +to the specified version.

+
+ +
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/filepaths.html b/.venv/Lib/site-packages/pygame/docs/generated/filepaths.html new file mode 100644 index 00000000..d2309127 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/filepaths.html @@ -0,0 +1,147 @@ + + + + + + + + + File Path Function Arguments — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+

A pygame function or method which takes a file path argument will accept +either a Unicode or a byte (8-bit or ASCII character) string. +Unicode strings are translated to Python's default filesystem encoding, +as returned by sys.getfilesystemencoding(). A Unicode code point +above U+FFFF (\uFFFF) can be coded directly with a 32-bit escape sequences +(\Uxxxxxxxx), even for Python interpreters built with an UCS-2 +(16-bit character) Unicode type. Byte strings are passed +to the operating system unchanged.

+

Null characters (\x00) are not permitted in the path, raising an exception. +An exception is also raised if an Unicode file path cannot be encoded. +How UTF-16 surrogate codes are handled is Python-interpreter-dependent. +Use UTF-32 code points and 32-bit escape sequences instead. +The exception types are function-dependent.

+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/genindex.html b/.venv/Lib/site-packages/pygame/docs/generated/genindex.html new file mode 100644 index 00000000..0e34bcba --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/genindex.html @@ -0,0 +1,2678 @@ + + + + + + + + Index — pygame v2.5.2 documentation + + + + + + + + + + + + +
+
+
+ + +

Index

+ +
+ _ + | A + | B + | C + | D + | E + | F + | G + | H + | I + | J + | K + | L + | M + | N + | O + | P + | Q + | R + | S + | T + | U + | V + | W + +
+

_

+ + + +
+ +

A

+ + + +
+ +

B

+ + + +
+ +

C

+ + + +
+ +

D

+ + + +
+ +

E

+ + + +
+ +

F

+ + + +
+ +

G

+ + + +
+ +

H

+ + + +
+ +

I

+ + + +
+ +

J

+ + + +
+ +

K

+ + + +
+ +

L

+ + + +
+ +

M

+ + + +
+ +

N

+ + + +
+ +

O

+ + + +
+ +

P

+ + + +
+ +

Q

+ + +
+ +

R

+ + + +
+ +

S

+ + + +
+ +

T

+ + + +
+ +

U

+ + + +
+ +

V

+ + + +
+ +

W

+ + + +
+ + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/index.html b/.venv/Lib/site-packages/pygame/docs/generated/index.html new file mode 100644 index 00000000..4e0b1736 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/index.html @@ -0,0 +1,345 @@ + + + + + + + + + Pygame Front Page — pygame v2.5.2 documentation + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+

Pygame Front Page

+
+
+
+

Quick start

+

Welcome to pygame! Once you've got pygame installed (pip install pygame or +pip3 install pygame for most people), the next question is how to get a game +loop running. Pygame, unlike some other libraries, gives you full control of program +execution. That freedom means it is easy to mess up in your initial steps.

+

Here is a good example of a basic setup (opens the window, updates the screen, and handles events)--

+
# Example file showing a basic pygame "game loop"
+import pygame
+
+# pygame setup
+pygame.init()
+screen = pygame.display.set_mode((1280, 720))
+clock = pygame.time.Clock()
+running = True
+
+while running:
+    # poll for events
+    # pygame.QUIT event means the user clicked X to close your window
+    for event in pygame.event.get():
+        if event.type == pygame.QUIT:
+            running = False
+
+    # fill the screen with a color to wipe away anything from last frame
+    screen.fill("purple")
+
+    # RENDER YOUR GAME HERE
+
+    # flip() the display to put your work on screen
+    pygame.display.flip()
+
+    clock.tick(60)  # limits FPS to 60
+
+pygame.quit()
+
+
+

Here is a slightly more fleshed out example, which shows you how to move something +(a circle in this case) around on screen--

+
# Example file showing a circle moving on screen
+import pygame
+
+# pygame setup
+pygame.init()
+screen = pygame.display.set_mode((1280, 720))
+clock = pygame.time.Clock()
+running = True
+dt = 0
+
+player_pos = pygame.Vector2(screen.get_width() / 2, screen.get_height() / 2)
+
+while running:
+    # poll for events
+    # pygame.QUIT event means the user clicked X to close your window
+    for event in pygame.event.get():
+        if event.type == pygame.QUIT:
+            running = False
+
+    # fill the screen with a color to wipe away anything from last frame
+    screen.fill("purple")
+
+    pygame.draw.circle(screen, "red", player_pos, 40)
+
+    keys = pygame.key.get_pressed()
+    if keys[pygame.K_w]:
+        player_pos.y -= 300 * dt
+    if keys[pygame.K_s]:
+        player_pos.y += 300 * dt
+    if keys[pygame.K_a]:
+        player_pos.x -= 300 * dt
+    if keys[pygame.K_d]:
+        player_pos.x += 300 * dt
+
+    # flip() the display to put your work on screen
+    pygame.display.flip()
+
+    # limits FPS to 60
+    # dt is delta time in seconds since last frame, used for framerate-
+    # independent physics.
+    dt = clock.tick(60) / 1000
+
+pygame.quit()
+
+
+

For more in depth reference, check out the Tutorials +section below, check out a video tutorial (I'm a fan of this one), or reference the API +documentation by module.

+
+
+

Documents

+
+
Readme

Basic information about pygame: what it is, who is involved, and where to find it.

+
+
Install

Steps needed to compile pygame on several platforms. +Also help on finding and installing prebuilt binaries for your system.

+
+
File Path Function Arguments

How pygame handles file system paths.

+
+
Pygame Logos

The logos of Pygame in different resolutions.

+
+
LGPL License

This is the license pygame is distributed under. +It provides for pygame to be distributed with open source and commercial software. +Generally, if pygame is not changed, it can be used with any type of program.

+
+
+
+
+

Tutorials

+
+
Introduction to Pygame

An introduction to the basics of pygame. +This is written for users of Python and appeared in volume two of the Py magazine.

+
+
Import and Initialize

The beginning steps on importing and initializing pygame. +The pygame package is made of several modules. +Some modules are not included on all platforms.

+
+
How do I move an Image?

A basic tutorial that covers the concepts behind 2D computer animation. +Information about drawing and clearing objects to make them appear animated.

+
+
Chimp Tutorial, Line by Line

The pygame examples include a simple program with an interactive fist and a chimpanzee. +This was inspired by the annoying flash banner of the early 2000s. +This tutorial examines every line of code used in the example.

+
+
Sprite Module Introduction

Pygame includes a higher level sprite module to help organize games. +The sprite module includes several classes that help manage details found in almost all games types. +The Sprite classes are a bit more advanced than the regular pygame modules, +and need more understanding to be properly used.

+
+
Surfarray Introduction

Pygame used the NumPy python module to allow efficient per pixel effects on images. +Using the surface arrays is an advanced feature that allows custom effects and filters. +This also examines some of the simple effects from the pygame example, arraydemo.py.

+
+
Camera Module Introduction

Pygame, as of 1.9, has a camera module that allows you to capture images, +watch live streams, and do some basic computer vision. +This tutorial covers those use cases.

+
+
Newbie Guide

A list of thirteen helpful tips for people to get comfortable using pygame.

+
+
Making Games Tutorial

A large tutorial that covers the bigger topics needed to create an entire game.

+
+
Display Modes

Getting a display surface for the screen.

+
+
한국어 튜토리얼 (Korean Tutorial)

빨간블록 검은블록

+
+
+
+
+

Reference

+
+
Index

A list of all functions, classes, and methods in the pygame package.

+
+
pygame.BufferProxy

An array protocol view of surface pixels

+
+
pygame.Color

Color representation.

+
+
pygame.cursors

Loading and compiling cursor images.

+
+
pygame.display

Configure the display surface.

+
+
pygame.draw

Drawing simple shapes like lines and ellipses to surfaces.

+
+
pygame.event

Manage the incoming events from various input devices and the windowing platform.

+
+
pygame.examples

Various programs demonstrating the use of individual pygame modules.

+
+
pygame.font

Loading and rendering TrueType fonts.

+
+
pygame.freetype

Enhanced pygame module for loading and rendering font faces.

+
+
pygame.gfxdraw

Anti-aliasing draw functions.

+
+
pygame.image

Loading, saving, and transferring of surfaces.

+
+
pygame.joystick

Manage the joystick devices.

+
+
pygame.key

Manage the keyboard device.

+
+
pygame.locals

Pygame constants.

+
+
pygame.mixer

Load and play sounds

+
+
pygame.mouse

Manage the mouse device and display.

+
+
pygame.mixer.music

Play streaming music tracks.

+
+
pygame

Top level functions to manage pygame.

+
+
pygame.PixelArray

Manipulate image pixel data.

+
+
pygame.Rect

Flexible container for a rectangle.

+
+
pygame.scrap

Native clipboard access.

+
+
pygame.sndarray

Manipulate sound sample data.

+
+
pygame.sprite

Higher level objects to represent game images.

+
+
pygame.Surface

Objects for images and the screen.

+
+
pygame.surfarray

Manipulate image pixel data.

+
+
pygame.tests

Test pygame.

+
+
pygame.time

Manage timing and framerate.

+
+
pygame.transform

Resize and move images.

+
+
pygame C API

The C api shared amongst pygame extension modules.

+
+
Search Page

Search pygame documents by keyword.

+
+
+
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/logos.html b/.venv/Lib/site-packages/pygame/docs/generated/logos.html new file mode 100644 index 00000000..5aa6d891 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/logos.html @@ -0,0 +1,170 @@ + + + + + + + + + Pygame Logos Page — pygame v2.5.2 documentation + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

Pygame Logos

+

These logos are available for use in your own game projects. +Please put them up wherever you see fit. The logo was created +by TheCorruptor on July 29, 2001 and upscaled by Mega_JC on +August 29, 2021.

+
+_images/pygame_logo.png + +_images/pygame_lofi.png + +_images/pygame_powered.png + +_images/pygame_tiny.png +
+
pygame_tiny.png - 214 x 60
+
+_images/pygame_powered_lowres.png + +
+

There is a higher resolution layered photoshop image +available here. (1.3 MB)

+
+

Legacy logos

+
+

legacy_logos.zip - 50.1 KB

+
+
+
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/py-modindex.html b/.venv/Lib/site-packages/pygame/docs/generated/py-modindex.html new file mode 100644 index 00000000..0829d95e --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/py-modindex.html @@ -0,0 +1,251 @@ + + + + + + + + Python Module Index — pygame v2.5.2 documentation + + + + + + + + + + + + + + + +
+
+
+ + +

Python Module Index

+ +
+ . | + p +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ .
    + pygame._sdl2.controller + pygame module to work with controllers
    + pygame._sdl2.touch + pygame module to work with touch input
    + pygame._sdl2.video + Experimental pygame module for porting new SDL video systems
    + pygame.camera + pygame module for camera use
    + pygame.cdrom + pygame module for audio cdrom control
    + pygame.cursors + pygame module for cursor resources
    + pygame.display + pygame module to control the display window and screen
    + pygame.draw + pygame module for drawing shapes
    + pygame.event + pygame module for interacting with events and queues
    + pygame.examples + module of example programs
    + pygame.fastevent + pygame module for interacting with events and queues from multiple +threads.
    + pygame.font + pygame module for loading and rendering fonts
    + pygame.freetype + Enhanced pygame module for loading and rendering computer fonts
    + pygame.gfxdraw + pygame module for drawing shapes
    + pygame.image + pygame module for loading and saving images
    + pygame.joystick + Pygame module for interacting with joysticks, gamepads, and trackballs.
    + pygame.key + pygame module to work with the keyboard
    + pygame.locals + pygame constants
    + pygame.mask + pygame module for image masks.
    + pygame.math + pygame module for vector classes
    + pygame.midi + pygame module for interacting with midi input and output.
    + pygame.mixer + pygame module for loading and playing sounds
    + pygame.mixer.music + pygame module for controlling streamed audio
    + pygame.mouse + pygame module to work with the mouse
    + pygame.pixelcopy + pygame module for general pixel array copying
    + pygame.scrap + pygame module for clipboard support.
    + pygame.sndarray + pygame module for accessing sound sample data
    + pygame.sprite + pygame module with basic game object classes
    + pygame.surfarray + pygame module for accessing surface pixel data using array interfaces
    + pygame.tests + Pygame unit test suite package
    + pygame.time + pygame module for monitoring time
    + pygame.transform + pygame module to transform surfaces
    + pygame.version + small module containing version information
 
+ p
+ pygame + the top level pygame package
+ + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/bufferproxy.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/bufferproxy.html new file mode 100644 index 00000000..aafaa95d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/bufferproxy.html @@ -0,0 +1,281 @@ + + + + + + + + + pygame.BufferProxy — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.BufferProxy
+
+
pygame object to export a surface buffer through an array protocol
+
BufferProxy(<parent>) -> BufferProxy
+
+ +++++ + + + + + + + + + + + + + + + + + + +
+Return wrapped exporting object.
+The size, in bytes, of the exported buffer.
+A copy of the exported buffer as a single block of bytes.
+Write raw bytes to object buffer.
+

BufferProxy is a pygame support type, designed as the return value +of the Surface.get_buffer() and Surface.get_view() methods. +For all Python versions a BufferProxy object exports a C struct +and Python level array interface on behalf of its parent object's buffer. +A new buffer interface is also exported. +In pygame, BufferProxy is key to implementing the +pygame.surfarraypygame module for accessing surface pixel data using array interfaces module.

+

BufferProxy instances can be created directly from Python code, +either for a parent that exports an interface, or from a Python dict +describing an object's buffer layout. The dict entries are based on the +Python level array interface mapping. The following keys are recognized:

+
+
+
"shape"tuple

The length of each array dimension as a tuple of integers. The +length of the tuple is the number of dimensions in the array.

+
+
"typestr"string

The array element type as a length 3 string. The first character +gives byteorder, '<' for little-endian, '>' for big-endian, and +'|' for not applicable. The second character is the element type, +'i' for signed integer, 'u' for unsigned integer, 'f' for floating +point, and 'V' for an chunk of bytes. The third character gives the +bytesize of the element, from '1' to '9' bytes. So, for example, +"<u4" is an unsigned 4 byte little-endian integer, such as a +32 bit pixel on a PC, while "|V3" would represent a 24 bit pixel, +which has no integer equivalent.

+
+
"data"tuple

The physical buffer start address and a read-only flag as a length +2 tuple. The address is an integer value, while the read-only flag +is a bool—False for writable, True for read-only.

+
+
"strides"tuple(optional)

Array stride information as a tuple of integers. It is required +only of non C-contiguous arrays. The tuple length must match +that of "shape".

+
+
"parent"object(optional)

The exporting object. It can be used to keep the parent object +alive while its buffer is visible.

+
+
"before"callable(optional)

Callback invoked when the BufferProxy instance +exports the buffer. The callback is given one argument, the +"parent" object if given, otherwise None. +The callback is useful for setting a lock on the parent.

+
+
"after"callable(optional)

Callback invoked when an exported buffer is released. +The callback is passed on argument, the "parent" object if given, +otherwise None. The callback is useful for releasing a lock on the +parent.

+
+
+
+

The BufferProxy class supports subclassing, instance variables, and weak +references.

+
+

New in pygame 1.8.0.

+
+
+

Extended in pygame 1.9.2.

+
+
+
+parent
+
+
Return wrapped exporting object.
+
parent -> Surface
+
parent -> <parent>
+
+

The Surface which returned the BufferProxy object or +the object passed to a BufferProxy call.

+
+ +
+
+length
+
+
The size, in bytes, of the exported buffer.
+
length -> int
+
+

The number of valid bytes of data exported. For discontinuous data, +that is data which is not a single block of memory, the bytes within +the gaps are excluded from the count. This property is equivalent to +the Py_buffer C struct len field.

+
+ +
+
+raw
+
+
A copy of the exported buffer as a single block of bytes.
+
raw -> bytes
+
+

The buffer data as a str/bytes object. +Any gaps in the exported data are removed.

+
+ +
+
+write()
+
+
Write raw bytes to object buffer.
+
write(buffer, offset=0)
+
+

Overwrite bytes in the parent object's data. The data must be C or F +contiguous, otherwise a ValueError is raised. Argument buffer is a +str/bytes object. An optional offset gives a +start position, in bytes, within the buffer where overwriting begins. +If the offset is negative or greater that or equal to the buffer proxy's +length value, an IndexException is raised. +If len(buffer) > proxy.length + offset, a ValueError is raised.

+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/camera.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/camera.html new file mode 100644 index 00000000..5f153ed7 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/camera.html @@ -0,0 +1,474 @@ + + + + + + + + + pygame.camera — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.camera
+
+
pygame module for camera use
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + +
+Module init
+Get the backends supported on this system
+Surface colorspace conversion
+returns a list of available cameras
+load a camera
+
+

Note

+

Use import pygame.camera before using this module.

+
+

Pygame currently supports Linux (V4L2) and Windows (MSMF) cameras natively, +with wider platform support available via an integrated OpenCV backend.

+
+

New in pygame 2.0.2: Windows native camera support

+
+
+

New in pygame 2.0.3: New OpenCV backends

+
+

EXPERIMENTAL!: This API may change or disappear in later pygame releases. If +you use this, your code will very likely break with the next pygame release.

+

The Bayer to RGB function is based on:

+
Sonix SN9C101 based webcam basic I/F routines
+Copyright (C) 2004 Takafumi Mizuno <taka-qce@ls-a.jp>
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+
+

New in pygame 1.9.0.

+
+
+pygame.camera.init()
+
+
Module init
+
init(backend = None) -> None
+
+

This function starts up the camera module, choosing the best webcam backend +it can find for your system. This is not guaranteed to succeed, and may even +attempt to import third party modules, like OpenCV. If you want to +override its backend choice, you can call pass the name of the backend you +want into this function. More about backends in +get_backends().

+
+

Changed in pygame 2.0.3: Option to explicitly select backend

+
+
+ +
+
+pygame.camera.get_backends()
+
+
Get the backends supported on this system
+
get_backends() -> [str]
+
+

This function returns every backend it thinks has a possibility of working +on your system, in order of priority.

+

pygame.camera Backends:

+
Backend           OS        Description
+---------------------------------------------------------------------------------
+_camera (MSMF)    Windows   Builtin, works on Windows 8+ Python3
+_camera (V4L2)    Linux     Builtin
+OpenCV            Any       Uses `opencv-python` module, can't enumerate cameras
+OpenCV-Mac        Mac       Same as OpenCV, but has camera enumeration
+VideoCapture      Windows   Uses abandoned `VideoCapture` module, can't enumerate
+                            cameras, may be removed in the future
+
+
+

There are two main differences among backends.

+

The _camera backends are built in to pygame itself, and require no third +party imports. All the other backends do. For the OpenCV and VideoCapture +backends, those modules need to be installed on your system.

+

The other big difference is "camera enumeration." Some backends don't have +a way to list out camera names, or even the number of cameras on the +system. In these cases, list_cameras() will return +something like [0]. If you know you have multiple cameras on the +system, these backend ports will pass through a "camera index number" +through if you use that as the device parameter.

+
+

New in pygame 2.0.3.

+
+
+ +
+
+pygame.camera.colorspace()
+
+
Surface colorspace conversion
+
colorspace(Surface, format, DestSurface = None) -> Surface
+
+

Allows for conversion from "RGB" to a destination colorspace of "HSV" or +"YUV". The source and destination surfaces must be the same size and pixel +depth. This is useful for computer vision on devices with limited processing +power. Capture as small of an image as possible, transform.scale() it +even smaller, and then convert the colorspace to YUV or HSV before +doing any processing on it.

+
+ +
+
+pygame.camera.list_cameras()
+
+
returns a list of available cameras
+
list_cameras() -> [cameras]
+
+

Checks the computer for available cameras and returns a list of strings of +camera names, ready to be fed into pygame.camera.Cameraload a camera.

+

If the camera backend doesn't support webcam enumeration, this will return +something like [0]. See get_backends() for much more +information.

+
+ +
+
+pygame.camera.Camera
+
+
load a camera
+
Camera(device, (width, height), format) -> Camera
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+opens, initializes, and starts capturing
+stops, uninitializes, and closes the camera
+gets current values of user controls
+changes camera settings if supported by the camera
+returns the dimensions of the images being recorded
+checks if a frame is ready
+captures an image as a Surface
+returns an unmodified image as bytes
+

Loads a camera. On Linux, the device is typically something like +"/dev/video0". Default width and height are 640 by 480. +Format is the desired colorspace of the output. +This is useful for computer vision purposes. The default is +RGB. The following are supported:

+
+
    +
  • RGB - Red, Green, Blue

  • +
  • YUV - Luma, Blue Chrominance, Red Chrominance

  • +
  • HSV - Hue, Saturation, Value

  • +
+
+
+
+start()
+
+
opens, initializes, and starts capturing
+
start() -> None
+
+

Opens the camera device, attempts to initialize it, and begins recording +images to a buffer. The camera must be started before any of the below +functions can be used.

+
+ +
+
+stop()
+
+
stops, uninitializes, and closes the camera
+
stop() -> None
+
+

Stops recording, uninitializes the camera, and closes it. Once a camera +is stopped, the below functions cannot be used until it is started again.

+
+ +
+
+get_controls()
+
+
gets current values of user controls
+
get_controls() -> (hflip = bool, vflip = bool, brightness)
+
+

If the camera supports it, get_controls will return the current settings +for horizontal and vertical image flip as bools and brightness as an int. +If unsupported, it will return the default values of (0, 0, 0). Note that +the return values here may be different than those returned by +set_controls, though these are more likely to be correct.

+
+ +
+
+set_controls()
+
+
changes camera settings if supported by the camera
+
set_controls(hflip = bool, vflip = bool, brightness) -> (hflip = bool, vflip = bool, brightness)
+
+

Allows you to change camera settings if the camera supports it. The +return values will be the input values if the camera claims it succeeded +or the values previously in use if not. Each argument is optional, and +the desired one can be chosen by supplying the keyword, like hflip. Note +that the actual settings being used by the camera may not be the same as +those returned by set_controls. On Windows, hflip and vflip are +implemented by pygame, not by the Camera, so they should always work, but +brightness is unsupported.

+
+ +
+
+get_size()
+
+
returns the dimensions of the images being recorded
+
get_size() -> (width, height)
+
+

Returns the current dimensions of the images being captured by the +camera. This will return the actual size, which may be different than the +one specified during initialization if the camera did not support that +size.

+
+ +
+
+query_image()
+
+
checks if a frame is ready
+
query_image() -> bool
+
+

If an image is ready to get, it returns true. Otherwise it returns false. +Note that some webcams will always return False and will only queue a +frame when called with a blocking function like get_image(). +On Windows (MSMF), and the OpenCV backends, query_image() +should be reliable, though. This is useful to separate the framerate of +the game from that of the camera without having to use threading.

+
+ +
+
+get_image()
+
+
captures an image as a Surface
+
get_image(Surface = None) -> Surface
+
+

Pulls an image off of the buffer as an RGB Surface. It can optionally +reuse an existing Surface to save time. The bit-depth of the surface is +24 bits on Linux, 32 bits on Windows, or the same as the optionally +supplied Surface.

+
+ +
+
+get_raw()
+
+
returns an unmodified image as bytes
+
get_raw() -> bytes
+
+

Gets an image from a camera as a string in the native pixelformat of the +camera. Useful for integration with other libraries. This returns a +bytes object

+
+ +
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/cdrom.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/cdrom.html new file mode 100644 index 00000000..9a7ad5ca --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/cdrom.html @@ -0,0 +1,590 @@ + + + + + + + + + pygame.cdrom — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.cdrom
+
+
pygame module for audio cdrom control
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + +
+initialize the cdrom module
+uninitialize the cdrom module
+true if the cdrom module is initialized
+number of cd drives on the system
+class to manage a cdrom drive
+
+

Warning

+

This module is non functional in pygame 2.0 and above, unless you have manually compiled pygame with SDL1. +This module will not be supported in the future. +One alternative for python cdrom functionality is pycdio.

+
+

The cdrom module manages the CD and DVD drives on a computer. It can +also control the playback of audio CDs. This module needs to be initialized +before it can do anything. Each CD object you create represents a cdrom +drive and must also be initialized individually before it can do most things.

+
+
+pygame.cdrom.init()
+
+
initialize the cdrom module
+
init() -> None
+
+

Initialize the cdrom module. This will scan the system for all CD +devices. The module must be initialized before any other functions will +work. This automatically happens when you call pygame.init().

+

It is safe to call this function more than once.

+
+ +
+
+pygame.cdrom.quit()
+
+
uninitialize the cdrom module
+
quit() -> None
+
+

Uninitialize the cdrom module. After you call this any existing CD +objects will no longer work.

+

It is safe to call this function more than once.

+
+ +
+
+pygame.cdrom.get_init()
+
+
true if the cdrom module is initialized
+
get_init() -> bool
+
+

Test if the cdrom module is initialized or not. This is different than the +CD.init() since each drive must also be initialized individually.

+
+ +
+
+pygame.cdrom.get_count()
+
+
number of cd drives on the system
+
get_count() -> count
+
+

Return the number of cd drives on the system. When you create CD objects +you need to pass an integer id that must be lower than this count. The count +will be 0 if there are no drives on the system.

+
+ +
+
+pygame.cdrom.CD
+
+
class to manage a cdrom drive
+
CD(id) -> CD
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+initialize a cdrom drive for use
+uninitialize a cdrom drive for use
+true if this cd device initialized
+start playing audio
+stop audio playback
+temporarily stop audio playback
+unpause audio playback
+eject or open the cdrom drive
+the index of the cdrom drive
+the system name of the cdrom drive
+true if the drive is playing audio
+true if the drive is paused
+the current audio playback position
+False if a cdrom is in the drive
+the number of tracks on the cdrom
+true if the cdrom track has audio data
+get all track information
+start time of a cdrom track
+length of a cdrom track
+

You can create a CD object for each cdrom on the system. Use +pygame.cdrom.get_count() to determine how many drives actually exist. +The id argument is an integer of the drive, starting at zero.

+

The CD object is not initialized, you can only call CD.get_id() and +CD.get_name() on an uninitialized drive.

+

It is safe to create multiple CD objects for the same drive, they will +all cooperate normally.

+
+
+init()
+
+
initialize a cdrom drive for use
+
init() -> None
+
+

Initialize the cdrom drive for use. The drive must be initialized for +most CD methods to work. Even if the rest of pygame has been +initialized.

+

There may be a brief pause while the drive is initialized. Avoid +CD.init() if the program should not stop for a second or two.

+
+ +
+
+quit()
+
+
uninitialize a cdrom drive for use
+
quit() -> None
+
+

Uninitialize a drive for use. Call this when your program will not be +accessing the drive for awhile.

+
+ +
+
+get_init()
+
+
true if this cd device initialized
+
get_init() -> bool
+
+

Test if this CDROM device is initialized. This is different than the +pygame.cdrom.init() since each drive must also be initialized +individually.

+
+ +
+
+play()
+
+
start playing audio
+
play(track, start=None, end=None) -> None
+
+

Playback audio from an audio cdrom in the drive. Besides the track number +argument, you can also pass a starting and ending time for playback. The +start and end time are in seconds, and can limit the section of an audio +track played.

+

If you pass a start time but no end, the audio will play to the end of +the track. If you pass a start time and 'None' for the end time, the +audio will play to the end of the entire disc.

+

See the CD.get_numtracks() and CD.get_track_audio() to find +tracks to playback.

+

Note, track 0 is the first track on the CD. Track numbers start at +zero.

+
+ +
+
+stop()
+
+
stop audio playback
+
stop() -> None
+
+

Stops playback of audio from the cdrom. This will also lose the current +playback position. This method does nothing if the drive isn't already +playing audio.

+
+ +
+
+pause()
+
+
temporarily stop audio playback
+
pause() -> None
+
+

Temporarily stop audio playback on the CD. The playback can be +resumed at the same point with the CD.resume() method. If the CD +is not playing this method does nothing.

+

Note, track 0 is the first track on the CD. Track numbers start at +zero.

+
+ +
+
+resume()
+
+
unpause audio playback
+
resume() -> None
+
+

Unpause a paused CD. If the CD is not paused or already playing, +this method does nothing.

+
+ +
+
+eject()
+
+
eject or open the cdrom drive
+
eject() -> None
+
+

This will open the cdrom drive and eject the cdrom. If the drive is +playing or paused it will be stopped.

+
+ +
+
+get_id()
+
+
the index of the cdrom drive
+
get_id() -> id
+
+

Returns the integer id that was used to create the CD instance. This +method can work on an uninitialized CD.

+
+ +
+
+get_name()
+
+
the system name of the cdrom drive
+
get_name() -> name
+
+

Return the string name of the drive. This is the system name used to +represent the drive. It is often the drive letter or device name. This +method can work on an uninitialized CD.

+
+ +
+
+get_busy()
+
+
true if the drive is playing audio
+
get_busy() -> bool
+
+

Returns True if the drive busy playing back audio.

+
+ +
+
+get_paused()
+
+
true if the drive is paused
+
get_paused() -> bool
+
+

Returns True if the drive is currently paused.

+
+ +
+
+get_current()
+
+
the current audio playback position
+
get_current() -> track, seconds
+
+

Returns both the current track and time of that track. This method works +when the drive is either playing or paused.

+

Note, track 0 is the first track on the CD. Track numbers start at +zero.

+
+ +
+
+get_empty()
+
+
False if a cdrom is in the drive
+
get_empty() -> bool
+
+

Return False if there is a cdrom currently in the drive. If the drive is +empty this will return True.

+
+ +
+
+get_numtracks()
+
+
the number of tracks on the cdrom
+
get_numtracks() -> count
+
+

Return the number of tracks on the cdrom in the drive. This will return +zero of the drive is empty or has no tracks.

+
+ +
+
+get_track_audio()
+
+
true if the cdrom track has audio data
+
get_track_audio(track) -> bool
+
+

Determine if a track on a cdrom contains audio data. You can also call +CD.num_tracks() and CD.get_all() to determine more information +about the cdrom.

+

Note, track 0 is the first track on the CD. Track numbers start at +zero.

+
+ +
+
+get_all()
+
+
get all track information
+
get_all() -> [(audio, start, end, length), ...]
+
+

Return a list with information for every track on the cdrom. The +information consists of a tuple with four values. The audio value is True +if the track contains audio data. The start, end, and length values are +floating point numbers in seconds. Start and end represent absolute times +on the entire disc.

+
+ +
+
+get_track_start()
+
+
start time of a cdrom track
+
get_track_start(track) -> seconds
+
+

Return the absolute time in seconds where at start of the cdrom track.

+

Note, track 0 is the first track on the CD. Track numbers start at +zero.

+
+ +
+
+get_track_length()
+
+
length of a cdrom track
+
get_track_length(track) -> seconds
+
+

Return a floating point value in seconds of the length of the cdrom +track.

+

Note, track 0 is the first track on the CD. Track numbers start at +zero.

+
+ +
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/color.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/color.html new file mode 100644 index 00000000..1f8d05ca --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/color.html @@ -0,0 +1,530 @@ + + + + + + + + + pygame.Color — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.Color
+
+
pygame object for color representations
+
Color(r, g, b) -> Color
+
Color(r, g, b, a=255) -> Color
+
Color(color_value) -> Color
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Gets or sets the red value of the Color.
+Gets or sets the green value of the Color.
+Gets or sets the blue value of the Color.
+Gets or sets the alpha value of the Color.
+Gets or sets the CMY representation of the Color.
+Gets or sets the HSVA representation of the Color.
+Gets or sets the HSLA representation of the Color.
+Gets or sets the I1I2I3 representation of the Color.
+Returns the normalized RGBA values of the Color.
+Applies a certain gamma value to the Color.
+Set the number of elements in the Color to 1,2,3, or 4.
+returns the grayscale of a Color
+returns a linear interpolation to the given Color.
+returns a Color where the r,g,b components have been multiplied by the alpha.
+Sets the elements of the color
+

The Color class represents RGBA color values using a value range of +0 to 255 inclusive. It allows basic arithmetic operations — binary +operations +, -, *, //, %, and unary operation ~ — to +create new colors, supports conversions to other color spaces such as HSV +or HSL and lets you adjust single color channels. +Alpha defaults to 255 (fully opaque) when not given. +The arithmetic operations and correct_gamma() method preserve subclasses. +For the binary operators, the class of the returned color is that of the +left hand color object of the operator.

+

Color objects support equality comparison with other color objects and 3 or +4 element tuples of integers. There was a bug in pygame 1.8.1 +where the default alpha was 0, not 255 like previously.

+

Color objects export the C level array interface. The interface exports a +read-only one dimensional unsigned byte array of the same assigned length +as the color. The new buffer interface is also exported, with the same +characteristics as the array interface.

+

The floor division, //, and modulus, %, operators do not raise +an exception for division by zero. Instead, if a color, or alpha, channel +in the right hand color is 0, then the result is 0. For example:

+
# These expressions are True
+Color(255, 255, 255, 255) // Color(0, 64, 64, 64) == Color(0, 3, 3, 3)
+Color(255, 255, 255, 255) % Color(64, 64, 64, 0) == Color(63, 63, 63, 0)
+
+
+

Use int(color) to return the immutable integer value of the color, +usable as a dict key. This integer value differs from the mapped +pixel values of pygame.Surface.get_at_mapped()get the mapped color value at a single pixel, +pygame.Surface.map_rgb()convert a color into a mapped color value and pygame.Surface.unmap_rgb()convert a mapped integer color value into a Color. +It can be passed as a color_value argument to Color +(useful with sets).

+

See Named Colors for samples of the available named colors.

+
+
Parameters
+
    +
  • r (int) -- red value in the range of 0 to 255 inclusive

  • +
  • g (int) -- green value in the range of 0 to 255 inclusive

  • +
  • b (int) -- blue value in the range of 0 to 255 inclusive

  • +
  • a (int) -- (optional) alpha value in the range of 0 to 255 inclusive, +default is 255

  • +
  • color_value (Color or str or int or tuple(int, int, int, [int]) or +list(int, int, int, [int])) --

    color value (see note below for the supported formats)

    +
    +

    Note

    +
    +
    Supported color_value formats:
    +
    - Color object: clones the given Color object
    +
    - Color name: str: name of the color to use, e.g. 'red' +(all the supported name strings can be found in the + Named Colors, with sample swatches)
    +
    - HTML color format str: '#rrggbbaa' or '#rrggbb', +where rr, gg, bb, and aa are 2-digit hex numbers in the range +of 0 to 0xFF inclusive, the aa (alpha) value defaults to 0xFF +if not provided
    +
    - hex number str: '0xrrggbbaa' or '0xrrggbb', where +rr, gg, bb, and aa are 2-digit hex numbers in the range of 0x00 +to 0xFF inclusive, the aa (alpha) value defaults to 0xFF if not +provided
    +
    - int: int value of the color to use, using hex numbers can +make this parameter more readable, e.g. 0xrrggbbaa, where rr, +gg, bb, and aa are 2-digit hex numbers in the range of 0x00 to +0xFF inclusive, note that the aa (alpha) value is not optional for +the int format and must be provided
    +
    - tuple/list of int color values: (R, G, B, A) or +(R, G, B), where R, G, B, and A are int values in the range of +0 to 255 inclusive, the A (alpha) value defaults to 255 if not +provided
    +
    +
    +
    +
    +

  • +
+
+
Returns
+

a newly created Color object

+
+
Return type
+

Color

+
+
+
+

Changed in pygame 2.0.0: Support for tuples, lists, and Color objects when creating +Color objects.

+
+
+

Changed in pygame 1.9.2: Color objects export the C level array interface.

+
+
+

Changed in pygame 1.9.0: Color objects support 4-element tuples of integers.

+
+
+

Changed in pygame 1.8.1: New implementation of the class.

+
+
+
+r
+
+
Gets or sets the red value of the Color.
+
r -> int
+
+

The red value of the Color.

+
+ +
+
+g
+
+
Gets or sets the green value of the Color.
+
g -> int
+
+

The green value of the Color.

+
+ +
+
+b
+
+
Gets or sets the blue value of the Color.
+
b -> int
+
+

The blue value of the Color.

+
+ +
+
+a
+
+
Gets or sets the alpha value of the Color.
+
a -> int
+
+

The alpha value of the Color.

+
+ +
+
+cmy
+
+
Gets or sets the CMY representation of the Color.
+
cmy -> tuple
+
+

The CMY representation of the Color. The CMY components are in +the ranges C = [0, 1], M = [0, 1], Y = [0, 1]. Note that this +will not return the absolutely exact CMY values for the set RGB +values in all cases. Due to the RGB mapping from 0-255 and the +CMY mapping from 0-1 rounding errors may cause the CMY values to +differ slightly from what you might expect.

+
+ +
+
+hsva
+
+
Gets or sets the HSVA representation of the Color.
+
hsva -> tuple
+
+

The HSVA representation of the Color. The HSVA components are in +the ranges H = [0, 360], S = [0, 100], V = [0, 100], A = [0, +100]. Note that this will not return the absolutely exact HSV values +for the set RGB values in all cases. Due to the RGB mapping from +0-255 and the HSV mapping from 0-100 and 0-360 rounding errors may +cause the HSV values to differ slightly from what you might expect.

+
+ +
+
+hsla
+
+
Gets or sets the HSLA representation of the Color.
+
hsla -> tuple
+
+

The HSLA representation of the Color. The HSLA components are in +the ranges H = [0, 360], S = [0, 100], L = [0, 100], A = [0, +100]. Note that this will not return the absolutely exact HSL values +for the set RGB values in all cases. Due to the RGB mapping from +0-255 and the HSL mapping from 0-100 and 0-360 rounding errors may +cause the HSL values to differ slightly from what you might expect.

+
+ +
+
+i1i2i3
+
+
Gets or sets the I1I2I3 representation of the Color.
+
i1i2i3 -> tuple
+
+

The I1I2I3 representation of the Color. The I1I2I3 components are +in the ranges I1 = [0, 1], I2 = [-0.5, 0.5], I3 = [-0.5, +0.5]. Note that this will not return the absolutely exact I1I2I3 +values for the set RGB values in all cases. Due to the RGB +mapping from 0-255 and the I1I2I3 mapping from 0-1 rounding errors +may cause the I1I2I3 values to differ slightly from what you might +expect.

+
+ +
+
+normalize()
+
+
Returns the normalized RGBA values of the Color.
+
normalize() -> tuple
+
+

Returns the normalized RGBA values of the Color as floating point +values.

+
+ +
+
+correct_gamma()
+
+
Applies a certain gamma value to the Color.
+
correct_gamma (gamma) -> Color
+
+

Applies a certain gamma value to the Color and returns a new Color with +the adjusted RGBA values.

+
+ +
+
+set_length()
+
+
Set the number of elements in the Color to 1,2,3, or 4.
+
set_length(len) -> None
+
+

DEPRECATED: You may unpack the values you need like so, +r, g, b, _ = pygame.Color(100, 100, 100) +If you only want r, g and b +Or +r, g, *_ = pygame.Color(100, 100, 100) +if you only want r and g

+

The default Color length is 4. Colors can have lengths 1,2,3 or 4. This +is useful if you want to unpack to r,g,b and not r,g,b,a. If you want to +get the length of a Color do len(acolor).

+
+

Deprecated since pygame 2.1.3.

+
+
+

New in pygame 1.9.0.

+
+
+ +
+
+grayscale()
+
+
returns the grayscale of a Color
+
grayscale() -> Color
+
+

Returns a Color which represents the grayscaled version of self using the luminosity formula which weights red, green and blue according to their wavelengths..

+
+ +
+
+lerp()
+
+
returns a linear interpolation to the given Color.
+
lerp(Color, float) -> Color
+
+

Returns a Color which is a linear interpolation between self and the +given Color in RGBA space. The second parameter determines how far +between self and other the result is going to be. +It must be a value between 0 and 1 where 0 means self and 1 means +other will be returned.

+
+

New in pygame 2.0.1.

+
+
+ +
+
+premul_alpha()
+
+
returns a Color where the r,g,b components have been multiplied by the alpha.
+
premul_alpha() -> Color
+
+

Returns a new Color where each of the red, green and blue colour +channels have been multiplied by the alpha channel of the original +color. The alpha channel remains unchanged.

+

This is useful when working with the BLEND_PREMULTIPLIED blending mode +flag for pygame.Surface.blit()draw one image onto another, which assumes that all surfaces using +it are using pre-multiplied alpha colors.

+
+

New in pygame 2.0.0.

+
+
+ +
+
+update()
+
+
Sets the elements of the color
+
update(r, g, b) -> None
+
update(r, g, b, a=255) -> None
+
update(color_value) -> None
+
+

Sets the elements of the color. See parameters for pygame.Color()pygame object for color representations for the +parameters of this function. If the alpha value was not set it will not change.

+
+

New in pygame 2.0.1.

+
+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/color_list.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/color_list.html new file mode 100644 index 00000000..5570c2d7 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/color_list.html @@ -0,0 +1,2811 @@ + + + + + + + + + Named Colors — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+

pygame.Colorpygame object for color representations lets you specify any of these named colors when creating a new +pygame.Color (taken from the +colordict module).

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Name

Color

aliceblue

████████

antiquewhite

████████

antiquewhite1

████████

antiquewhite2

████████

antiquewhite3

████████

antiquewhite4

████████

aqua

████████

aquamarine

████████

aquamarine1

████████

aquamarine2

████████

aquamarine3

████████

aquamarine4

████████

azure

████████

azure1

████████

azure2

████████

azure3

████████

azure4

████████

beige

████████

bisque

████████

bisque1

████████

bisque2

████████

bisque3

████████

bisque4

████████

black

████████

blanchedalmond

████████

blue

████████

blue1

████████

blue2

████████

blue3

████████

blue4

████████

blueviolet

████████

brown

████████

brown1

████████

brown2

████████

brown3

████████

brown4

████████

burlywood

████████

burlywood1

████████

burlywood2

████████

burlywood3

████████

burlywood4

████████

cadetblue

████████

cadetblue1

████████

cadetblue2

████████

cadetblue3

████████

cadetblue4

████████

chartreuse

████████

chartreuse1

████████

chartreuse2

████████

chartreuse3

████████

chartreuse4

████████

chocolate

████████

chocolate1

████████

chocolate2

████████

chocolate3

████████

chocolate4

████████

coral

████████

coral1

████████

coral2

████████

coral3

████████

coral4

████████

cornflowerblue

████████

cornsilk

████████

cornsilk1

████████

cornsilk2

████████

cornsilk3

████████

cornsilk4

████████

crimson

████████

cyan

████████

cyan1

████████

cyan2

████████

cyan3

████████

cyan4

████████

darkblue

████████

darkcyan

████████

darkgoldenrod

████████

darkgoldenrod1

████████

darkgoldenrod2

████████

darkgoldenrod3

████████

darkgoldenrod4

████████

darkgray

████████

darkgreen

████████

darkgrey

████████

darkkhaki

████████

darkmagenta

████████

darkolivegreen

████████

darkolivegreen1

████████

darkolivegreen2

████████

darkolivegreen3

████████

darkolivegreen4

████████

darkorange

████████

darkorange1

████████

darkorange2

████████

darkorange3

████████

darkorange4

████████

darkorchid

████████

darkorchid1

████████

darkorchid2

████████

darkorchid3

████████

darkorchid4

████████

darkred

████████

darksalmon

████████

darkseagreen

████████

darkseagreen1

████████

darkseagreen2

████████

darkseagreen3

████████

darkseagreen4

████████

darkslateblue

████████

darkslategray

████████

darkslategray1

████████

darkslategray2

████████

darkslategray3

████████

darkslategray4

████████

darkslategrey

████████

darkturquoise

████████

darkviolet

████████

deeppink

████████

deeppink1

████████

deeppink2

████████

deeppink3

████████

deeppink4

████████

deepskyblue

████████

deepskyblue1

████████

deepskyblue2

████████

deepskyblue3

████████

deepskyblue4

████████

dimgray

████████

dimgrey

████████

dodgerblue

████████

dodgerblue1

████████

dodgerblue2

████████

dodgerblue3

████████

dodgerblue4

████████

firebrick

████████

firebrick1

████████

firebrick2

████████

firebrick3

████████

firebrick4

████████

floralwhite

████████

forestgreen

████████

fuchsia

████████

gainsboro

████████

ghostwhite

████████

gold

████████

gold1

████████

gold2

████████

gold3

████████

gold4

████████

goldenrod

████████

goldenrod1

████████

goldenrod2

████████

goldenrod3

████████

goldenrod4

████████

gray

████████

gray0

████████

gray1

████████

gray2

████████

gray3

████████

gray4

████████

gray5

████████

gray6

████████

gray7

████████

gray8

████████

gray9

████████

gray10

████████

gray11

████████

gray12

████████

gray13

████████

gray14

████████

gray15

████████

gray16

████████

gray17

████████

gray18

████████

gray19

████████

gray20

████████

gray21

████████

gray22

████████

gray23

████████

gray24

████████

gray25

████████

gray26

████████

gray27

████████

gray28

████████

gray29

████████

gray30

████████

gray31

████████

gray32

████████

gray33

████████

gray34

████████

gray35

████████

gray36

████████

gray37

████████

gray38

████████

gray39

████████

gray40

████████

gray41

████████

gray42

████████

gray43

████████

gray44

████████

gray45

████████

gray46

████████

gray47

████████

gray48

████████

gray49

████████

gray50

████████

gray51

████████

gray52

████████

gray53

████████

gray54

████████

gray55

████████

gray56

████████

gray57

████████

gray58

████████

gray59

████████

gray60

████████

gray61

████████

gray62

████████

gray63

████████

gray64

████████

gray65

████████

gray66

████████

gray67

████████

gray68

████████

gray69

████████

gray70

████████

gray71

████████

gray72

████████

gray73

████████

gray74

████████

gray75

████████

gray76

████████

gray77

████████

gray78

████████

gray79

████████

gray80

████████

gray81

████████

gray82

████████

gray83

████████

gray84

████████

gray85

████████

gray86

████████

gray87

████████

gray88

████████

gray89

████████

gray90

████████

gray91

████████

gray92

████████

gray93

████████

gray94

████████

gray95

████████

gray96

████████

gray97

████████

gray98

████████

gray99

████████

gray100

████████

green

████████

green1

████████

green2

████████

green3

████████

green4

████████

greenyellow

████████

grey

████████

grey0

████████

grey1

████████

grey2

████████

grey3

████████

grey4

████████

grey5

████████

grey6

████████

grey7

████████

grey8

████████

grey9

████████

grey10

████████

grey11

████████

grey12

████████

grey13

████████

grey14

████████

grey15

████████

grey16

████████

grey17

████████

grey18

████████

grey19

████████

grey20

████████

grey21

████████

grey22

████████

grey23

████████

grey24

████████

grey25

████████

grey26

████████

grey27

████████

grey28

████████

grey29

████████

grey30

████████

grey31

████████

grey32

████████

grey33

████████

grey34

████████

grey35

████████

grey36

████████

grey37

████████

grey38

████████

grey39

████████

grey40

████████

grey41

████████

grey42

████████

grey43

████████

grey44

████████

grey45

████████

grey46

████████

grey47

████████

grey48

████████

grey49

████████

grey50

████████

grey51

████████

grey52

████████

grey53

████████

grey54

████████

grey55

████████

grey56

████████

grey57

████████

grey58

████████

grey59

████████

grey60

████████

grey61

████████

grey62

████████

grey63

████████

grey64

████████

grey65

████████

grey66

████████

grey67

████████

grey68

████████

grey69

████████

grey70

████████

grey71

████████

grey72

████████

grey73

████████

grey74

████████

grey75

████████

grey76

████████

grey77

████████

grey78

████████

grey79

████████

grey80

████████

grey81

████████

grey82

████████

grey83

████████

grey84

████████

grey85

████████

grey86

████████

grey87

████████

grey88

████████

grey89

████████

grey90

████████

grey91

████████

grey92

████████

grey93

████████

grey94

████████

grey95

████████

grey96

████████

grey97

████████

grey98

████████

grey99

████████

grey100

████████

honeydew

████████

honeydew1

████████

honeydew2

████████

honeydew3

████████

honeydew4

████████

hotpink

████████

hotpink1

████████

hotpink2

████████

hotpink3

████████

hotpink4

████████

indianred

████████

indianred1

████████

indianred2

████████

indianred3

████████

indianred4

████████

indigo

████████

ivory

████████

ivory1

████████

ivory2

████████

ivory3

████████

ivory4

████████

khaki

████████

khaki1

████████

khaki2

████████

khaki3

████████

khaki4

████████

lavender

████████

lavenderblush

████████

lavenderblush1

████████

lavenderblush2

████████

lavenderblush3

████████

lavenderblush4

████████

lawngreen

████████

lemonchiffon

████████

lemonchiffon1

████████

lemonchiffon2

████████

lemonchiffon3

████████

lemonchiffon4

████████

lightblue

████████

lightblue1

████████

lightblue2

████████

lightblue3

████████

lightblue4

████████

lightcoral

████████

lightcyan

████████

lightcyan1

████████

lightcyan2

████████

lightcyan3

████████

lightcyan4

████████

lightgoldenrod

████████

lightgoldenrod1

████████

lightgoldenrod2

████████

lightgoldenrod3

████████

lightgoldenrod4

████████

lightgoldenrodyellow

████████

lightgray

████████

lightgreen

████████

lightgrey

████████

lightpink

████████

lightpink1

████████

lightpink2

████████

lightpink3

████████

lightpink4

████████

lightsalmon

████████

lightsalmon1

████████

lightsalmon2

████████

lightsalmon3

████████

lightsalmon4

████████

lightseagreen

████████

lightskyblue

████████

lightskyblue1

████████

lightskyblue2

████████

lightskyblue3

████████

lightskyblue4

████████

lightslateblue

████████

lightslategray

████████

lightslategrey

████████

lightsteelblue

████████

lightsteelblue1

████████

lightsteelblue2

████████

lightsteelblue3

████████

lightsteelblue4

████████

lightyellow

████████

lightyellow1

████████

lightyellow2

████████

lightyellow3

████████

lightyellow4

████████

lime

████████

limegreen

████████

linen

████████

magenta

████████

magenta1

████████

magenta2

████████

magenta3

████████

magenta4

████████

maroon

████████

maroon1

████████

maroon2

████████

maroon3

████████

maroon4

████████

mediumaquamarine

████████

mediumblue

████████

mediumorchid

████████

mediumorchid1

████████

mediumorchid2

████████

mediumorchid3

████████

mediumorchid4

████████

mediumpurple

████████

mediumpurple1

████████

mediumpurple2

████████

mediumpurple3

████████

mediumpurple4

████████

mediumseagreen

████████

mediumslateblue

████████

mediumspringgreen

████████

mediumturquoise

████████

mediumvioletred

████████

midnightblue

████████

mintcream

████████

mistyrose

████████

mistyrose1

████████

mistyrose2

████████

mistyrose3

████████

mistyrose4

████████

moccasin

████████

navajowhite

████████

navajowhite1

████████

navajowhite2

████████

navajowhite3

████████

navajowhite4

████████

navy

████████

navyblue

████████

oldlace

████████

olive

████████

olivedrab

████████

olivedrab1

████████

olivedrab2

████████

olivedrab3

████████

olivedrab4

████████

orange

████████

orange1

████████

orange2

████████

orange3

████████

orange4

████████

orangered

████████

orangered1

████████

orangered2

████████

orangered3

████████

orangered4

████████

orchid

████████

orchid1

████████

orchid2

████████

orchid3

████████

orchid4

████████

palegoldenrod

████████

palegreen

████████

palegreen1

████████

palegreen2

████████

palegreen3

████████

palegreen4

████████

paleturquoise

████████

paleturquoise1

████████

paleturquoise2

████████

paleturquoise3

████████

paleturquoise4

████████

palevioletred

████████

palevioletred1

████████

palevioletred2

████████

palevioletred3

████████

palevioletred4

████████

papayawhip

████████

peachpuff

████████

peachpuff1

████████

peachpuff2

████████

peachpuff3

████████

peachpuff4

████████

peru

████████

pink

████████

pink1

████████

pink2

████████

pink3

████████

pink4

████████

plum

████████

plum1

████████

plum2

████████

plum3

████████

plum4

████████

powderblue

████████

purple

████████

purple1

████████

purple2

████████

purple3

████████

purple4

████████

red

████████

red1

████████

red2

████████

red3

████████

red4

████████

rosybrown

████████

rosybrown1

████████

rosybrown2

████████

rosybrown3

████████

rosybrown4

████████

royalblue

████████

royalblue1

████████

royalblue2

████████

royalblue3

████████

royalblue4

████████

saddlebrown

████████

salmon

████████

salmon1

████████

salmon2

████████

salmon3

████████

salmon4

████████

sandybrown

████████

seagreen

████████

seagreen1

████████

seagreen2

████████

seagreen3

████████

seagreen4

████████

seashell

████████

seashell1

████████

seashell2

████████

seashell3

████████

seashell4

████████

sienna

████████

sienna1

████████

sienna2

████████

sienna3

████████

sienna4

████████

silver

████████

skyblue

████████

skyblue1

████████

skyblue2

████████

skyblue3

████████

skyblue4

████████

slateblue

████████

slateblue1

████████

slateblue2

████████

slateblue3

████████

slateblue4

████████

slategray

████████

slategray1

████████

slategray2

████████

slategray3

████████

slategray4

████████

slategrey

████████

snow

████████

snow1

████████

snow2

████████

snow3

████████

snow4

████████

springgreen

████████

springgreen1

████████

springgreen2

████████

springgreen3

████████

springgreen4

████████

steelblue

████████

steelblue1

████████

steelblue2

████████

steelblue3

████████

steelblue4

████████

tan

████████

tan1

████████

tan2

████████

tan3

████████

tan4

████████

teal

████████

thistle

████████

thistle1

████████

thistle2

████████

thistle3

████████

thistle4

████████

tomato

████████

tomato1

████████

tomato2

████████

tomato3

████████

tomato4

████████

turquoise

████████

turquoise1

████████

turquoise2

████████

turquoise3

████████

turquoise4

████████

violet

████████

violetred

████████

violetred1

████████

violetred2

████████

violetred3

████████

violetred4

████████

wheat

████████

wheat1

████████

wheat2

████████

wheat3

████████

wheat4

████████

white

████████

whitesmoke

████████

yellow

████████

yellow1

████████

yellow2

████████

yellow3

████████

yellow4

████████

yellowgreen

████████

+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/cursors.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/cursors.html new file mode 100644 index 00000000..ed9bae24 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/cursors.html @@ -0,0 +1,450 @@ + + + + + + + + + pygame.cursors — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.cursors
+
+
pygame module for cursor resources
+
+ +++++ + + + + + + + + + + + + + + +
+create binary cursor data from simple strings
+load cursor data from an XBM file
+pygame object representing a cursor
+

Pygame offers control over the system hardware cursor. Pygame supports +black and white cursors (bitmap cursors), as well as system variant cursors and color cursors. +You control the cursor with functions inside pygame.mousepygame module to work with the mouse.

+

This cursors module contains functions for loading and decoding various +cursor formats. These allow you to easily store your cursors in external files +or directly as encoded python strings.

+

The module includes several standard cursors. The pygame.mouse.set_cursor()set the mouse cursor to a new cursor +function takes several arguments. All those arguments have been stored in a +single tuple you can call like this:

+
>>> pygame.mouse.set_cursor(*pygame.cursors.arrow)
+
+
+

The following variables can be passed to pygame.mouse.set_cursor function:

+
+
    +
  • pygame.cursors.arrow

  • +
  • pygame.cursors.diamond

  • +
  • pygame.cursors.broken_x

  • +
  • pygame.cursors.tri_left

  • +
  • pygame.cursors.tri_right

  • +
+
+

This module also contains a few cursors as formatted strings. You'll need to +pass these to pygame.cursors.compile() function before you can use them. +The example call would look like this:

+
>>> cursor = pygame.cursors.compile(pygame.cursors.textmarker_strings)
+>>> pygame.mouse.set_cursor((8, 16), (0, 0), *cursor)
+
+
+

The following strings can be converted into cursor bitmaps with +pygame.cursors.compile() :

+
+
    +
  • pygame.cursors.thickarrow_strings

  • +
  • pygame.cursors.sizer_x_strings

  • +
  • pygame.cursors.sizer_y_strings

  • +
  • pygame.cursors.sizer_xy_strings

  • +
  • pygame.cursor.textmarker_strings

  • +
+
+
+
+pygame.cursors.compile()
+
+
create binary cursor data from simple strings
+
compile(strings, black='X', white='.', xor='o') -> data, mask
+
+

A sequence of strings can be used to create binary cursor data for the +system cursor. This returns the binary data in the form of two tuples. +Those can be passed as the third and fourth arguments respectively of the +pygame.mouse.set_cursor()set the mouse cursor to a new cursor function.

+

If you are creating your own cursor strings, you can use any value represent +the black and white pixels. Some system allow you to set a special toggle +color for the system color, this is also called the xor color. If the system +does not support xor cursors, that color will simply be black.

+

The height must be divisible by 8. The width of the strings must all be equal +and be divisible by 8. If these two conditions are not met, ValueError is +raised. +An example set of cursor strings looks like this

+
thickarrow_strings = (               #sized 24x24
+  "XX                      ",
+  "XXX                     ",
+  "XXXX                    ",
+  "XX.XX                   ",
+  "XX..XX                  ",
+  "XX...XX                 ",
+  "XX....XX                ",
+  "XX.....XX               ",
+  "XX......XX              ",
+  "XX.......XX             ",
+  "XX........XX            ",
+  "XX........XXX           ",
+  "XX......XXXXX           ",
+  "XX.XXX..XX              ",
+  "XXXX XX..XX             ",
+  "XX   XX..XX             ",
+  "     XX..XX             ",
+  "      XX..XX            ",
+  "      XX..XX            ",
+  "       XXXX             ",
+  "       XX               ",
+  "                        ",
+  "                        ",
+  "                        ")
+
+
+
+ +
+
+pygame.cursors.load_xbm()
+
+
load cursor data from an XBM file
+
load_xbm(cursorfile) -> cursor_args
+
load_xbm(cursorfile, maskfile) -> cursor_args
+
+

This loads cursors for a simple subset of XBM files. XBM files are +traditionally used to store cursors on UNIX systems, they are an ASCII +format used to represent simple images.

+

Sometimes the black and white color values will be split into two separate +XBM files. You can pass a second maskfile argument to load the two +images into a single cursor.

+

The cursorfile and maskfile arguments can either be filenames or file-like +object with the readlines method.

+

The return value cursor_args can be passed directly to the +pygame.mouse.set_cursor() function.

+
+ +
+
+pygame.cursors.Cursor
+
+
pygame object representing a cursor
+
Cursor(size, hotspot, xormasks, andmasks) -> Cursor
+
Cursor(hotspot, surface) -> Cursor
+
Cursor(constant) -> Cursor
+
Cursor(Cursor) -> Cursor
+
Cursor() -> Cursor
+
+ +++++ + + + + + + + + + + + + + + +
+copy the current cursor
+Gets the cursor type
+Gets the cursor data
+

In pygame 2, there are 3 types of cursors you can create to give your +game that little bit of extra polish. There's bitmap type cursors, +which existed in pygame 1.x, and are compiled from a string or load from an xbm file. +Then there are system type cursors, where you choose a preset that will +convey the same meaning but look native across different operating systems. +Finally you can create a color cursor, which displays a pygame surface as the cursor.

+

Creating a system cursor

+

Choose a constant from this list, pass it into pygame.cursors.Cursor(constant), +and you're good to go. Be advised that not all systems support every system +cursor, and you may get a substitution instead. For example, on MacOS, +WAIT/WAITARROW should show up as an arrow, and SIZENWSE/SIZENESW/SIZEALL +should show up as a closed hand. And on Wayland, every SIZE cursor should +show up as a hand.

+
Pygame Cursor Constant           Description
+--------------------------------------------
+pygame.SYSTEM_CURSOR_ARROW       arrow
+pygame.SYSTEM_CURSOR_IBEAM       i-beam
+pygame.SYSTEM_CURSOR_WAIT        wait
+pygame.SYSTEM_CURSOR_CROSSHAIR   crosshair
+pygame.SYSTEM_CURSOR_WAITARROW   small wait cursor
+                                 (or wait if not available)
+pygame.SYSTEM_CURSOR_SIZENWSE    double arrow pointing
+                                 northwest and southeast
+pygame.SYSTEM_CURSOR_SIZENESW    double arrow pointing
+                                 northeast and southwest
+pygame.SYSTEM_CURSOR_SIZEWE      double arrow pointing
+                                 west and east
+pygame.SYSTEM_CURSOR_SIZENS      double arrow pointing
+                                 north and south
+pygame.SYSTEM_CURSOR_SIZEALL     four pointed arrow pointing
+                                 north, south, east, and west
+pygame.SYSTEM_CURSOR_NO          slashed circle or crossbones
+pygame.SYSTEM_CURSOR_HAND        hand
+
+
+

Creating a cursor without passing arguments

+

In addition to the cursor constants available and described above, +you can also call pygame.cursors.Cursor(), and your cursor is ready (doing that is the same as +calling pygame.cursors.Cursor(pygame.SYSTEM_CURSOR_ARROW). +Doing one of those calls actually creates a system cursor using the default native image.

+

Creating a color cursor

+

To create a color cursor, create a Cursor from a hotspot and a surface. +hotspot is an (x,y) coordinate that determines where in the cursor the exact point is. +The hotspot position must be within the bounds of the surface.

+

Creating a bitmap cursor

+

When the mouse cursor is visible, it will be displayed as a black and white +bitmap using the given bitmask arrays. The size is a sequence containing +the cursor width and height. hotspot is a sequence containing the cursor +hotspot position.

+

A cursor has a width and height, but a mouse position is represented by a +set of point coordinates. So the value passed into the cursor hotspot +variable helps pygame to actually determine at what exact point the cursor +is at.

+

xormasks is a sequence of bytes containing the cursor xor data masks. +Lastly andmasks, a sequence of bytes containing the cursor bitmask data. +To create these variables, we can make use of the +pygame.cursors.compile()create binary cursor data from simple strings function.

+

Width and height must be a multiple of 8, and the mask arrays must be the +correct size for the given width and height. Otherwise an exception is raised.

+
+
+copy()
+
+
copy the current cursor
+
copy() -> Cursor
+
+

Returns a new Cursor object with the same data and hotspot as the original.

+
+ +
+
+type
+
+
Gets the cursor type
+
type -> string
+
+

The type will be "system", "bitmap", or "color".

+
+ +
+
+data
+
+
Gets the cursor data
+
data -> tuple
+
+

Returns the data that was used to create this cursor object, wrapped up in a tuple.

+
+ +
+

New in pygame 2.0.1.

+
+
+ +

Example code for creating and settings cursors. (Click the mouse to switch cursor)

+
# pygame setup
+import pygame as pg
+
+pg.init()
+screen = pg.display.set_mode([600, 400])
+pg.display.set_caption("Example code for the cursors module")
+
+# create a system cursor
+system = pg.cursors.Cursor(pg.SYSTEM_CURSOR_NO)
+
+# create bitmap cursors
+bitmap_1 = pg.cursors.Cursor(*pg.cursors.arrow)
+bitmap_2 = pg.cursors.Cursor(
+    (24, 24), (0, 0), *pg.cursors.compile(pg.cursors.thickarrow_strings)
+)
+
+# create a color cursor
+surf = pg.Surface((40, 40)) # you could also load an image 
+surf.fill((120, 50, 50))        # and use that as your surface
+color = pg.cursors.Cursor((20, 20), surf)
+
+cursors = [system, bitmap_1, bitmap_2, color]
+cursor_index = 0
+
+pg.mouse.set_cursor(cursors[cursor_index])
+
+clock = pg.time.Clock()
+going = True
+while going:
+    clock.tick(60)
+    screen.fill((0, 75, 30))
+    pg.display.flip()
+
+    for event in pg.event.get():
+        if event.type == pg.QUIT or (event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE):
+            going = False
+
+        # if the mouse is clicked it will switch to a new cursor
+        if event.type == pg.MOUSEBUTTONDOWN:
+            cursor_index += 1
+            cursor_index %= len(cursors)
+            pg.mouse.set_cursor(cursors[cursor_index])
+
+pg.quit()
+
+
+
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/display.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/display.html new file mode 100644 index 00000000..61618200 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/display.html @@ -0,0 +1,1013 @@ + + + + + + + + + pygame.display — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.display
+
+
pygame module to control the display window and screen
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Initialize the display module
+Uninitialize the display module
+Returns True if the display module has been initialized
+Initialize a window or screen for display
+Get a reference to the currently set display surface
+Update the full display Surface to the screen
+Update portions of the screen for software displays
+Get the name of the pygame display backend
+Create a video display information object
+Get information about the current windowing system
+Get sizes of active desktops
+Get list of available fullscreen modes
+Pick the best color depth for a display mode
+Get the value for an OpenGL flag for the current display
+Request an OpenGL display attribute for the display mode
+Returns True when the display is active on the screen
+Iconify the display surface
+Switch between fullscreen and windowed displays
+Change the hardware gamma ramps
+Change the hardware gamma ramps with a custom lookup
+Change the system image for the display window
+Set the current window caption
+Get the current window caption
+Set the display color palette for indexed displays
+Return the number of displays
+Return the size of the window or screen
+Return whether the screensaver is allowed to run.
+Set whether the screensaver may run
+

This module offers control over the pygame display. Pygame has a single display +Surface that is either contained in a window or runs full screen. Once you +create the display you treat it as a regular Surface. Changes are not +immediately visible onscreen; you must choose one of the two flipping functions +to update the actual display.

+

The origin of the display, where x = 0 and y = 0, is the top left of the +screen. Both axes increase positively towards the bottom right of the screen.

+

The pygame display can actually be initialized in one of several modes. By +default, the display is a basic software driven framebuffer. You can request +special modules like automatic scaling or OpenGL support. These are +controlled by flags passed to pygame.display.set_mode().

+

Pygame can only have a single display active at any time. Creating a new one +with pygame.display.set_mode() will close the previous display. To detect +the number and size of attached screens, you can use +pygame.display.get_desktop_sizes and then select appropriate window size +and display index to pass to pygame.display.set_mode().

+

For backward compatibility pygame.display allows precise control over +the pixel format or display resolutions. This used to be necessary with old +graphics cards and CRT screens, but is usually not needed any more. Use the +functions pygame.display.mode_ok(), pygame.display.list_modes(), and +pygame.display.Info() to query detailed information about the display.

+

Once the display Surface is created, the functions from this module affect the +single existing display. The Surface becomes invalid if the module is +uninitialized. If a new display mode is set, the existing Surface will +automatically switch to operate on the new display.

+

When the display mode is set, several events are placed on the pygame event +queue. pygame.QUIT is sent when the user has requested the program to +shut down. The window will receive pygame.ACTIVEEVENT events as the display +gains and loses input focus. If the display is set with the +pygame.RESIZABLE flag, pygame.VIDEORESIZE events will be sent when the +user adjusts the window dimensions. Hardware displays that draw direct to the +screen will get pygame.VIDEOEXPOSE events when portions of the window must +be redrawn.

+

A new windowevent API was introduced in pygame 2.0.1. Check event module docs +for more information on that

+

Some display environments have an option for automatically stretching all +windows. When this option is enabled, this automatic stretching distorts the +appearance of the pygame window. In the pygame examples directory, there is +example code (prevent_display_stretching.py) which shows how to disable this +automatic stretching of the pygame display on Microsoft Windows (Vista or newer +required).

+
+
+pygame.display.init()
+
+
Initialize the display module
+
init() -> None
+
+

Initializes the pygame display module. The display module cannot do anything +until it is initialized. This is usually handled for you automatically when +you call the higher level pygame.init().

+

Pygame will select from one of several internal display backends when it is +initialized. The display mode will be chosen depending on the platform and +permissions of current user. Before the display module is initialized the +environment variable SDL_VIDEODRIVER can be set to control which backend +is used. The systems with multiple choices are listed here.

+
Windows : windib, directx
+Unix    : x11, dga, fbcon, directfb, ggi, vgl, svgalib, aalib
+
+
+

On some platforms it is possible to embed the pygame display into an already +existing window. To do this, the environment variable SDL_WINDOWID must +be set to a string containing the window id or handle. The environment +variable is checked when the pygame display is initialized. Be aware that +there can be many strange side effects when running in an embedded display.

+

It is harmless to call this more than once, repeated calls have no effect.

+
+ +
+
+pygame.display.quit()
+
+
Uninitialize the display module
+
quit() -> None
+
+

This will shut down the entire display module. This means any active +displays will be closed. This will also be handled automatically when the +program exits.

+

It is harmless to call this more than once, repeated calls have no effect.

+
+ +
+
+pygame.display.get_init()
+
+
Returns True if the display module has been initialized
+
get_init() -> bool
+
+

Returns True if the pygame.displaypygame module to control the display window and screen module is currently initialized.

+
+ +
+
+pygame.display.set_mode()
+
+
Initialize a window or screen for display
+
set_mode(size=(0, 0), flags=0, depth=0, display=0, vsync=0) -> Surface
+
+

This function will create a display Surface. The arguments passed in are +requests for a display type. The actual created display will be the best +possible match supported by the system.

+

Note that calling this function implicitly initializes pygame.display, if +it was not initialized before.

+

The size argument is a pair of numbers representing the width and +height. The flags argument is a collection of additional options. The depth +argument represents the number of bits to use for color.

+

The Surface that gets returned can be drawn to like a regular Surface but +changes will eventually be seen on the monitor.

+

If no size is passed or is set to (0, 0) and pygame uses SDL +version 1.2.10 or above, the created Surface will have the same size as the +current screen resolution. If only the width or height are set to 0, the +Surface will have the same width or height as the screen resolution. Using a +SDL version prior to 1.2.10 will raise an exception.

+

It is usually best to not pass the depth argument. It will default to the +best and fastest color depth for the system. If your game requires a +specific color format you can control the depth with this argument. Pygame +will emulate an unavailable color depth which can be slow.

+

When requesting fullscreen display modes, sometimes an exact match for the +requested size cannot be made. In these situations pygame will select +the closest compatible match. The returned surface will still always match +the requested size.

+

On high resolution displays(4k, 1080p) and tiny graphics games (640x480) +show up very small so that they are unplayable. SCALED scales up the window +for you. The game thinks it's a 640x480 window, but really it can be bigger. +Mouse events are scaled for you, so your game doesn't need to do it. Note +that SCALED is considered an experimental API and may change in future +releases.

+

The flags argument controls which type of display you want. There are +several to choose from, and you can even combine multiple types using the +bitwise or operator, (the pipe "|" character). Here are the display +flags you will want to choose from:

+
pygame.FULLSCREEN    create a fullscreen display
+pygame.DOUBLEBUF     only applicable with OPENGL
+pygame.HWSURFACE     (obsolete in pygame 2) hardware accelerated, only in FULLSCREEN
+pygame.OPENGL        create an OpenGL-renderable display
+pygame.RESIZABLE     display window should be sizeable
+pygame.NOFRAME       display window will have no border or controls
+pygame.SCALED        resolution depends on desktop size and scale graphics
+pygame.SHOWN         window is opened in visible mode (default)
+pygame.HIDDEN        window is opened in hidden mode
+
+
+
+

New in pygame 2.0.0: SCALED, SHOWN and HIDDEN

+
+

By setting the vsync parameter to 1, it is possible to get a display +with vertical sync, but you are not guaranteed to get one. The request only +works at all for calls to set_mode() with the pygame.OPENGL or +pygame.SCALED flags set, and is still not guaranteed even with one of +those set. What you get depends on the hardware and driver configuration +of the system pygame is running on. Here is an example usage of a call +to set_mode() that may give you a display with vsync:

+
flags = pygame.OPENGL | pygame.FULLSCREEN
+window_surface = pygame.display.set_mode((1920, 1080), flags, vsync=1)
+
+
+

Vsync behaviour is considered experimental, and may change in future releases.

+
+

New in pygame 2.0.0: vsync

+
+

Basic example:

+
# Open a window on the screen
+screen_width=700
+screen_height=400
+screen=pygame.display.set_mode([screen_width, screen_height])
+
+
+

The display index 0 means the default display is used. If no display +index argument is provided, the default display can be overridden with an +environment variable.

+
+

Changed in pygame 1.9.5: display argument added

+
+
+

Changed in pygame 2.1.3: pygame now ensures that subsequent calls to this function clears the +window to black. On older versions, this was an implementation detail +on the major platforms this function was tested with.

+
+
+ +
+
+pygame.display.get_surface()
+
+
Get a reference to the currently set display surface
+
get_surface() -> Surface
+
+

Return a reference to the currently set display Surface. If no display mode +has been set this will return None.

+
+ +
+
+pygame.display.flip()
+
+
Update the full display Surface to the screen
+
flip() -> None
+
+

This will update the contents of the entire display. If your display mode is +using the flags pygame.HWSURFACE and pygame.DOUBLEBUF on pygame 1, +this will wait for a vertical retrace and swap the surfaces.

+

When using an pygame.OPENGL display mode this will perform a gl buffer +swap.

+
+ +
+
+pygame.display.update()
+
+
Update portions of the screen for software displays
+
update(rectangle=None) -> None
+
update(rectangle_list) -> None
+
+

This function is like an optimized version of pygame.display.flip() for +software displays. It allows only a portion of the screen to be updated, +instead of the entire area. If no argument is passed it updates the entire +Surface area like pygame.display.flip().

+

Note that calling display.update(None) means no part of the window is +updated. Whereas display.update() means the whole window is updated.

+

You can pass the function a single rectangle, or a sequence of rectangles. +It is more efficient to pass many rectangles at once than to call update +multiple times with single or a partial list of rectangles. If passing a +sequence of rectangles it is safe to include None values in the list, which +will be skipped.

+

This call cannot be used on pygame.OPENGL displays and will generate an +exception.

+
+ +
+
+pygame.display.get_driver()
+
+
Get the name of the pygame display backend
+
get_driver() -> name
+
+

Pygame chooses one of many available display backends when it is +initialized. This returns the internal name used for the display backend. +This can be used to provide limited information about what display +capabilities might be accelerated. See the SDL_VIDEODRIVER flags in +pygame.display.set_mode() to see some of the common options.

+
+ +
+
+pygame.display.Info()
+
+
Create a video display information object
+
Info() -> VideoInfo
+
+

Creates a simple object containing several attributes to describe the +current graphics environment. If this is called before +pygame.display.set_mode() some platforms can provide information about +the default display mode. This can also be called after setting the display +mode to verify specific display options were satisfied. The VidInfo object +has several attributes:

+
hw:         1 if the display is hardware accelerated
+wm:         1 if windowed display modes can be used
+video_mem:  The megabytes of video memory on the display. This is 0 if
+            unknown
+bitsize:    Number of bits used to store each pixel
+bytesize:   Number of bytes used to store each pixel
+masks:      Four values used to pack RGBA values into pixels
+shifts:     Four values used to pack RGBA values into pixels
+losses:     Four values used to pack RGBA values into pixels
+blit_hw:    1 if hardware Surface blitting is accelerated
+blit_hw_CC: 1 if hardware Surface colorkey blitting is accelerated
+blit_hw_A:  1 if hardware Surface pixel alpha blitting is accelerated
+blit_sw:    1 if software Surface blitting is accelerated
+blit_sw_CC: 1 if software Surface colorkey blitting is accelerated
+blit_sw_A:  1 if software Surface pixel alpha blitting is accelerated
+current_h, current_w:  Height and width of the current video mode, or
+            of the desktop mode if called before the display.set_mode
+            is called. (current_h, current_w are available since
+            SDL 1.2.10, and pygame 1.8.0). They are -1 on error, or if
+            an old SDL is being used.
+
+
+
+ +
+
+pygame.display.get_wm_info()
+
+
Get information about the current windowing system
+
get_wm_info() -> dict
+
+

Creates a dictionary filled with string keys. The strings and values are +arbitrarily created by the system. Some systems may have no information and +an empty dictionary will be returned. Most platforms will return a "window" +key with the value set to the system id for the current display.

+
+

New in pygame 1.7.1.

+
+
+ +
+
+pygame.display.get_desktop_sizes()
+
+
Get sizes of active desktops
+
get_desktop_sizes() -> list
+
+

This function returns the sizes of the currently configured +virtual desktops as a list of (x, y) tuples of integers.

+

The length of the list is not the same as the number of attached monitors, +as a desktop can be mirrored across multiple monitors. The desktop sizes +do not indicate the maximum monitor resolutions supported by the hardware, +but the desktop size configured in the operating system.

+

In order to fit windows into the desktop as it is currently configured, and +to respect the resolution configured by the operating system in fullscreen +mode, this function should be used to replace many use cases of +pygame.display.list_modes() whenever applicable.

+
+

New in pygame 2.0.0.

+
+
+ +
+
+pygame.display.list_modes()
+
+
Get list of available fullscreen modes
+
list_modes(depth=0, flags=pygame.FULLSCREEN, display=0) -> list
+
+

This function returns a list of possible sizes for a specified color +depth. The return value will be an empty list if no display modes are +available with the given arguments. A return value of -1 means that +any requested size should work (this is likely the case for windowed +modes). Mode sizes are sorted from biggest to smallest.

+

If depth is 0, the current/best color depth for the display is used. +The flags defaults to pygame.FULLSCREEN, but you may need to add +additional flags for specific fullscreen modes.

+

The display index 0 means the default display is used.

+

Since pygame 2.0, pygame.display.get_desktop_sizes() has taken over +some use cases from pygame.display.list_modes():

+

To find a suitable size for non-fullscreen windows, it is preferable to +use pygame.display.get_desktop_sizes() to get the size of the current +desktop, and to then choose a smaller window size. This way, the window is +guaranteed to fit, even when the monitor is configured to a lower resolution +than the maximum supported by the hardware.

+

To avoid changing the physical monitor resolution, it is also preferable to +use pygame.display.get_desktop_sizes() to determine the fullscreen +resolution. Developers are strongly advised to default to the current +physical monitor resolution unless the user explicitly requests a different +one (e.g. in an options menu or configuration file).

+
+

Changed in pygame 1.9.5: display argument added

+
+
+ +
+
+pygame.display.mode_ok()
+
+
Pick the best color depth for a display mode
+
mode_ok(size, flags=0, depth=0, display=0) -> depth
+
+

This function uses the same arguments as pygame.display.set_mode(). It +is used to determine if a requested display mode is available. It will +return 0 if the display mode cannot be set. Otherwise it will return a +pixel depth that best matches the display asked for.

+

Usually the depth argument is not passed, but some platforms can support +multiple display depths. If passed it will hint to which depth is a better +match.

+

The function will return 0 if the passed display flags cannot be set.

+

The display index 0 means the default display is used.

+
+

Changed in pygame 1.9.5: display argument added

+
+
+ +
+
+pygame.display.gl_get_attribute()
+
+
Get the value for an OpenGL flag for the current display
+
gl_get_attribute(flag) -> value
+
+

After calling pygame.display.set_mode() with the pygame.OPENGL flag, +it is a good idea to check the value of any requested OpenGL attributes. See +pygame.display.gl_set_attribute() for a list of valid flags.

+
+

Changed in pygame 2.5.0: Added support for keyword arguments.

+
+
+ +
+
+pygame.display.gl_set_attribute()
+
+
Request an OpenGL display attribute for the display mode
+
gl_set_attribute(flag, value) -> None
+
+

When calling pygame.display.set_mode() with the pygame.OPENGL flag, +Pygame automatically handles setting the OpenGL attributes like color and +double-buffering. OpenGL offers several other attributes you may want control +over. Pass one of these attributes as the flag, and its appropriate value. +This must be called before pygame.display.set_mode().

+

Many settings are the requested minimum. Creating a window with an OpenGL context +will fail if OpenGL cannot provide the requested attribute, but it may for example +give you a stencil buffer even if you request none, or it may give you a larger +one than requested.

+

The OPENGL flags are:

+
GL_ALPHA_SIZE, GL_DEPTH_SIZE, GL_STENCIL_SIZE, GL_ACCUM_RED_SIZE,
+GL_ACCUM_GREEN_SIZE,  GL_ACCUM_BLUE_SIZE, GL_ACCUM_ALPHA_SIZE,
+GL_MULTISAMPLEBUFFERS, GL_MULTISAMPLESAMPLES, GL_STEREO
+
+
+

GL_MULTISAMPLEBUFFERS

+
+

Whether to enable multisampling anti-aliasing. +Defaults to 0 (disabled).

+

Set GL_MULTISAMPLESAMPLES to a value +above 0 to control the amount of anti-aliasing. +A typical value is 2 or 3.

+
+

GL_STENCIL_SIZE

+
+

Minimum bit size of the stencil buffer. Defaults to 0.

+
+

GL_DEPTH_SIZE

+
+

Minimum bit size of the depth buffer. Defaults to 16.

+
+

GL_STEREO

+
+

1 enables stereo 3D. Defaults to 0.

+
+

GL_BUFFER_SIZE

+
+

Minimum bit size of the frame buffer. Defaults to 0.

+
+
+

Changed in pygame 2.5.0: Added support for keyword arguments.

+
+
+

New in pygame 2.0.0: Additional attributes:

+
+
GL_ACCELERATED_VISUAL,
+GL_CONTEXT_MAJOR_VERSION, GL_CONTEXT_MINOR_VERSION,
+GL_CONTEXT_FLAGS, GL_CONTEXT_PROFILE_MASK,
+GL_SHARE_WITH_CURRENT_CONTEXT,
+GL_CONTEXT_RELEASE_BEHAVIOR,
+GL_FRAMEBUFFER_SRGB_CAPABLE
+
+
+

GL_CONTEXT_PROFILE_MASK

+
+

Sets the OpenGL profile to one of these values:

+
GL_CONTEXT_PROFILE_CORE             disable deprecated features
+GL_CONTEXT_PROFILE_COMPATIBILITY    allow deprecated features
+GL_CONTEXT_PROFILE_ES               allow only the ES feature
+                                    subset of OpenGL
+
+
+
+

GL_ACCELERATED_VISUAL

+
+

Set to 1 to require hardware acceleration, or 0 to force software render. +By default, both are allowed.

+
+
+ +
+
+pygame.display.get_active()
+
+
Returns True when the display is active on the screen
+
get_active() -> bool
+
+

Returns True when the display Surface is considered actively +renderable on the screen and may be visible to the user. This is +the default state immediately after pygame.display.set_mode(). +This method may return True even if the application is fully hidden +behind another application window.

+

This will return False if the display Surface has been iconified or +minimized (either via pygame.display.iconify() or via an OS +specific method such as the minimize-icon available on most +desktops).

+

The method can also return False for other reasons without the +application being explicitly iconified or minimized by the user. A +notable example being if the user has multiple virtual desktops and +the display Surface is not on the active virtual desktop.

+
+

Note

+

This function returning True is unrelated to whether the +application has input focus. Please see +pygame.key.get_focused() and pygame.mouse.get_focused() +for APIs related to input focus.

+
+
+ +
+
+pygame.display.iconify()
+
+
Iconify the display surface
+
iconify() -> bool
+
+

Request the window for the display surface be iconified or hidden. Not all +systems and displays support an iconified display. The function will return +True if successful.

+

When the display is iconified pygame.display.get_active() will return +False. The event queue should receive an ACTIVEEVENT event when the +window has been iconified. Additionally, the event queue also receives a +WINDOWEVENT_MINIMIZED event when the window has been iconified on pygame 2.

+
+ +
+
+pygame.display.toggle_fullscreen()
+
+
Switch between fullscreen and windowed displays
+
toggle_fullscreen() -> int
+
+

Switches the display window between windowed and fullscreen modes. +Display driver support is not great when using pygame 1, but with +pygame 2 it is the most reliable method to switch to and from fullscreen.

+

Supported display drivers in pygame 1:

+
+
    +
  • x11 (Linux/Unix)

  • +
  • wayland (Linux/Unix)

  • +
+
+

Supported display drivers in pygame 2:

+
+
    +
  • windows (Windows)

  • +
  • x11 (Linux/Unix)

  • +
  • wayland (Linux/Unix)

  • +
  • cocoa (OSX/Mac)

  • +
+
+
+

Note

+

toggle_fullscreen() doesn't work on Windows +unless the window size is in pygame.display.list_modes()Get list of available fullscreen modes or +the window is created with the flag pygame.SCALED. +See issue #2380.

+
+
+ +
+
+pygame.display.set_gamma()
+
+
Change the hardware gamma ramps
+
set_gamma(red, green=None, blue=None) -> bool
+
+

DEPRECATED: This functionality will go away in SDL3.

+

Set the red, green, and blue gamma values on the display hardware. If the +green and blue arguments are not passed, they will both be the same as red. +Not all systems and hardware support gamma ramps, if the function succeeds +it will return True.

+

A gamma value of 1.0 creates a linear color table. Lower values will +darken the display and higher values will brighten.

+
+

Deprecated since pygame 2.2.0.

+
+
+ +
+
+pygame.display.set_gamma_ramp()
+
+
Change the hardware gamma ramps with a custom lookup
+
set_gamma_ramp(red, green, blue) -> bool
+
+

DEPRECATED: This functionality will go away in SDL3.

+

Set the red, green, and blue gamma ramps with an explicit lookup table. Each +argument should be sequence of 256 integers. The integers should range +between 0 and 0xffff. Not all systems and hardware support gamma +ramps, if the function succeeds it will return True.

+
+

Deprecated since pygame 2.2.0.

+
+
+ +
+
+pygame.display.set_icon()
+
+
Change the system image for the display window
+
set_icon(Surface) -> None
+
+

Sets the runtime icon the system will use to represent the display window. +All windows default to a simple pygame logo for the window icon.

+

Note that calling this function implicitly initializes pygame.display, if +it was not initialized before.

+

You can pass any surface, but most systems want a smaller image around +32x32. The image can have colorkey transparency which will be passed to the +system.

+

Some systems do not allow the window icon to change after it has been shown. +This function can be called before pygame.display.set_mode() to create +the icon before the display mode is set.

+
+ +
+
+pygame.display.set_caption()
+
+
Set the current window caption
+
set_caption(title, icontitle=None) -> None
+
+

If the display has a window title, this function will change the name on the +window. In pygame 1.x, some systems supported an alternate shorter title to +be used for minimized displays, but in pygame 2 icontitle does nothing.

+
+

Changed in pygame 2.5.0: Added support for keyword arguments.

+
+
+ +
+
+pygame.display.get_caption()
+
+
Get the current window caption
+
get_caption() -> (title, icontitle)
+
+

Returns the title and icontitle for the display window. In pygame 2.x +these will always be the same value.

+
+ +
+
+pygame.display.set_palette()
+
+
Set the display color palette for indexed displays
+
set_palette(palette=None) -> None
+
+

This will change the video display color palette for 8-bit displays. This +does not change the palette for the actual display Surface, only the palette +that is used to display the Surface. If no palette argument is passed, the +system default palette will be restored. The palette is a sequence of +RGB triplets.

+
+

Changed in pygame 2.5.0: Added support for keyword arguments.

+
+
+ +
+
+pygame.display.get_num_displays()
+
+
Return the number of displays
+
get_num_displays() -> int
+
+

Returns the number of available displays. This is always 1 if +pygame.get_sdl_version()get the version number of SDL returns a major version number below 2.

+
+

New in pygame 1.9.5.

+
+
+ +
+
+pygame.display.get_window_size()
+
+
Return the size of the window or screen
+
get_window_size() -> tuple
+
+

Returns the size of the window initialized with pygame.display.set_mode()Initialize a window or screen for display. +This may differ from the size of the display surface if SCALED is used.

+
+

New in pygame 2.0.0.

+
+
+ +
+
+pygame.display.get_allow_screensaver()
+
+
Return whether the screensaver is allowed to run.
+
get_allow_screensaver() -> bool
+
+

Return whether screensaver is allowed to run whilst the app is running. +Default is False. +By default pygame does not allow the screensaver during game play.

+
+

Note

+

Some platforms do not have a screensaver or support +disabling the screensaver. Please see +pygame.display.set_allow_screensaver()Set whether the screensaver may run for +caveats with screensaver support.

+
+
+

New in pygame 2.0.0.

+
+
+ +
+
+pygame.display.set_allow_screensaver()
+
+
Set whether the screensaver may run
+
set_allow_screensaver(bool) -> None
+
+

Change whether screensavers should be allowed whilst the app is running. +The default value of the argument to the function is True. +By default pygame does not allow the screensaver during game play.

+

If the screensaver has been disallowed due to this function, it will automatically +be allowed to run when pygame.quit()uninitialize all pygame modules is called.

+

It is possible to influence the default value via the environment variable +SDL_HINT_VIDEO_ALLOW_SCREENSAVER, which can be set to either 0 (disable) +or 1 (enable).

+
+

Note

+

Disabling screensaver is subject to platform support. +When platform support is absent, this function will +silently appear to work even though the screensaver state +is unchanged. The lack of feedback is due to SDL not +providing any supported method for determining whether +it supports changing the screensaver state. +SDL_HINT_VIDEO_ALLOW_SCREENSAVER is available in SDL 2.0.2 or later. +SDL1.2 does not implement this.

+
+
+

New in pygame 2.0.0.

+
+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/draw.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/draw.html new file mode 100644 index 00000000..5a0762ca --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/draw.html @@ -0,0 +1,963 @@ + + + + + + + + + pygame.draw — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.draw
+
+
pygame module for drawing shapes
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+draw a rectangle
+draw a polygon
+draw a circle
+draw an ellipse
+draw an elliptical arc
+draw a straight line
+draw multiple contiguous straight line segments
+draw a straight antialiased line
+draw multiple contiguous straight antialiased line segments
+

Draw several simple shapes to a surface. These functions will work for +rendering to any format of surface.

+

Most of the functions take a width argument to represent the size of stroke +(thickness) around the edge of the shape. If a width of 0 is passed the shape +will be filled (solid).

+

All the drawing functions respect the clip area for the surface and will be +constrained to that area. The functions return a rectangle representing the +bounding area of changed pixels. This bounding rectangle is the 'minimum' +bounding box that encloses the affected area.

+

All the drawing functions accept a color argument that can be one of the +following formats:

+
+
+
+

A color's alpha value will be written directly into the surface (if the +surface contains pixel alphas), but the draw function will not draw +transparently.

+

These functions temporarily lock the surface they are operating on. Many +sequential drawing calls can be sped up by locking and unlocking the surface +object around the draw calls (see pygame.Surface.lock()lock the Surface memory for pixel access and +pygame.Surface.unlock()unlock the Surface memory from pixel access).

+
+

Note

+

See the pygame.gfxdrawpygame module for drawing shapes module for alternative draw methods.

+
+
+
+pygame.draw.rect()
+
+
draw a rectangle
+
rect(surface, color, rect) -> Rect
+
rect(surface, color, rect, width=0, border_radius=0, border_top_left_radius=-1, border_top_right_radius=-1, border_bottom_left_radius=-1, border_bottom_right_radius=-1) -> Rect
+
+

Draws a rectangle on the given surface.

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • color (Color or int or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
  • rect (Rect) -- rectangle to draw, position and dimensions

  • +
  • width (int) --

    (optional) used for line thickness or to indicate that +the rectangle is to be filled (not to be confused with the width value +of the rect parameter)

    +
    +
    +
    if width == 0, (default) fill the rectangle
    +
    if width > 0, used for line thickness
    +
    if width < 0, nothing will be drawn
    +

    +
    +
    +
    +

    Changed in pygame 2.1.1: Drawing rects with width now draws the width correctly inside the +rect's area, rather than using an internal call to draw.lines(), +which had half the width spill outside the rect area.

    +
    +

  • +
  • border_radius (int) -- (optional) used for drawing rectangle with rounded corners. +The supported range is [0, min(height, width) / 2], with 0 representing a rectangle +without rounded corners.

  • +
  • border_top_left_radius (int) -- (optional) used for setting the value of top left +border. If you don't set this value, it will use the border_radius value.

  • +
  • border_top_right_radius (int) -- (optional) used for setting the value of top right +border. If you don't set this value, it will use the border_radius value.

  • +
  • border_bottom_left_radius (int) -- (optional) used for setting the value of bottom left +border. If you don't set this value, it will use the border_radius value.

  • +
  • border_bottom_right_radius (int) --

    (optional) used for setting the value of bottom right +border. If you don't set this value, it will use the border_radius value.

    +
    +
    +
    if border_radius < 1 it will draw rectangle without rounded corners
    +
    if any of border radii has the value < 0 it will use value of the border_radius
    +
    If sum of radii on the same side of the rectangle is greater than the rect size the radii
    +
    will get scaled
    +
    +
    +

  • +
+
+
Returns
+

a rect bounding the changed pixels, if nothing is drawn the +bounding rect's position will be the position of the given rect +parameter and its width and height will be 0

+
+
Return type
+

Rect

+
+
+
+

Note

+

The pygame.Surface.fill()fill Surface with a solid color method works just as well for drawing +filled rectangles and can be hardware accelerated on some platforms.

+
+
+

Changed in pygame 2.0.0: Added support for keyword arguments.

+
+
+

Changed in pygame 2.0.0.dev8: Added support for border radius.

+
+
+ +
+
+pygame.draw.polygon()
+
+
draw a polygon
+
polygon(surface, color, points) -> Rect
+
polygon(surface, color, points, width=0) -> Rect
+
+

Draws a polygon on the given surface.

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • color (Color or int or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
  • points (tuple(coordinate) or list(coordinate)) -- a sequence of 3 or more (x, y) coordinates that make up the +vertices of the polygon, each coordinate in the sequence must be a +tuple/list/pygame.math.Vector2a 2-Dimensional Vector of 2 ints/floats, +e.g. [(x1, y1), (x2, y2), (x3, y3)]

  • +
  • width (int) --

    (optional) used for line thickness or to indicate that +the polygon is to be filled

    +
    +
    +
    if width == 0, (default) fill the polygon
    +
    if width > 0, used for line thickness
    +
    if width < 0, nothing will be drawn
    +

    +
    +
    +

    Note

    +

    When using width values > 1, the edge lines will grow +outside the original boundary of the polygon. For more details on +how the thickness for edge lines grow, refer to the width notes +of the pygame.draw.line()draw a straight line function.

    +
    +
    +

  • +
+
+
Returns
+

a rect bounding the changed pixels, if nothing is drawn the +bounding rect's position will be the position of the first point in the +points parameter (float values will be truncated) and its width and +height will be 0

+
+
Return type
+

Rect

+
+
Raises
+
    +
  • ValueError -- if len(points) < 3 (must have at least 3 points)

  • +
  • TypeError -- if points is not a sequence or points does not +contain number pairs

  • +
+
+
+
+

Note

+

For an aapolygon, use aalines() with closed=True.

+
+
+

Changed in pygame 2.0.0: Added support for keyword arguments.

+
+
+ +
+
+pygame.draw.circle()
+
+
draw a circle
+
circle(surface, color, center, radius) -> Rect
+
circle(surface, color, center, radius, width=0, draw_top_right=None, draw_top_left=None, draw_bottom_left=None, draw_bottom_right=None) -> Rect
+
+

Draws a circle on the given surface.

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • color (Color or int or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
  • center (tuple(int or float, int or float) or +list(int or float, int or float) or Vector2(int or float, int or float)) -- center point of the circle as a sequence of 2 ints/floats, +e.g. (x, y)

  • +
  • radius (int or float) -- radius of the circle, measured from the center parameter, +nothing will be drawn if the radius is less than 1

  • +
  • width (int) --

    (optional) used for line thickness or to indicate that +the circle is to be filled

    +
    +
    +
    if width == 0, (default) fill the circle
    +
    if width > 0, used for line thickness
    +
    if width < 0, nothing will be drawn
    +

    +
    +
    +

    Note

    +

    When using width values > 1, the edge lines will only grow +inward.

    +
    +
    +

  • +
  • draw_top_right (bool) -- (optional) if this is set to True then the top right corner +of the circle will be drawn

  • +
  • draw_top_left (bool) -- (optional) if this is set to True then the top left corner +of the circle will be drawn

  • +
  • draw_bottom_left (bool) -- (optional) if this is set to True then the bottom left corner +of the circle will be drawn

  • +
  • draw_bottom_right (bool) --

    (optional) if this is set to True then the bottom right corner +of the circle will be drawn

    +
    +
    +
    if any of the draw_circle_part is True then it will draw all circle parts that have the True
    +
    value, otherwise it will draw the entire circle.
    +
    +
    +

  • +
+
+
Returns
+

a rect bounding the changed pixels, if nothing is drawn the +bounding rect's position will be the center parameter value (float +values will be truncated) and its width and height will be 0

+
+
Return type
+

Rect

+
+
Raises
+
    +
  • TypeError -- if center is not a sequence of two numbers

  • +
  • TypeError -- if radius is not a number

  • +
+
+
+
+

Changed in pygame 2.0.0: Added support for keyword arguments. +Nothing is drawn when the radius is 0 (a pixel at the center coordinates +used to be drawn when the radius equaled 0). +Floats, and Vector2 are accepted for the center param. +The drawing algorithm was improved to look more like a circle.

+
+
+

Changed in pygame 2.0.0.dev8: Added support for drawing circle quadrants.

+
+
+ +
+
+pygame.draw.ellipse()
+
+
draw an ellipse
+
ellipse(surface, color, rect) -> Rect
+
ellipse(surface, color, rect, width=0) -> Rect
+
+

Draws an ellipse on the given surface.

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • color (Color or int or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
  • rect (Rect) -- rectangle to indicate the position and dimensions of the +ellipse, the ellipse will be centered inside the rectangle and bounded +by it

  • +
  • width (int) --

    (optional) used for line thickness or to indicate that +the ellipse is to be filled (not to be confused with the width value +of the rect parameter)

    +
    +
    +
    if width == 0, (default) fill the ellipse
    +
    if width > 0, used for line thickness
    +
    if width < 0, nothing will be drawn
    +

    +
    +
    +

    Note

    +

    When using width values > 1, the edge lines will only grow +inward from the original boundary of the rect parameter.

    +
    +
    +

  • +
+
+
Returns
+

a rect bounding the changed pixels, if nothing is drawn the +bounding rect's position will be the position of the given rect +parameter and its width and height will be 0

+
+
Return type
+

Rect

+
+
+
+

Changed in pygame 2.0.0: Added support for keyword arguments.

+
+
+ +
+
+pygame.draw.arc()
+
+
draw an elliptical arc
+
arc(surface, color, rect, start_angle, stop_angle) -> Rect
+
arc(surface, color, rect, start_angle, stop_angle, width=1) -> Rect
+
+

Draws an elliptical arc on the given surface.

+

The two angle arguments are given in radians and indicate the start and stop +positions of the arc. The arc is drawn in a counterclockwise direction from +the start_angle to the stop_angle.

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • color (Color or int or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
  • rect (Rect) -- rectangle to indicate the position and dimensions of the +ellipse which the arc will be based on, the ellipse will be centered +inside the rectangle

  • +
  • start_angle (float) -- start angle of the arc in radians

  • +
  • stop_angle (float) --

    stop angle of the arc in +radians

    +
    +
    +
    if start_angle < stop_angle, the arc is drawn in a +counterclockwise direction from the start_angle to the +stop_angle
    +
    if start_angle > stop_angle, tau (tau == 2 * pi) will be added +to the stop_angle, if the resulting stop angle value is greater +than the start_angle the above start_angle < stop_angle case +applies, otherwise nothing will be drawn
    +
    if start_angle == stop_angle, nothing will be drawn
    +

    +
    +
    +

  • +
  • width (int) --

    (optional) used for line thickness (not to be confused +with the width value of the rect parameter)

    +
    +
    +
    if width == 0, nothing will be drawn
    +
    if width > 0, (default is 1) used for line thickness
    +
    if width < 0, same as width == 0
    +
    +
    +

    Note

    +

    When using width values > 1, the edge lines will only grow +inward from the original boundary of the rect parameter.

    +
    +
    +

  • +
+
+
Returns
+

a rect bounding the changed pixels, if nothing is drawn the +bounding rect's position will be the position of the given rect +parameter and its width and height will be 0

+
+
Return type
+

Rect

+
+
+
+

Changed in pygame 2.0.0: Added support for keyword arguments.

+
+
+ +
+
+pygame.draw.line()
+
+
draw a straight line
+
line(surface, color, start_pos, end_pos) -> Rect
+
line(surface, color, start_pos, end_pos, width=1) -> Rect
+
+

Draws a straight line on the given surface. There are no endcaps. For thick +lines the ends are squared off.

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • color (Color or int or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
  • start_pos (tuple(int or float, int or float) or +list(int or float, int or float) or Vector2(int or float, int or float)) -- start position of the line, (x, y)

  • +
  • end_pos (tuple(int or float, int or float) or +list(int or float, int or float) or Vector2(int or float, int or float)) -- end position of the line, (x, y)

  • +
  • width (int) --

    (optional) used for line thickness

    +
    +
    if width >= 1, used for line thickness (default is 1)
    +
    if width < 1, nothing will be drawn
    +

    +
    +
    +

    Note

    +

    When using width values > 1, lines will grow as follows.

    +

    For odd width values, the thickness of each line grows with the +original line being in the center.

    +

    For even width values, the thickness of each line grows with the +original line being offset from the center (as there is no exact +center line drawn). As a result, lines with a slope < 1 +(horizontal-ish) will have 1 more pixel of thickness below the +original line (in the y direction). Lines with a slope >= 1 +(vertical-ish) will have 1 more pixel of thickness to the right of +the original line (in the x direction).

    +
    +

  • +
+
+
Returns
+

a rect bounding the changed pixels, if nothing is drawn the +bounding rect's position will be the start_pos parameter value (float +values will be truncated) and its width and height will be 0

+
+
Return type
+

Rect

+
+
Raises
+

TypeError -- if start_pos or end_pos is not a sequence of +two numbers

+
+
+
+

Changed in pygame 2.0.0: Added support for keyword arguments.

+
+
+ +
+
+pygame.draw.lines()
+
+
draw multiple contiguous straight line segments
+
lines(surface, color, closed, points) -> Rect
+
lines(surface, color, closed, points, width=1) -> Rect
+
+

Draws a sequence of contiguous straight lines on the given surface. There are +no endcaps or miter joints. For thick lines the ends are squared off. +Drawing thick lines with sharp corners can have undesired looking results.

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • color (Color or int or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
  • closed (bool) -- if True an additional line segment is drawn between +the first and last points in the points sequence

  • +
  • points (tuple(coordinate) or list(coordinate)) -- a sequence of 2 or more (x, y) coordinates, where each +coordinate in the sequence must be a +tuple/list/pygame.math.Vector2a 2-Dimensional Vector of 2 ints/floats and adjacent +coordinates will be connected by a line segment, e.g. for the +points [(x1, y1), (x2, y2), (x3, y3)] a line segment will be drawn +from (x1, y1) to (x2, y2) and from (x2, y2) to (x3, y3), +additionally if the closed parameter is True another line segment +will be drawn from (x3, y3) to (x1, y1)

  • +
  • width (int) --

    (optional) used for line thickness

    +
    +
    if width >= 1, used for line thickness (default is 1)
    +
    if width < 1, nothing will be drawn
    +

    +
    +
    +

    Note

    +

    When using width values > 1 refer to the width notes +of line() for details on how thick lines grow.

    +
    +

  • +
+
+
Returns
+

a rect bounding the changed pixels, if nothing is drawn the +bounding rect's position will be the position of the first point in the +points parameter (float values will be truncated) and its width and +height will be 0

+
+
Return type
+

Rect

+
+
Raises
+
    +
  • ValueError -- if len(points) < 2 (must have at least 2 points)

  • +
  • TypeError -- if points is not a sequence or points does not +contain number pairs

  • +
+
+
+
+

Changed in pygame 2.0.0: Added support for keyword arguments.

+
+
+ +
+
+pygame.draw.aaline()
+
+
draw a straight antialiased line
+
aaline(surface, color, start_pos, end_pos) -> Rect
+
aaline(surface, color, start_pos, end_pos, blend=1) -> Rect
+
+

Draws a straight antialiased line on the given surface.

+

The line has a thickness of one pixel and the endpoints have a height and +width of one pixel each.

+
+
The way a line and its endpoints are drawn:

If both endpoints are equal, only a single pixel is drawn (after +rounding floats to nearest integer).

+

Otherwise if the line is not steep (i.e. if the length along the x-axis +is greater than the height along the y-axis):

+
+

For each endpoint:

+
+

If x, the endpoint's x-coordinate, is a whole number find +which pixels would be covered by it and draw them.

+

Otherwise:

+
+

Calculate the position of the nearest point with a whole number +for its x-coordinate, when extending the line past the +endpoint.

+

Find which pixels would be covered and how much by that point.

+

If the endpoint is the left one, multiply the coverage by (1 - +the decimal part of x).

+

Otherwise multiply the coverage by the decimal part of x.

+

Then draw those pixels.

+
+
e.g.:
+
The left endpoint of the line ((1, 1.3), (5, 3)) would +cover 70% of the pixel (1, 1) and 30% of the pixel +(1, 2) while the right one would cover 100% of the +pixel (5, 3).
+
The left endpoint of the line ((1.2, 1.4), (4.6, 3.1)) +would cover 56% (i.e. 0.8 * 70%) of the pixel (1, 1) +and 24% (i.e. 0.8 * 30%) of the pixel (1, 2) while +the right one would cover 42% (i.e. 0.6 * 70%) of the +pixel (5, 3) and 18% (i.e. 0.6 * 30%) of the pixel +(5, 4) while the right
+
+
+
+
+
+

Then for each point between the endpoints, along the line, whose +x-coordinate is a whole number:

+
+

Find which pixels would be covered and how much by that point and +draw them.

+
+
e.g.:
+
The points along the line ((1, 1), (4, 2.5)) would be +(2, 1.5) and (3, 2) and would cover 50% of the pixel +(2, 1), 50% of the pixel (2, 2) and 100% of the pixel +(3, 2).
+
The points along the line ((1.2, 1.4), (4.6, 3.1)) would +be (2, 1.8) (covering 20% of the pixel (2, 1) and 80% +of the pixel (2, 2)), (3, 2.3) (covering 70% of the +pixel (3, 2) and 30% of the pixel (3, 3)) and (4, +2.8) (covering 20% of the pixel (2, 1) and 80% of the +pixel (2, 2))
+
+
+
+
+
+

Otherwise do the same for steep lines as for non-steep lines except +along the y-axis instead of the x-axis (using y instead of x, +top instead of left and bottom instead of right).

+
+
+
+

Note

+

Regarding float values for coordinates, a point with coordinate +consisting of two whole numbers is considered being right in the center +of said pixel (and having a height and width of 1 pixel would therefore +completely cover it), while a point with coordinate where one (or both) +of the numbers have non-zero decimal parts would be partially covering +two (or four if both numbers have decimal parts) adjacent pixels, e.g. +the point (1.4, 2) covers 60% of the pixel (1, 2) and 40% of the +pixel (2,2).

+
+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • color (Color or int or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
  • start_pos (tuple(int or float, int or float) or +list(int or float, int or float) or Vector2(int or float, int or float)) -- start position of the line, (x, y)

  • +
  • end_pos (tuple(int or float, int or float) or +list(int or float, int or float) or Vector2(int or float, int or float)) -- end position of the line, (x, y)

  • +
  • blend (int) -- (optional) (deprecated) if non-zero (default) the line will be blended +with the surface's existing pixel shades, otherwise it will overwrite them

  • +
+
+
Returns
+

a rect bounding the changed pixels, if nothing is drawn the +bounding rect's position will be the start_pos parameter value (float +values will be truncated) and its width and height will be 0

+
+
Return type
+

Rect

+
+
Raises
+

TypeError -- if start_pos or end_pos is not a sequence of +two numbers

+
+
+
+

Changed in pygame 2.0.0: Added support for keyword arguments.

+
+
+ +
+
+pygame.draw.aalines()
+
+
draw multiple contiguous straight antialiased line segments
+
aalines(surface, color, closed, points) -> Rect
+
aalines(surface, color, closed, points, blend=1) -> Rect
+
+

Draws a sequence of contiguous straight antialiased lines on the given +surface.

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • color (Color or int or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
  • closed (bool) -- if True an additional line segment is drawn between +the first and last points in the points sequence

  • +
  • points (tuple(coordinate) or list(coordinate)) -- a sequence of 2 or more (x, y) coordinates, where each +coordinate in the sequence must be a +tuple/list/pygame.math.Vector2a 2-Dimensional Vector of 2 ints/floats and adjacent +coordinates will be connected by a line segment, e.g. for the +points [(x1, y1), (x2, y2), (x3, y3)] a line segment will be drawn +from (x1, y1) to (x2, y2) and from (x2, y2) to (x3, y3), +additionally if the closed parameter is True another line segment +will be drawn from (x3, y3) to (x1, y1)

  • +
  • blend (int) -- (optional) (deprecated) if non-zero (default) each line will be blended +with the surface's existing pixel shades, otherwise the pixels will be +overwritten

  • +
+
+
Returns
+

a rect bounding the changed pixels, if nothing is drawn the +bounding rect's position will be the position of the first point in the +points parameter (float values will be truncated) and its width and +height will be 0

+
+
Return type
+

Rect

+
+
Raises
+
    +
  • ValueError -- if len(points) < 2 (must have at least 2 points)

  • +
  • TypeError -- if points is not a sequence or points does not +contain number pairs

  • +
+
+
+
+

Changed in pygame 2.0.0: Added support for keyword arguments.

+
+
+ +
+draw module example +
+

Example code for draw module.

+
+
+
import pygame
+from math import pi
+
+# Initialize pygame
+pygame.init()
+
+# Set the height and width of the screen
+size = [400, 300]
+screen = pygame.display.set_mode(size)
+
+pygame.display.set_caption("Example code for the draw module")
+
+# Loop until the user clicks the close button.
+done = False
+clock = pygame.time.Clock()
+
+while not done:
+    # This limits the while loop to a max of 60 times per second.
+    # Leave this out and we will use all CPU we can.
+    clock.tick(60)
+
+    for event in pygame.event.get():  # User did something
+        if event.type == pygame.QUIT:  # If user clicked close
+            done = True  # Flag that we are done so we exit this loop
+
+    # Clear the screen and set the screen background
+    screen.fill("white")
+
+    # Draw on the screen a green line from (0, 0) to (50, 30)
+    # 5 pixels wide. Uses (r, g, b) color - medium sea green.
+    pygame.draw.line(screen, (60, 179, 113), [0, 0], [50, 30], 5)
+
+    # Draw on the screen a green line from (0, 50) to (50, 80)
+    # Because it is an antialiased line, it is 1 pixel wide.
+    # Uses (r, g, b) color - medium sea green.
+    pygame.draw.aaline(screen, (60, 179, 113), [0, 50], [50, 80], True)
+
+    # Draw on the screen 3 black lines, each 5 pixels wide.
+    # The 'False' means the first and last points are not connected.
+    pygame.draw.lines(
+        screen, "black", False, [[0, 80], [50, 90], [200, 80], [220, 30]], 5
+    )
+
+    # Draw a rectangle outline
+    pygame.draw.rect(screen, "black", [75, 10, 50, 20], 2)
+
+    # Draw a solid rectangle. Same color as "black" above, specified in a new way
+    pygame.draw.rect(screen, (0, 0, 0), [150, 10, 50, 20])
+
+    # Draw a rectangle with rounded corners
+    pygame.draw.rect(screen, "green", [115, 210, 70, 40], 10, border_radius=15)
+    pygame.draw.rect(
+        screen,
+        "red",
+        [135, 260, 50, 30],
+        0,
+        border_radius=10,
+        border_top_left_radius=0,
+        border_bottom_right_radius=15,
+    )
+
+    # Draw an ellipse outline, using a rectangle as the outside boundaries
+    pygame.draw.ellipse(screen, "red", [225, 10, 50, 20], 2)
+
+    # Draw an solid ellipse, using a rectangle as the outside boundaries
+    pygame.draw.ellipse(screen, "red", [300, 10, 50, 20])
+
+    # This draws a triangle using the polygon command
+    pygame.draw.polygon(screen, "black", [[100, 100], [0, 200], [200, 200]], 5)
+
+    # Draw an arc as part of an ellipse.
+    # Use radians to determine what angle to draw.
+    pygame.draw.arc(screen, "black", [210, 75, 150, 125], 0, pi / 2, 2)
+    pygame.draw.arc(screen, "green", [210, 75, 150, 125], pi / 2, pi, 2)
+    pygame.draw.arc(screen, "blue", [210, 75, 150, 125], pi, 3 * pi / 2, 2)
+    pygame.draw.arc(screen, "red", [210, 75, 150, 125], 3 * pi / 2, 2 * pi, 2)
+
+    # Draw a circle
+    pygame.draw.circle(screen, "blue", [60, 250], 40)
+
+    # Draw only one circle quadrant
+    pygame.draw.circle(screen, "blue", [250, 250], 40, 0, draw_top_right=True)
+    pygame.draw.circle(screen, "red", [250, 250], 40, 30, draw_top_left=True)
+    pygame.draw.circle(screen, "green", [250, 250], 40, 20, draw_bottom_left=True)
+    pygame.draw.circle(screen, "black", [250, 250], 40, 10, draw_bottom_right=True)
+
+    # Go ahead and update the screen with what we've drawn.
+    # This MUST happen after all the other drawing commands.
+    pygame.display.flip()
+
+# Be IDLE friendly
+pygame.quit()
+
+
+
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/event.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/event.html new file mode 100644 index 00000000..d4de22d9 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/event.html @@ -0,0 +1,808 @@ + + + + + + + + + pygame.event — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.event
+
+
pygame module for interacting with events and queues
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+internally process pygame event handlers
+get events from the queue
+get a single event from the queue
+wait for a single event from the queue
+test if event types are waiting on the queue
+remove all events from the queue
+get the string name from an event id
+control which events are allowed on the queue
+control which events are allowed on the queue
+test if a type of event is blocked from the queue
+control the sharing of input devices with other applications
+test if the program is sharing input devices
+grab enables capture of system keyboard shortcuts like Alt+Tab or the Meta/Super key.
+get the current keyboard grab state
+place a new event on the queue
+make custom user event type
+pygame object for representing events
+

Pygame handles all its event messaging through an event queue. The routines in +this module help you manage that event queue. The input queue is heavily +dependent on the pygame.displaypygame module to control the display window and screen module. If the display has not been +initialized and a video mode not set, the event queue may not work properly.

+

The event queue has an upper limit on the number of events it can hold. When +the queue becomes full new events are quietly dropped. To prevent lost events, +especially input events which signal a quit command, your program must handle +events every frame (with pygame.event.get(), pygame.event.pump(), +pygame.event.wait(), pygame.event.peek() or pygame.event.clear()) +and process them. Not handling events may cause your system to decide your +program has locked up. To speed up queue processing use +pygame.event.set_blocked()control which events are allowed on the queue to limit which events get queued.

+

To get the state of various input devices, you can forego the event queue and +access the input devices directly with their appropriate modules: +pygame.mousepygame module to work with the mouse, pygame.keypygame module to work with the keyboard, and pygame.joystickPygame module for interacting with joysticks, gamepads, and trackballs.. If you use +this method, remember that pygame requires some form of communication with the +system window manager and other parts of the platform. To keep pygame in sync +with the system, you will need to call pygame.event.pump()internally process pygame event handlers to keep +everything current. Usually, this should be called once per game loop. +Note: Joysticks will not send any events until the device has been initialized.

+

The event queue contains pygame.event.Eventpygame object for representing events event objects. +There are a variety of ways to access the queued events, from simply +checking for the existence of events, to grabbing them directly off the stack. +The event queue also offers some simple filtering which can slightly help +performance by blocking certain event types from the queue. Use +pygame.event.set_allowed()control which events are allowed on the queue and pygame.event.set_blocked()control which events are allowed on the queue to +change this filtering. By default, all event types can be placed on the queue.

+

All pygame.event.Eventpygame object for representing events instances contain an event type identifier +and attributes specific to that event type. The event type identifier is +accessible as the pygame.event.Event.typeevent type identifier. property. Any of the +event specific attributes can be accessed through the +pygame.event.Event.__dict__event attribute dictionary attribute or directly as an attribute +of the event object (as member lookups are passed through to the object's +dictionary values). The event object has no method functions. Users can create +their own new events with the pygame.event.Event()pygame object for representing events function.

+

The event type identifier is in between the values of NOEVENT and +NUMEVENTS. User defined events should have a value in the inclusive range +of USEREVENT to NUMEVENTS - 1. User defined events can get a custom +event number with pygame.event.custom_type()make custom user event type. +It is recommended all user events follow this system.

+

Events support equality and inequality comparisons. Two events are equal if +they are the same type and have identical attribute values.

+

While debugging and experimenting, you can print an event object for a quick +display of its type and members. The function pygame.event.event_name()get the string name from an event id +can be used to get a string representing the name of the event type.

+

Events that come from the system will have a guaranteed set of member +attributes based on the type. The following is a list event types with their +specific attributes.

+
QUIT              none
+ACTIVEEVENT       gain, state
+KEYDOWN           key, mod, unicode, scancode
+KEYUP             key, mod, unicode, scancode
+MOUSEMOTION       pos, rel, buttons, touch
+MOUSEBUTTONUP     pos, button, touch
+MOUSEBUTTONDOWN   pos, button, touch
+JOYAXISMOTION     joy (deprecated), instance_id, axis, value
+JOYBALLMOTION     joy (deprecated), instance_id, ball, rel
+JOYHATMOTION      joy (deprecated), instance_id, hat, value
+JOYBUTTONUP       joy (deprecated), instance_id, button
+JOYBUTTONDOWN     joy (deprecated), instance_id, button
+VIDEORESIZE       size, w, h
+VIDEOEXPOSE       none
+USEREVENT         code
+
+
+
+

Changed in pygame 2.0.0: The joy attribute was deprecated, instance_id was added.

+
+
+

Changed in pygame 2.0.1: The unicode attribute was added to KEYUP event.

+
+

Note that ACTIVEEVENT, VIDEORESIZE and VIDEOEXPOSE are considered +as "legacy" events, the use of pygame2 WINDOWEVENT API is recommended over +the use of this older API.

+

You can also find a list of constants for keyboard keys +here.

+

A keyboard event occurs when a key is pressed (KEYDOWN) and when a key is released (KEYUP) +The key attribute of keyboard events contains the value of what key was pressed or released. +The mod attribute contains information about the state of keyboard modifiers (SHIFT, CTRL, ALT, etc.). +The unicode attribute stores the 16-bit unicode value of the key that was pressed or released. +The scancode attribute represents the physical location of a key on the keyboard.

+

The ACTIVEEVENT contains information about the application gaining or losing focus. The gain attribute +will be 1 if the mouse enters the window, otherwise gain will be 0. The state attribute will have a +value of SDL_APPMOUSEFOCUS if mouse focus was gained/lost, SDL_APPINPUTFOCUS if the application loses +or gains keyboard focus, or SDL_APPACTIVE if the application is minimized (gain will be 0) or restored.

+
+

+
+

When compiled with SDL2, pygame has these additional events and their +attributes.

+
AUDIODEVICEADDED   which, iscapture (SDL backend >= 2.0.4)
+AUDIODEVICEREMOVED which, iscapture (SDL backend >= 2.0.4)
+FINGERMOTION       touch_id, finger_id, x, y, dx, dy
+FINGERDOWN         touch_id, finger_id, x, y, dx, dy
+FINGERUP           touch_id, finger_id, x, y, dx, dy
+MOUSEWHEEL         which, flipped, x, y, touch, precise_x, precise_y
+MULTIGESTURE       touch_id, x, y, pinched, rotated, num_fingers
+TEXTEDITING        text, start, length
+TEXTINPUT          text
+
+
+
+

New in pygame 1.9.5.

+
+
+

Changed in pygame 2.0.2: Fixed amount horizontal scroll (x, positive to the right and negative to the left).

+
+
+

Changed in pygame 2.0.2: The touch attribute was added to all the MOUSE events.

+
+

The touch attribute of MOUSE events indicates whether or not the events were generated +by a touch input device, and not a real mouse. You might want to ignore such events, if your application +already handles FINGERMOTION, FINGERDOWN and FINGERUP events.

+
+

New in pygame 2.1.3: Added precise_x and precise_y to MOUSEWHEEL events

+
+

MOUSEWHEEL event occurs whenever the mouse wheel is moved. +The which attribute determines if the event was generated from a touch input device vs an actual +mousewheel. +The preciseX attribute contains a float with the amount scrolled horizontally (positive to the right, +negative to the left). +The preciseY attribute contains a float with the amount scrolled vertically (positive away from user, +negative towards user). +The flipped attribute determines if the values in x and y will be opposite or not. If SDL_MOUSEWHEEL_FLIPPED +is defined, the direction of x and y will be opposite.

+

TEXTEDITING event is triggered when a user activates an input method via hotkey or selecting an +input method in a GUI and starts typing

+

The which attribute for AUDIODEVICE* events is an integer representing the index for new audio +devices that are added. AUDIODEVICE* events are used to update audio settings or device list.

+
+

+
+

Many new events were introduced in pygame 2.

+

pygame can recognize text or files dropped in its window. If a file +is dropped, DROPFILE event will be sent, file will be its path. +The DROPTEXT event is only supported on X11.

+

MIDIIN and MIDIOUT are events reserved for pygame.midipygame module for interacting with midi input and output. use. +MIDI* events differ from AUDIODEVICE* events in that AUDIODEVICE +events are triggered when there is a state change related to an audio +input/output device.

+

pygame 2 also supports controller hot-plugging

+
Event name               Attributes and notes
+
+DROPFILE                 file
+DROPBEGIN                (SDL backend >= 2.0.5)
+DROPCOMPLETE             (SDL backend >= 2.0.5)
+DROPTEXT                 text (SDL backend >= 2.0.5)
+MIDIIN
+MIDIOUT
+CONTROLLERDEVICEADDED    device_index
+JOYDEVICEADDED           device_index
+CONTROLLERDEVICEREMOVED  instance_id
+JOYDEVICEREMOVED         instance_id
+CONTROLLERDEVICEREMAPPED instance_id
+KEYMAPCHANGED            (SDL backend >= 2.0.4)
+CLIPBOARDUPDATE
+RENDER_TARGETS_RESET     (SDL backend >= 2.0.2)
+RENDER_DEVICE_RESET      (SDL backend >= 2.0.4)
+LOCALECHANGED            (SDL backend >= 2.0.14)
+
+
+

Also in this version, instance_id attributes were added to joystick events, +and the joy attribute was deprecated.

+

KEYMAPCHANGED is a type of an event sent when keymap changes due to a +system event such as an input language or keyboard layout change.

+

CLIPBOARDUPDATE is an event sent when clipboard changes. This can still +be considered as an experimental feature, some kinds of clipboard changes might +not trigger this event.

+

LOCALECHANGED is an event sent when user locale changes

+
+

New in pygame 2.0.0.

+
+
+

New in pygame 2.1.3: KEYMAPCHANGED, CLIPBOARDUPDATE, +RENDER_TARGETS_RESET, RENDER_DEVICE_RESET and LOCALECHANGED

+
+
+

+
+

Since pygame 2.0.1, there are a new set of events, called window events. +Here is a list of all window events, along with a short description

+
Event type                Short description
+
+WINDOWSHOWN            Window became shown
+WINDOWHIDDEN           Window became hidden
+WINDOWEXPOSED          Window got updated by some external event
+WINDOWMOVED            Window got moved
+WINDOWRESIZED          Window got resized
+WINDOWSIZECHANGED      Window changed its size
+WINDOWMINIMIZED        Window was minimized
+WINDOWMAXIMIZED        Window was maximized
+WINDOWRESTORED         Window was restored
+WINDOWENTER            Mouse entered the window
+WINDOWLEAVE            Mouse left the window
+WINDOWFOCUSGAINED      Window gained focus
+WINDOWFOCUSLOST        Window lost focus
+WINDOWCLOSE            Window was closed
+WINDOWTAKEFOCUS        Window was offered focus (SDL backend >= 2.0.5)
+WINDOWHITTEST          Window has a special hit test (SDL backend >= 2.0.5)
+WINDOWICCPROFCHANGED   Window ICC profile changed (SDL backend >= 2.0.18)
+WINDOWDISPLAYCHANGED   Window moved on a new display (SDL backend >= 2.0.18)
+
+
+

WINDOWMOVED, WINDOWRESIZED and WINDOWSIZECHANGED have x and +y attributes, WINDOWDISPLAYCHANGED has a display_index attribute. +All windowevents have a window attribute.

+
+

New in pygame 2.0.1.

+
+
+

New in pygame 2.1.3: WINDOWICCPROFCHANGED and WINDOWDISPLAYCHANGED

+
+
+

+
+

On Android, the following events can be generated

+
Event type                 Short description
+
+APP_TERMINATING           OS is terminating the application
+APP_LOWMEMORY             OS is low on memory, try to free memory if possible
+APP_WILLENTERBACKGROUND   Application is entering background
+APP_DIDENTERBACKGROUND    Application entered background
+APP_WILLENTERFOREGROUND   Application is entering foreground
+APP_DIDENTERFOREGROUND    Application entered foreground
+
+
+
+

New in pygame 2.1.3.

+
+
+

+
+
+
+pygame.event.pump()
+
+
internally process pygame event handlers
+
pump() -> None
+
+

For each frame of your game, you will need to make some sort of call to the +event queue. This ensures your program can internally interact with the rest +of the operating system. If you are not using other event functions in your +game, you should call pygame.event.pump() to allow pygame to handle +internal actions.

+

This function is not necessary if your program is consistently processing +events on the queue through the other pygame.eventpygame module for interacting with events and queues functions.

+

There are important things that must be dealt with internally in the event +queue. The main window may need to be repainted or respond to the system. If +you fail to make a call to the event queue for too long, the system may +decide your program has locked up.

+
+

Caution

+

This function should only be called in the thread that initialized pygame.displaypygame module to control the display window and screen.

+
+
+ +
+
+pygame.event.get()
+
+
get events from the queue
+
get(eventtype=None) -> Eventlist
+
get(eventtype=None, pump=True) -> Eventlist
+
get(eventtype=None, pump=True, exclude=None) -> Eventlist
+
+

This will get all the messages and remove them from the queue. If a type or +sequence of types is given only those messages will be removed from the +queue and returned.

+

If a type or sequence of types is passed in the exclude argument +instead, then all only other messages will be removed from the queue. If +an exclude parameter is passed, the eventtype parameter must be +None.

+

If you are only taking specific events from the queue, be aware that the +queue could eventually fill up with the events you are not interested.

+

If pump is True (the default), then pygame.event.pump()internally process pygame event handlers will be called.

+
+

Changed in pygame 1.9.5: Added pump argument

+
+
+

Changed in pygame 2.0.2: Added exclude argument

+
+
+ +
+
+pygame.event.poll()
+
+
get a single event from the queue
+
poll() -> Event instance
+
+

Returns a single event from the queue. If the event queue is empty an event +of type pygame.NOEVENT will be returned immediately. The returned event +is removed from the queue.

+
+

Caution

+

This function should only be called in the thread that initialized pygame.displaypygame module to control the display window and screen.

+
+
+ +
+
+pygame.event.wait()
+
+
wait for a single event from the queue
+
wait() -> Event instance
+
wait(timeout) -> Event instance
+
+

Returns a single event from the queue. If the queue is empty this function +will wait until one is created. From pygame 2.0.0, if a timeout argument +is given, the function will return an event of type pygame.NOEVENT +if no events enter the queue in timeout milliseconds. The event is removed +from the queue once it has been returned. While the program is waiting it will +sleep in an idle state. This is important for programs that want to share the +system with other applications.

+
+

Changed in pygame 2.0.0.dev13: Added timeout argument

+
+
+

Caution

+

This function should only be called in the thread that initialized pygame.displaypygame module to control the display window and screen.

+
+
+ +
+
+pygame.event.peek()
+
+
test if event types are waiting on the queue
+
peek(eventtype=None) -> bool
+
peek(eventtype=None, pump=True) -> bool
+
+

Returns True if there are any events of the given type waiting on the +queue. If a sequence of event types is passed, this will return True if +any of those events are on the queue.

+

If pump is True (the default), then pygame.event.pump()internally process pygame event handlers will be called.

+
+

Changed in pygame 1.9.5: Added pump argument

+
+
+ +
+
+pygame.event.clear()
+
+
remove all events from the queue
+
clear(eventtype=None) -> None
+
clear(eventtype=None, pump=True) -> None
+
+

Removes all events from the queue. If eventtype is given, removes the given event +or sequence of events. This has the same effect as pygame.event.get()get events from the queue except None +is returned. It can be slightly more efficient when clearing a full event queue.

+

If pump is True (the default), then pygame.event.pump()internally process pygame event handlers will be called.

+
+

Changed in pygame 1.9.5: Added pump argument

+
+
+ +
+
+pygame.event.event_name()
+
+
get the string name from an event id
+
event_name(type) -> string
+
+

Returns a string representing the name (in CapWords style) of the given +event type.

+

"UserEvent" is returned for all values in the user event id range. +"Unknown" is returned when the event type does not exist.

+
+

Changed in pygame 2.5.0: Added support for keyword arguments.

+
+
+ +
+
+pygame.event.set_blocked()
+
+
control which events are allowed on the queue
+
set_blocked(type) -> None
+
set_blocked(typelist) -> None
+
set_blocked(None) -> None
+
+

The given event types are not allowed to appear on the event queue. By +default all events can be placed on the queue. It is safe to disable an +event type multiple times.

+

If None is passed as the argument, ALL of the event types are blocked +from being placed on the queue.

+
+ +
+
+pygame.event.set_allowed()
+
+
control which events are allowed on the queue
+
set_allowed(type) -> None
+
set_allowed(typelist) -> None
+
set_allowed(None) -> None
+
+

The given event types are allowed to appear on the event queue. By default, +all event types can be placed on the queue. It is safe to enable an event +type multiple times.

+

If None is passed as the argument, ALL of the event types are allowed +to be placed on the queue.

+
+ +
+
+pygame.event.get_blocked()
+
+
test if a type of event is blocked from the queue
+
get_blocked(type) -> bool
+
get_blocked(typelist) -> bool
+
+

Returns True if the given event type is blocked from the queue. If a +sequence of event types is passed, this will return True if any of those +event types are blocked.

+
+ +
+
+pygame.event.set_grab()
+
+
control the sharing of input devices with other applications
+
set_grab(bool) -> None
+
+

When your program runs in a windowed environment, it will share the mouse +and keyboard devices with other applications that have focus. If your +program sets the event grab to True, it will lock all input into your +program.

+

It is best to not always grab the input, since it prevents the user from +doing other things on their system.

+
+ +
+
+pygame.event.get_grab()
+
+
test if the program is sharing input devices
+
get_grab() -> bool
+
+

Returns True when the input events are grabbed for this application.

+
+ +
+
+pygame.event.set_keyboard_grab()
+
+
grab enables capture of system keyboard shortcuts like Alt+Tab or the Meta/Super key.
+
set_keyboard_grab(bool) -> None
+
+

Keyboard grab enables capture of system keyboard shortcuts like Alt+Tab or the Meta/Super key. +Note that not all system keyboard shortcuts can be captured by applications (one example is Ctrl+Alt+Del on Windows). +This is primarily intended for specialized applications such as VNC clients or VM frontends. Normal games should not use keyboard grab.

+
+

New in pygame 2.5.0.

+
+
+ +
+
+pygame.event.get_keyboard_grab()
+
+
get the current keyboard grab state
+
get_keyboard_grab() -> bool
+
+

Returns True when keyboard grab is enabled.

+
+

New in pygame 2.5.0.

+
+
+ +
+
+pygame.event.post()
+
+
place a new event on the queue
+
post(Event) -> bool
+
+

Places the given event at the end of the event queue.

+

This is usually used for placing custom events on the event queue. +Any type of event can be posted, and the events posted can have any attributes.

+

This returns a boolean on whether the event was posted or not. Blocked events +cannot be posted, and this function returns False if you try to post them.

+
+

Changed in pygame 2.0.1: returns a boolean, previously returned None

+
+
+ +
+
+pygame.event.custom_type()
+
+
make custom user event type
+
custom_type() -> int
+
+

Reserves a pygame.USEREVENT for a custom use.

+

If too many events are made a pygame.errorstandard pygame exception is raised.

+
+

New in pygame 2.0.0.dev3.

+
+
+ +
+
+pygame.event.Event
+
+
pygame object for representing events
+
Event(type, dict) -> Event
+
Event(type, **attributes) -> Event
+
+ +++++ + + + + + + + + + + +
+event type identifier.
+event attribute dictionary
+

A pygame object used for representing an event. Event instances +support attribute assignment and deletion.

+

When creating the object, the attributes may come from a dictionary +argument with string keys or from keyword arguments.

+
+

Note

+

From version 2.1.3 EventType is an alias for Event. Beforehand, +Event was a function that returned EventType instances. Use of +Event is preferred over EventType wherever it is possible, as +the latter could be deprecated in a future version.

+
+
+
+type
+
+
event type identifier.
+
type -> int
+
+

Read-only. The event type identifier. For user created event +objects, this is the type argument passed to +pygame.event.Event()pygame object for representing events.

+

For example, some predefined event identifiers are QUIT and +MOUSEMOTION.

+
+ +
+
+__dict__
+
+
event attribute dictionary
+
__dict__ -> dict
+
+

Read-only. The event type specific attributes of an event. The +dict attribute is a synonym for backward compatibility.

+

For example, the attributes of a KEYDOWN event would be unicode, +key, and mod

+
+ +
+

New in pygame 1.9.2: Mutable attributes.

+
+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/examples.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/examples.html new file mode 100644 index 00000000..f9c5fac9 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/examples.html @@ -0,0 +1,710 @@ + + + + + + + + + pygame.examples — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.examples
+
+
module of example programs
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+play the full aliens example
+run a simple starfield example
+hit the moving chimp
+display animated objects on the screen
+run a font rendering example
+run a FreeType rendering example
+display a vertical gradient
+display pygame events
+show various surfarray effects
+load and play a sound
+play various sndarray effects
+display an animated liquid effect
+display an animated 3D cube using OpenGL
+access the clipboard
+display multiple images bounce off each other using collision detection
+show lots of sprites moving around
+write an image file that is smoothscaled copy of an input file
+demonstrate joystick functionality
+demonstrate the various surface.fill method blend options
+uses alternative additive fill to that of surface.fill
+display two different custom cursors
+display various pixelarray generated effects
+interactively scale an image using smoothscale
+run a midi example
+run a Surface.scroll example that shows a magnified image
+display video captured live from an attached camera
+play an audio file
+

These examples should help get you started with pygame. Here is a brief rundown +of what you get. The source code for these examples is in the public domain. +Feel free to use for your own projects.

+

There are several ways to run the examples. First they can be run as +stand-alone programs. Second they can be imported and their main() methods +called (see below). Finally, the easiest way is to use the python -m option:

+
python -m pygame.examples.<example name> <example arguments>
+
+
+

eg:

+
python -m pygame.examples.scaletest someimage.png
+
+
+

Resources such as images and sounds for the examples are found in the +pygame/examples/data subdirectory.

+

You can find where the example files are installed by using the following +commands inside the python interpreter.

+
>>> import pygame.examples.scaletest
+>>> pygame.examples.scaletest.__file__
+'/usr/lib/python2.6/site-packages/pygame/examples/scaletest.py'
+
+
+

On each OS and version of Python the location will be slightly different. +For example on Windows it might be in 'C:/Python26/Lib/site-packages/pygame/examples/' +On Mac OS X it might be in '/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/pygame/examples/'

+

You can also run the examples in the python interpreter by calling each modules main() function.

+
>>> import pygame.examples.scaletest
+>>> pygame.examples.scaletest.main()
+
+
+

We're always on the lookout for more examples and/or example requests. Code +like this is probably the best way to start getting involved with python +gaming.

+

examples as a package is new to pygame 1.9.0. But most of the examples came with +pygame much earlier.

+
+
+aliens.main()
+
+
play the full aliens example
+
aliens.main() -> None
+
+

This started off as a port of the SDL demonstration, Aliens. Now it has +evolved into something sort of resembling fun. This demonstrates a lot of +different uses of sprites and optimized blitting. Also transparency, +colorkeys, fonts, sound, music, joystick, and more. (PS, my high score is +117! goodluck)

+
+ +
+
+stars.main()
+
+
run a simple starfield example
+
stars.main() -> None
+
+

A simple starfield example. You can change the center of perspective by +leftclicking the mouse on the screen.

+
+ +
+
+chimp.main()
+
+
hit the moving chimp
+
chimp.main() -> None
+
+

This simple example is derived from the line-by-line tutorial that comes +with pygame. It is based on a 'popular' web banner. Note there are comments +here, but for the full explanation, follow along in the tutorial.

+
+ +
+
+moveit.main()
+
+
display animated objects on the screen
+
moveit.main() -> None
+
+

This is the full and final example from the Pygame Tutorial, "How Do I Make +It Move". It creates 10 objects and animates them on the screen.

+

Note it's a bit scant on error checking, but it's easy to read. :] +Fortunately, this is python, and we needn't wrestle with a pile of error +codes.

+
+ +
+
+fonty.main()
+
+
run a font rendering example
+
fonty.main() -> None
+
+

Super quick, super simple application demonstrating the different ways to +render fonts with the font module

+
+ +
+
+freetype_misc.main()
+
+
run a FreeType rendering example
+
freetype_misc.main() -> None
+
+

A showcase of rendering features the pygame.freetype.FontCreate a new Font instance from a supported font file. +class provides in addition to those available with pygame.font.Fontcreate a new Font object from a file. +It is a demonstration of direct to surface rendering, with vertical text +and rotated text, opaque text and semi transparent text, horizontally +stretched text and vertically stretched text.

+
+ +
+
+vgrade.main()
+
+
display a vertical gradient
+
vgrade.main() -> None
+
+

Demonstrates creating a vertical gradient with pixelcopy and NumPy python. +The app will create a new gradient every half second and report the time +needed to create and display the image. If you're not prepared to start +working with the NumPy arrays, don't worry about the source for this one :]

+
+ +
+
+eventlist.main()
+
+
display pygame events
+
eventlist.main() -> None
+
+

Eventlist is a sloppy style of pygame, but is a handy tool for learning +about pygame events and input. At the top of the screen are the state of +several device values, and a scrolling list of events are displayed on the +bottom.

+

This is not quality 'ui' code at all, but you can see how to implement very +non-interactive status displays, or even a crude text output control.

+
+ +
+
+arraydemo.main()
+
+
show various surfarray effects
+
arraydemo.main(arraytype=None) -> None
+
+

Another example filled with various surfarray effects. It requires the +surfarray and image modules to be installed. This little demo can also make +a good starting point for any of your own tests with surfarray

+

The arraytype parameter is deprecated; passing any value besides 'numpy' +will raise ValueError.

+
+ +
+
+sound.main()
+
+
load and play a sound
+
sound.main(file_path=None) -> None
+
+

Extremely basic testing of the mixer module. Load a sound and play it. All +from the command shell, no graphics.

+

If provided, use the audio file 'file_path', otherwise use a default file.

+

sound.py optional command line argument: an audio file

+
+ +
+
+sound_array_demos.main()
+
+
play various sndarray effects
+
sound_array_demos.main(arraytype=None) -> None
+
+

Uses sndarray and NumPy to create offset faded copies of the +original sound. Currently it just uses hardcoded values for the number of +echoes and the delay. Easy for you to recreate as needed.

+

The arraytype parameter is deprecated; passing any value besides 'numpy' +will raise ValueError.

+
+ +
+
+liquid.main()
+
+
display an animated liquid effect
+
liquid.main() -> None
+
+

This example was created in a quick comparison with the BlitzBasic gaming +language. Nonetheless, it demonstrates a quick 8-bit setup (with colormap).

+
+ +
+
+glcube.main()
+
+
display an animated 3D cube using OpenGL
+
glcube.main() -> None
+
+

Using PyOpenGL and pygame, this creates a spinning 3D multicolored cube.

+
+ +
+
+scrap_clipboard.main()
+
+
access the clipboard
+
scrap_clipboard.main() -> None
+
+

A simple demonstration example for the clipboard support.

+
+ +
+
+mask.main()
+
+
display multiple images bounce off each other using collision detection
+
mask.main(*args) -> None
+
+

Positional arguments:

+
one or more image file names.
+
+
+

This pygame.masks demo will display multiple moving sprites bouncing off +each other. More than one sprite image can be provided.

+

If run as a program then mask.py takes one or more image files as +command line arguments.

+
+ +
+
+testsprite.main()
+
+
show lots of sprites moving around
+
testsprite.main(update_rects = True, use_static = False, use_FastRenderGroup = False, screen_dims = [640, 480], use_alpha = False, flags = 0) -> None
+
+

Optional keyword arguments:

+
update_rects - use the RenderUpdate sprite group class
+use_static - include non-moving images
+use_FastRenderGroup - Use the FastRenderGroup sprite group
+screen_dims - pygame window dimensions
+use_alpha - use alpha blending
+flags - additional display mode flags
+
+
+

Like the testsprite.c that comes with SDL, this pygame version shows +lots of sprites moving around.

+

If run as a stand-alone program then no command line arguments are taken.

+
+ +
+
+headless_no_windows_needed.main()
+
+
write an image file that is smoothscaled copy of an input file
+
headless_no_windows_needed.main(fin, fout, w, h) -> None
+
+

arguments:

+
fin - name of an input image file
+fout - name of the output file to create/overwrite
+w, h - size of the rescaled image, as integer width and height
+
+
+

How to use pygame with no windowing system, like on headless servers.

+

Thumbnail generation with scaling is an example of what you can do with +pygame.

+

NOTE: the pygame scale function uses MMX/SSE if available, and can be +run in multiple threads.

+

If headless_no_windows_needed.py is run as a program it takes the +following command line arguments:

+
-scale inputimage outputimage new_width new_height
+eg. -scale in.png outpng 50 50
+
+
+
+ +
+
+joystick.main()
+
+
demonstrate joystick functionality
+
joystick.main() -> None
+
+

A demo showing full joystick support.

+
+

New in pygame 2.0.2.

+
+
+ +
+
+blend_fill.main()
+
+
demonstrate the various surface.fill method blend options
+
blend_fill.main() -> None
+
+

A interactive demo that lets one choose which BLEND_xxx option to apply to a +surface.

+
+ +
+
+blit_blends.main()
+
+
uses alternative additive fill to that of surface.fill
+
blit_blends.main() -> None
+
+

Fake additive blending. Using NumPy. it doesn't clamp. Press r,g,b Somewhat +like blend_fill.

+
+ +
+
+cursors.main()
+
+
display two different custom cursors
+
cursors.main() -> None
+
+

Display an arrow or circle with crossbar cursor.

+
+ +
+
+pixelarray.main()
+
+
display various pixelarray generated effects
+
pixelarray.main() -> None
+
+

Display various pixelarray generated effects.

+
+ +
+
+scaletest.main()
+
+
interactively scale an image using smoothscale
+
scaletest.main(imagefile, convert_alpha=False, run_speed_test=True) -> None
+
+

arguments:

+
imagefile - file name of source image (required)
+convert_alpha - use convert_alpha() on the surf (default False)
+run_speed_test - (default False)
+
+
+

A smoothscale example that resized an image on the screen. Vertical and +horizontal arrow keys are used to change the width and height of the +displayed image. If the convert_alpha option is True then the source image +is forced to have source alpha, whether or not the original images does. If +run_speed_test is True then a background timing test is performed instead of +the interactive scaler.

+

If scaletest.py is run as a program then the command line options are:

+
ImageFile [-t] [-convert_alpha]
+[-t] = Run Speed Test
+[-convert_alpha] = Use convert_alpha() on the surf.
+
+
+
+ +
+
+midi.main()
+
+
run a midi example
+
midi.main(mode='output', device_id=None) -> None
+
+

Arguments:

+
mode - if 'output' run a midi keyboard output example
+          'input' run a midi event logger input example
+          'list' list available midi devices
+       (default 'output')
+device_id - midi device number; if None then use the default midi input or
+            output device for the system
+
+
+

The output example shows how to translate mouse clicks or computer keyboard +events into midi notes. It implements a rudimentary button widget and state +machine.

+

The input example shows how to translate midi input to pygame events.

+

With the use of a virtual midi patch cord the output and input examples can +be run as separate processes and connected so the keyboard output is +displayed on a console.

+

new to pygame 1.9.0

+
+ +
+
+scroll.main()
+
+
run a Surface.scroll example that shows a magnified image
+
scroll.main(image_file=None) -> None
+
+

This example shows a scrollable image that has a zoom factor of eight. It +uses the Surface.scroll() +function to shift the image on the display surface. +A clip rectangle protects a margin area. If called as a function, +the example accepts an optional image file path. If run as a program it +takes an optional file path command line argument. If no file is provided a +default image file is used.

+

When running click on a black triangle to move one pixel in the direction +the triangle points. Or use the arrow keys. Close the window or press +ESC to quit.

+
+ +
+
+camera.main()
+
+
display video captured live from an attached camera
+
camera.main() -> None
+
+

A simple live video player, it uses the first available camera it finds on +the system.

+
+ +
+
+playmus.main()
+
+
play an audio file
+
playmus.main(file_path) -> None
+
+

A simple music player with window and keyboard playback control. Playback can +be paused and rewound to the beginning.

+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/fastevent.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/fastevent.html new file mode 100644 index 00000000..830021bc --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/fastevent.html @@ -0,0 +1,284 @@ + + + + + + + + + pygame.fastevent — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.fastevent
+
+
pygame module for interacting with events and queues
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+initialize pygame.fastevent
+returns True if the fastevent module is currently initialized
+internally process pygame event handlers
+wait for an event
+get an available event
+get all events from the queue
+place an event on the queue
+

IMPORTANT NOTE: THIS MODULE IS DEPRECATED IN PYGAME 2.2

+

In older pygame versions before pygame 2, pygame.eventpygame module for interacting with events and queues was not well +suited for posting events from different threads. This module served as a +replacement (with less features) for multithreaded use. Now, the usage of this +module is highly discouraged in favour of use of the main pygame.eventpygame module for interacting with events and queues +module. This module will be removed in a future pygame version.

+

Below, the legacy docs of the module is provided

+
+
+pygame.fastevent.init()
+
+
initialize pygame.fastevent
+
init() -> None
+
+

Initialize the pygame.fastevent module.

+
+ +
+
+pygame.fastevent.get_init()
+
+
returns True if the fastevent module is currently initialized
+
get_init() -> bool
+
+

Returns True if the pygame.fastevent module is currently initialized.

+
+ +
+
+pygame.fastevent.pump()
+
+
internally process pygame event handlers
+
pump() -> None
+
+

For each frame of your game, you will need to make some sort of call to the +event queue. This ensures your program can internally interact with the rest +of the operating system.

+

This function is not necessary if your program is consistently processing +events on the queue through the other pygame.fasteventpygame module for interacting with events and queues functions.

+

There are important things that must be dealt with internally in the event +queue. The main window may need to be repainted or respond to the system. If +you fail to make a call to the event queue for too long, the system may +decide your program has locked up.

+
+ +
+
+pygame.fastevent.wait()
+
+
wait for an event
+
wait() -> Event
+
+

Returns the current event on the queue. If there are no messages +waiting on the queue, this will not return until one is available. +Sometimes it is important to use this wait to get events from the queue, +it will allow your application to idle when the user isn't doing anything +with it.

+
+ +
+
+pygame.fastevent.poll()
+
+
get an available event
+
poll() -> Event
+
+

Returns next event on queue. If there is no event waiting on the queue, +this will return an event with type NOEVENT.

+
+ +
+
+pygame.fastevent.get()
+
+
get all events from the queue
+
get() -> list of Events
+
+

This will get all the messages and remove them from the queue.

+
+ +
+
+pygame.fastevent.post()
+
+
place an event on the queue
+
post(Event) -> None
+
+

This will post your own event objects onto the event queue. You can post +any event type you want, but some care must be taken. For example, if you +post a MOUSEBUTTONDOWN event to the queue, it is likely any code receiving +the event will expect the standard MOUSEBUTTONDOWN attributes to be +available, like 'pos' and 'button'.

+

Because pygame.fastevent.post() may have to wait for the queue to empty, +you can get into a dead lock if you try to append an event on to a full +queue from the thread that processes events. For that reason I do not +recommend using this function in the main thread of an SDL program.

+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/font.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/font.html new file mode 100644 index 00000000..f8d1fe96 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/font.html @@ -0,0 +1,829 @@ + + + + + + + + + pygame.font — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.font
+
+
pygame module for loading and rendering fonts
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+initialize the font module
+uninitialize the font module
+true if the font module is initialized
+get the filename of the default font
+gets SDL_ttf version
+get all available fonts
+find a specific font on the system
+create a Font object from the system fonts
+create a new Font object from a file
+

The font module allows for rendering TrueType fonts into Surface objects. +This module is built on top of the SDL_ttf library, which comes with all +normal pygame installations.

+

Most of the work done with fonts are done by using the actual Font objects. +The module by itself only has routines to support the creation of Font objects +with pygame.font.Font()create a new Font object from a file.

+

You can load fonts from the system by using the pygame.font.SysFont()create a Font object from the system fonts +function. There are a few other functions to help look up the system fonts.

+

Pygame comes with a builtin default font, freesansbold. This can always be +accessed by passing None as the font name.

+

Before pygame 2.0.3, pygame.font accepts any UCS-2 / UTF-16 character +('\u0001' to '\uFFFF'). After 2.0.3, pygame.font built with SDL_ttf +2.0.15 accepts any valid UCS-4 / UTF-32 character +(like emojis, if the font has them) ('\U00000001' to '\U0010FFFF')). +More about this in Font.render().

+

Before pygame 2.0.3, this character space restriction can be avoided by +using the pygame.freetypeEnhanced pygame module for loading and rendering computer fonts based pygame.ftfont to emulate the Font +module. This can be used by defining the environment variable PYGAME_FREETYPE +before the first import of pygamethe top level pygame package. Since the problem pygame.ftfont +solves no longer exists, it will likely be removed in the future.

+
+
+pygame.font.init()
+
+
initialize the font module
+
init() -> None
+
+

This method is called automatically by pygame.init(). It initializes the +font module. The module must be initialized before any other functions will +work.

+

It is safe to call this function more than once.

+
+ +
+
+pygame.font.quit()
+
+
uninitialize the font module
+
quit() -> None
+
+

Manually uninitialize SDL_ttf's font system. This is called automatically by +pygame.quit().

+

It is safe to call this function even if font is currently not initialized.

+
+ +
+
+pygame.font.get_init()
+
+
true if the font module is initialized
+
get_init() -> bool
+
+

Test if the font module is initialized or not.

+
+ +
+
+pygame.font.get_default_font()
+
+
get the filename of the default font
+
get_default_font() -> string
+
+

Return the filename of the system font. This is not the full path to the +file. This file can usually be found in the same directory as the font +module, but it can also be bundled in separate archives.

+
+ +
+
+pygame.font.get_sdl_ttf_version()
+
+
gets SDL_ttf version
+
get_sdl_ttf_version(linked=True) -> (major, minor, patch)
+
+

Experimental: feature still in development available for testing and feedback. It may change. +Please leave get_sdl_ttf_version feedback with authors

+

Returns a tuple of integers that identify SDL_ttf's version. +SDL_ttf is the underlying font rendering library, written in C, +on which pygame's font module depends. If 'linked' is True (the default), +the function returns the version of the linked TTF library. +Otherwise this function returns the version of TTF pygame was compiled with

+
+

New in pygame 2.1.3.

+
+
+ +
+
+pygame.font.get_fonts()
+
+
get all available fonts
+
get_fonts() -> list of strings
+
+

Returns a list of all the fonts available on the system. The names of the +fonts will be set to lowercase with all spaces and punctuation removed. This +works on most systems, but some will return an empty list if they cannot +find fonts.

+
+

Changed in pygame 2.1.3: Checks through user fonts instead of just global fonts for Windows.

+
+
+ +
+
+pygame.font.match_font()
+
+
find a specific font on the system
+
match_font(name, bold=False, italic=False) -> path
+
+

Returns the full path to a font file on the system. If bold or italic are +set to true, this will attempt to find the correct family of font.

+

The font name can also be an iterable of font names, a string of +comma-separated font names, or a bytes of comma-separated font names, in +which case the set of names will be searched in order. +If none of the given names are found, None is returned.

+
+

New in pygame 2.0.1: Accept an iterable of font names.

+
+
+

Changed in pygame 2.1.3: Checks through user fonts instead of just global fonts for Windows.

+
+

Example:

+
print pygame.font.match_font('bitstreamverasans')
+# output is: /usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf
+# (but only if you have Vera on your system)
+
+
+
+ +
+
+pygame.font.SysFont()
+
+
create a Font object from the system fonts
+
SysFont(name, size, bold=False, italic=False) -> Font
+
+

Return a new Font object that is loaded from the system fonts. The font will +match the requested bold and italic flags. Pygame uses a small set of common +font aliases. If the specific font you ask for is not available, a reasonable +alternative may be used. If a suitable system font is not found this will +fall back on loading the default pygame font.

+

The font name can also be an iterable of font names, a string of +comma-separated font names, or a bytes of comma-separated font names, in +which case the set of names will be searched in order.

+
+

New in pygame 2.0.1: Accept an iterable of font names.

+
+
+

Changed in pygame 2.1.3: Checks through user fonts instead of just global fonts for Windows.

+
+
+ +
+
+pygame.font.Font
+
+
create a new Font object from a file
+
Font(file_path=None, size=12) -> Font
+
Font(file_path, size) -> Font
+
Font(pathlib.Path, size) -> Font
+
Font(object, size) -> Font
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Gets or sets whether the font should be rendered in (faked) bold.
+Gets or sets whether the font should be rendered in (faked) italics.
+Gets or sets whether the font should be rendered with an underline.
+Gets or sets whether the font should be rendered with a strikethrough.
+draw text on a new Surface
+determine the amount of space needed to render text
+control if text is rendered with an underline
+check if text will be rendered with an underline
+control if text is rendered with a strikethrough
+check if text will be rendered with a strikethrough
+enable fake rendering of bold text
+check if text will be rendered bold
+enable fake rendering of italic text
+gets the metrics for each character in the passed string
+check if the text will be rendered italic
+get the line space of the font text
+get the height of the font
+get the ascent of the font
+get the descent of the font
+set the script code for text shaping
+

Load a new font from a given filename or a python file object. The size is +the height of the font in pixels. If the filename is None the pygame +default font will be loaded. If a font cannot be loaded from the arguments +given an exception will be raised. Once the font is created the size cannot +be changed. If no arguments are given then the default font will be used and +a font size of 12 is used.

+

Font objects are mainly used to render text into new Surface objects. The +render can emulate bold or italic features, but it is better to load from a +font with actual italic or bold glyphs.

+
+
+bold
+
+
Gets or sets whether the font should be rendered in (faked) bold.
+
bold -> bool
+
+

Whether the font should be rendered in bold.

+

When set to True, this enables the bold rendering of text. This +is a fake stretching of the font that doesn't look good on many +font types. If possible load the font from a real bold font +file. While bold, the font will have a different width than when +normal. This can be mixed with the italic, underline and +strikethrough modes.

+
+

New in pygame 2.0.0.

+
+
+ +
+
+italic
+
+
Gets or sets whether the font should be rendered in (faked) italics.
+
italic -> bool
+
+

Whether the font should be rendered in italic.

+

When set to True, this enables fake rendering of italic +text. This is a fake skewing of the font that doesn't look good +on many font types. If possible load the font from a real italic +font file. While italic the font will have a different width +than when normal. This can be mixed with the bold, underline and +strikethrough modes.

+
+

New in pygame 2.0.0.

+
+
+ +
+
+underline
+
+
Gets or sets whether the font should be rendered with an underline.
+
underline -> bool
+
+

Whether the font should be rendered in underline.

+

When set to True, all rendered fonts will include an +underline. The underline is always one pixel thick, regardless +of font size. This can be mixed with the bold, italic and +strikethrough modes.

+
+

New in pygame 2.0.0.

+
+
+ +
+
+strikethrough
+
+
Gets or sets whether the font should be rendered with a strikethrough.
+
strikethrough -> bool
+
+

Whether the font should be rendered with a strikethrough.

+

When set to True, all rendered fonts will include an +strikethrough. The strikethrough is always one pixel thick, +regardless of font size. This can be mixed with the bold, +italic and underline modes.

+
+

New in pygame 2.1.3.

+
+
+ +
+
+render()
+
+
draw text on a new Surface
+
render(text, antialias, color, background=None) -> Surface
+
+

This creates a new Surface with the specified text rendered on it. +pygame.fontpygame module for loading and rendering fonts provides no way to directly draw text on an existing +Surface: instead you must use Font.render() to create an image +(Surface) of the text, then blit this image onto another Surface.

+

The text can only be a single line: newline characters are not rendered. +Null characters ('x00') raise a TypeError. Both Unicode and char (byte) +strings are accepted. For Unicode strings only UCS-2 characters +('\u0001' to '\uFFFF') were previously supported and any greater +unicode codepoint would raise a UnicodeError. Now, characters in the +UCS-4 range are supported. For char strings a LATIN1 encoding is +assumed. The antialias argument is a boolean: if True the characters +will have smooth edges. The color argument is the color of the text +[e.g.: (0,0,255) for blue]. The optional background argument is a color +to use for the text background. If no background is passed the area +outside the text will be transparent.

+

The Surface returned will be of the dimensions required to hold the text. +(the same as those returned by Font.size()). If an empty string is passed +for the text, a blank surface will be returned that is zero pixel wide and +the height of the font.

+

Depending on the type of background and antialiasing used, this returns +different types of Surfaces. For performance reasons, it is good to know +what type of image will be used. If antialiasing is not used, the return +image will always be an 8-bit image with a two-color palette. If the +background is transparent a colorkey will be set. Antialiased images are +rendered to 24-bit RGB images. If the background is transparent a +pixel alpha will be included.

+

Optimization: if you know that the final destination for the text (on the +screen) will always have a solid background, and the text is antialiased, +you can improve performance by specifying the background color. This will +cause the resulting image to maintain transparency information by +colorkey rather than (much less efficient) alpha values.

+

If you render '\n' an unknown char will be rendered. Usually a +rectangle. Instead you need to handle newlines yourself.

+

Font rendering is not thread safe: only a single thread can render text +at any time.

+
+

Changed in pygame 2.0.3: Rendering UCS4 unicode works and does not +raise an exception. Use if hasattr(pygame.font, "UCS4"): to see if +pygame supports rendering UCS4 unicode including more languages and +emoji.

+
+
+ +
+
+size()
+
+
determine the amount of space needed to render text
+
size(text) -> (width, height)
+
+

Returns the dimensions needed to render the text. This can be used to +help determine the positioning needed for text before it is rendered. It +can also be used for word wrapping and other layout effects.

+

Be aware that most fonts use kerning which adjusts the widths for +specific letter pairs. For example, the width for "ae" will not always +match the width for "a" + "e".

+
+ +
+
+set_underline()
+
+
control if text is rendered with an underline
+
set_underline(bool) -> None
+
+

When enabled, all rendered fonts will include an underline. The underline +is always one pixel thick, regardless of font size. This can be mixed +with the bold, italic and strikethrough modes.

+
+

Note

+

This is the same as the underline attribute.

+
+
+ +
+
+get_underline()
+
+
check if text will be rendered with an underline
+
get_underline() -> bool
+
+

Return True when the font underline is enabled.

+
+
+

Note

+

This is the same as the underline attribute.

+
+
+
+ +
+
+set_strikethrough()
+
+
control if text is rendered with a strikethrough
+
set_strikethrough(bool) -> None
+
+

When enabled, all rendered fonts will include a strikethrough. The +strikethrough is always one pixel thick, regardless of font size. +This can be mixed with the bold, italic and underline modes.

+
+

Note

+

This is the same as the strikethrough attribute.

+
+
+

New in pygame 2.1.3.

+
+
+ +
+
+get_strikethrough()
+
+
check if text will be rendered with a strikethrough
+
get_strikethrough() -> bool
+
+

Return True when the font strikethrough is enabled.

+
+
+

Note

+

This is the same as the strikethrough attribute.

+
+
+

New in pygame 2.1.3.

+
+
+
+ +
+
+set_bold()
+
+
enable fake rendering of bold text
+
set_bold(bool) -> None
+
+

Enables the bold rendering of text. This is a fake stretching of the font +that doesn't look good on many font types. If possible load the font from +a real bold font file. While bold, the font will have a different width +than when normal. This can be mixed with the italic, underline and +strikethrough modes.

+
+

Note

+

This is the same as the bold attribute.

+
+
+ +
+
+get_bold()
+
+
check if text will be rendered bold
+
get_bold() -> bool
+
+

Return True when the font bold rendering mode is enabled.

+
+

Note

+

This is the same as the bold attribute.

+
+
+ +
+
+set_italic()
+
+
enable fake rendering of italic text
+
set_italic(bool) -> None
+
+

Enables fake rendering of italic text. This is a fake skewing of the font +that doesn't look good on many font types. If possible load the font from +a real italic font file. While italic the font will have a different +width than when normal. This can be mixed with the bold, underline and +strikethrough modes.

+
+

Note

+

This is the same as the italic attribute.

+
+
+ +
+
+metrics()
+
+
gets the metrics for each character in the passed string
+
metrics(text) -> list
+
+

The list contains tuples for each character, which contain the minimum +X offset, the maximum X offset, the minimum Y offset, the +maximum Y offset and the advance offset (bearing plus width) of the +character. [(minx, maxx, miny, maxy, advance), (minx, maxx, miny, maxy, +advance), ...]. None is entered in the list for each unrecognized +character.

+
+ +
+
+get_italic()
+
+
check if the text will be rendered italic
+
get_italic() -> bool
+
+

Return True when the font italic rendering mode is enabled.

+
+

Note

+

This is the same as the italic attribute.

+
+
+ +
+
+get_linesize()
+
+
get the line space of the font text
+
get_linesize() -> int
+
+

Return the height in pixels for a line of text with the font. When +rendering multiple lines of text this is the recommended amount of space +between lines.

+
+ +
+
+get_height()
+
+
get the height of the font
+
get_height() -> int
+
+

Return the height in pixels of the actual rendered text. This is the +average size for each glyph in the font.

+
+ +
+
+get_ascent()
+
+
get the ascent of the font
+
get_ascent() -> int
+
+

Return the height in pixels for the font ascent. The ascent is the number +of pixels from the font baseline to the top of the font.

+
+ +
+
+get_descent()
+
+
get the descent of the font
+
get_descent() -> int
+
+

Return the height in pixels for the font descent. The descent is the +number of pixels from the font baseline to the bottom of the font.

+
+ +
+
+set_script()
+
+
set the script code for text shaping
+
set_script(str) -> None
+
+

Experimental: feature still in development available for testing and feedback. It may change. +Please leave feedback with authors

+

Sets the script used by harfbuzz text shaping, taking a 4 character +script code as input. For example, Hindi is written in the Devanagari +script, for which the script code is "Deva". See the full list of +script codes in ISO 15924.

+

This method requires pygame built with SDL_ttf 2.20.0 or above. Otherwise the +method will raise a pygame.error.

+
+

New in pygame 2.2.0.

+
+
+ +
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/freetype.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/freetype.html new file mode 100644 index 00000000..be7347b0 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/freetype.html @@ -0,0 +1,1273 @@ + + + + + + + + + pygame.freetype — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.freetype
+
+
Enhanced pygame module for loading and rendering computer fonts
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Return the latest FreeType error
+Return the FreeType version
+Initialize the underlying FreeType library.
+Shut down the underlying FreeType library.
+Returns True if the FreeType module is currently initialized.
+DEPRECATED: Use get_init() instead.
+Return the glyph case size
+Return the default pixel size in dots per inch
+Set the default pixel size in dots per inch for the module
+create a Font object from the system fonts
+Get the filename of the default font
+Create a new Font instance from a supported font file.
+

The pygame.freetype module is a replacement for pygame.fontpygame module for loading and rendering fonts. +It has all of the functionality of the original, plus many new features. +Yet is has absolutely no dependencies on the SDL_ttf library. +It is implemented directly on the FreeType 2 library. +The pygame.freetype module is not itself backward compatible with +pygame.fontpygame module for loading and rendering fonts. +Instead, use the pygame.ftfont module as a drop-in replacement +for pygame.fontpygame module for loading and rendering fonts.

+

All font file formats supported by FreeType can be rendered by +pygame.freetype, namely TTF, Type1, CFF, OpenType, +SFNT, PCF, FNT, BDF, PFR and Type42 fonts. +All glyphs having UTF-32 code points are accessible +(see Font.ucs4).

+

Most work on fonts is done using Font instances. +The module itself only has routines for initialization and creation +of Font objects. +You can load fonts from the system using the SysFont() function.

+

Extra support of bitmap fonts is available. Available bitmap sizes can +be listed (see Font.get_sizes()). For bitmap only fonts Font +can set the size for you (see the Font.size property).

+

For now undefined character codes are replaced with the .notdef +(not defined) character. +How undefined codes are handled may become configurable in a future release.

+

Pygame comes with a built-in default font. This can always be accessed by +passing None as the font name to the Font constructor.

+

Extra rendering features available to pygame.freetype.FontCreate a new Font instance from a supported font file. +are direct to surface rendering (see Font.render_to()), character kerning +(see Font.kerning), vertical layout (see Font.vertical), +rotation of rendered text (see Font.rotation), +and the strong style (see Font.strong). +Some properties are configurable, such as +strong style strength (see Font.strength) and underline positioning +(see Font.underline_adjustment). Text can be positioned by the upper +right corner of the text box or by the text baseline (see Font.origin). +Finally, a font's vertical and horizontal size can be adjusted separately +(see Font.size). +The pygame.examples.freetype_misc +example shows these features in use.

+

The pygame package does not import freetype automatically when +loaded. This module must be imported explicitly to be used.

+
import pygame
+import pygame.freetype
+
+
+
+

New in pygame 1.9.2: freetype

+
+
+
+pygame.freetype.get_error()
+
+
Return the latest FreeType error
+
get_error() -> str
+
get_error() -> None
+
+

Return a description of the last error which occurred in the FreeType2 +library, or None if no errors have occurred.

+
+ +
+
+pygame.freetype.get_version()
+
+
Return the FreeType version
+
get_version(linked=True) -> (int, int, int)
+
+

Returns the version of the FreeType library in use by this module. linked=True +is the default behavior and returns the linked version of FreeType and linked=False +returns the compiled version of FreeType.

+

Note that the freetype module depends on the FreeType 2 library. +It will not compile with the original FreeType 1.0. Hence, the first element +of the tuple will always be "2".

+
+

Changed in pygame 2.2.0: linked keyword argument added and default behavior changed from returning compiled version to returning linked version

+
+
+ +
+
+pygame.freetype.init()
+
+
Initialize the underlying FreeType library.
+
init(cache_size=64, resolution=72) -> None
+
+

This function initializes the underlying FreeType library and must be +called before trying to use any of the functionality of the freetype +module.

+

However, pygame.init()initialize all imported pygame modules will automatically call this function +if the freetype module is already imported. It is safe to call this +function more than once.

+

Optionally, you may specify a default cache_size for the Glyph cache: the +maximum number of glyphs that will be cached at any given time by the +module. Exceedingly small values will be automatically tuned for +performance. Also a default pixel resolution, in dots per inch, can +be given to adjust font scaling.

+
+ +
+
+pygame.freetype.quit()
+
+
Shut down the underlying FreeType library.
+
quit() -> None
+
+

This function closes the freetype module. After calling this +function, you should not invoke any class, method or function related to the +freetype module as they are likely to fail or might give unpredictable +results. It is safe to call this function even if the module hasn't been +initialized yet.

+
+ +
+
+pygame.freetype.get_init()
+
+
Returns True if the FreeType module is currently initialized.
+
get_init() -> bool
+
+

Returns True if the pygame.freetype module is currently initialized.

+
+

New in pygame 1.9.5.

+
+
+ +
+
+pygame.freetype.was_init()
+
+
DEPRECATED: Use get_init() instead.
+
was_init() -> bool
+
+

DEPRECATED: Returns True if the pygame.freetype module is currently +initialized. Use get_init() instead.

+
+ +
+
+pygame.freetype.get_cache_size()
+
+
Return the glyph case size
+
get_cache_size() -> long
+
+

See pygame.freetype.init()Initialize the underlying FreeType library..

+
+ +
+
+pygame.freetype.get_default_resolution()
+
+
Return the default pixel size in dots per inch
+
get_default_resolution() -> long
+
+

Returns the default pixel size, in dots per inch, for the module. +The default is 72 DPI.

+
+ +
+
+pygame.freetype.set_default_resolution()
+
+
Set the default pixel size in dots per inch for the module
+
set_default_resolution([resolution])
+
+

Set the default pixel size, in dots per inch, for the module. If the +optional argument is omitted or zero the resolution is reset to 72 DPI.

+
+ +
+
+pygame.freetype.SysFont()
+
+
create a Font object from the system fonts
+
SysFont(name, size, bold=False, italic=False) -> Font
+
+

Return a new Font object that is loaded from the system fonts. The font will +match the requested bold and italic flags. Pygame uses a small set of +common font aliases. If the specific font you ask for is not available, a +reasonable alternative may be used. If a suitable system font is not found +this will fall back on loading the default pygame font.

+

The font name can also be an iterable of font names, a string of +comma-separated font names, or a bytes of comma-separated font names, in +which case the set of names will be searched in order.

+
+

New in pygame 2.0.1: Accept an iterable of font names.

+
+
+ +
+
+pygame.freetype.get_default_font()
+
+
Get the filename of the default font
+
get_default_font() -> string
+
+

Return the filename of the default pygame font. This is not the full path +to the file. The file is usually in the same directory as the font module, +but can also be bundled in a separate archive.

+
+ +
+
+pygame.freetype.Font
+
+
Create a new Font instance from a supported font file.
+
Font(file, size=0, font_index=0, resolution=0, ucs4=False) -> Font
+
Font(pathlib.Path) -> Font
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Proper font name.
+Font file path
+The default point size used in rendering
+Return the size and offset of rendered text
+Return the glyph metrics for the given text
+The unscaled height of the font in font units
+The unscaled ascent of the font in font units
+The unscaled descent of the font in font units
+The scaled ascent of the font in pixels
+The scaled descent of the font in pixels
+The scaled height of the font in pixels
+The scaled bounding box height of the font in pixels
+return the available sizes of embedded bitmaps
+Return rendered text as a surface
+Render text onto an existing surface
+Return rendered text as a string of bytes
+Render text into an array of ints
+The font's style flags
+The state of the font's underline style flag
+The state of the font's strong style flag
+The state of the font's oblique style flag
+The state of the font's wide style flag
+The strength associated with the strong or wide font styles
+Adjustment factor for the underline position
+Gets whether the font is fixed-width
+the number of available bitmap sizes for the font
+Gets whether the font is scalable
+allow the use of embedded bitmaps in an outline font file
+Font anti-aliasing mode
+Character kerning mode
+Font vertical mode
+text rotation in degrees counterclockwise
+default foreground color
+default background color
+Font render to text origin mode
+padded boundary mode
+Enable UCS-4 mode
+Pixel resolution in dots per inch
+

Argument file can be either a string representing the font's filename, a +file-like object containing the font, or None; if None, a default, +Pygame, font is used.

+

Optionally, a size argument may be specified to set the default size in +points, which determines the size of the rendered characters. +The size can also be passed explicitly to each method call. +Because of the way the caching system works, specifying a default size on +the constructor doesn't imply a performance gain over manually passing +the size on each function call. If the font is bitmap and no size +is given, the default size is set to the first available size for the font.

+

If the font file has more than one font, the font to load can be chosen with +the index argument. An exception is raised for an out-of-range font index +value.

+

The optional resolution argument sets the pixel size, in dots per inch, +for use in scaling glyphs for this Font instance. If 0 then the default +module value, set by init(), is used. The Font object's +resolution can only be changed by re-initializing the Font instance.

+

The optional ucs4 argument, an integer, sets the default text translation +mode: 0 (False) recognize UTF-16 surrogate pairs, any other value (True), +to treat Unicode text as UCS-4, with no surrogate pairs. See +Font.ucs4.

+
+
+name
+
+
Proper font name.
+
name -> string
+
+

Read only. Returns the real (long) name of the font, as +recorded in the font file.

+
+ +
+
+path
+
+
Font file path
+
path -> unicode
+
+

Read only. Returns the path of the loaded font file

+
+ +
+
+size
+
+
The default point size used in rendering
+
size -> float
+
size -> (float, float)
+
+

Get or set the default size for text metrics and rendering. It can be +a single point size, given as a Python int or float, or a +font ppem (width, height) tuple. Size values are non-negative. +A zero size or width represents an undefined size. In this case +the size must be given as a method argument, or an exception is +raised. A zero width but non-zero height is a ValueError.

+

For a scalable font, a single number value is equivalent to a tuple +with width equal height. A font can be stretched vertically with +height set greater than width, or horizontally with width set +greater than height. For embedded bitmaps, as listed by get_sizes(), +use the nominal width and height to select an available size.

+

Font size differs for a non-scalable, bitmap, font. During a +method call it must match one of the available sizes returned by +method get_sizes(). If not, an exception is raised. +If the size is a single number, the size is first matched against the +point size value. If no match, then the available size with the +same nominal width and height is chosen.

+
+ +
+
+get_rect()
+
+
Return the size and offset of rendered text
+
get_rect(text, style=STYLE_DEFAULT, rotation=0, size=0) -> rect
+
+

Gets the final dimensions and origin, in pixels, of text using the +optional size in points, style, and rotation. For other +relevant render properties, and for any optional argument not given, +the default values set for the Font instance are used.

+

Returns a Rect instance containing the +width and height of the text's bounding box and the position of the +text's origin. +The origin is useful in aligning separately rendered pieces of text. +It gives the baseline position and bearing at the start of the text. +See the render_to() method for an example.

+

If text is a char (byte) string, its encoding is assumed to be +LATIN1.

+

Optionally, text can be None, which will return the bounding +rectangle for the text passed to a previous get_rect(), +render(), render_to(), render_raw(), or +render_raw_to() call. See render_to() for more +details.

+
+ +
+
+get_metrics()
+
+
Return the glyph metrics for the given text
+
get_metrics(text, size=0) -> [(...), ...]
+
+

Returns the glyph metrics for each character in text.

+

The glyph metrics are returned as a list of tuples. Each tuple gives +metrics of a single character glyph. The glyph metrics are:

+
(min_x, max_x, min_y, max_y, horizontal_advance_x, horizontal_advance_y)
+
+
+

The bounding box min_x, max_x, min_y, and max_y values are returned as +grid-fitted pixel coordinates of type int. The advance values are +float values.

+

The calculations are done using the font's default size in points. +Optionally you may specify another point size with the size argument.

+

The metrics are adjusted for the current rotation, strong, and oblique +settings.

+

If text is a char (byte) string, then its encoding is assumed to be +LATIN1.

+
+ +
+
+height
+
+
The unscaled height of the font in font units
+
height -> int
+
+

Read only. Gets the height of the font. This is the average value of all +glyphs in the font.

+
+ +
+
+ascender
+
+
The unscaled ascent of the font in font units
+
ascender -> int
+
+

Read only. Return the number of units from the font's baseline to +the top of the bounding box.

+
+ +
+
+descender
+
+
The unscaled descent of the font in font units
+
descender -> int
+
+

Read only. Return the height in font units for the font descent. +The descent is the number of units from the font's baseline to the +bottom of the bounding box.

+
+ +
+
+get_sized_ascender()
+
+
The scaled ascent of the font in pixels
+
get_sized_ascender(<size>=0) -> int
+
+

Return the number of units from the font's baseline to the top of the +bounding box. It is not adjusted for strong or rotation.

+
+ +
+
+get_sized_descender()
+
+
The scaled descent of the font in pixels
+
get_sized_descender(<size>=0) -> int
+
+

Return the number of pixels from the font's baseline to the top of the +bounding box. It is not adjusted for strong or rotation.

+
+ +
+
+get_sized_height()
+
+
The scaled height of the font in pixels
+
get_sized_height(<size>=0) -> int
+
+

Returns the height of the font. This is the average value of all +glyphs in the font. It is not adjusted for strong or rotation.

+
+ +
+
+get_sized_glyph_height()
+
+
The scaled bounding box height of the font in pixels
+
get_sized_glyph_height(<size>=0) -> int
+
+

Return the glyph bounding box height of the font in pixels. +This is the average value of all glyphs in the font. +It is not adjusted for strong or rotation.

+
+ +
+
+get_sizes()
+
+
return the available sizes of embedded bitmaps
+
get_sizes() -> [(int, int, int, float, float), ...]
+
get_sizes() -> []
+
+

Returns a list of tuple records, one for each point size +supported. Each tuple containing the point size, the height in pixels, +width in pixels, horizontal ppem (nominal width) in fractional pixels, +and vertical ppem (nominal height) in fractional pixels.

+
+ +
+
+render()
+
+
Return rendered text as a surface
+
render(text, fgcolor=None, bgcolor=None, style=STYLE_DEFAULT, rotation=0, size=0) -> (Surface, Rect)
+
+

Returns a new Surface, +with the text rendered to it +in the color given by 'fgcolor'. If no foreground color is given, +the default foreground color, fgcolor is used. +If bgcolor is given, the surface +will be filled with this color. When no background color is given, +the surface background is transparent, zero alpha. Normally the returned +surface has a 32 bit pixel size. However, if bgcolor is None +and anti-aliasing is disabled a monochrome 8 bit colorkey surface, +with colorkey set for the background color, is returned.

+

The return value is a tuple: the new surface and the bounding +rectangle giving the size and origin of the rendered text.

+

If an empty string is passed for text then the returned Rect is zero +width and the height of the font.

+

Optional fgcolor, style, rotation, and size arguments override +the default values set for the Font instance.

+

If text is a char (byte) string, then its encoding is assumed to be +LATIN1.

+

Optionally, text can be None, which will render the text +passed to a previous get_rect(), render(), render_to(), +render_raw(), or render_raw_to() call. +See render_to() for details.

+
+ +
+
+render_to()
+
+
Render text onto an existing surface
+
render_to(surf, dest, text, fgcolor=None, bgcolor=None, style=STYLE_DEFAULT, rotation=0, size=0) -> Rect
+
+

Renders the string text to the pygame.Surfacepygame object for representing images surf, +at position dest, a (x, y) surface coordinate pair. +If either x or y is not an integer it is converted to one if possible. +Any sequence where the first two items are x and y positional elements +is accepted, including a Rect instance. +As with render(), +optional fgcolor, style, rotation, and size argument are +available.

+

If a background color bgcolor is given, the text bounding box is +first filled with that color. The text is blitted next. +Both the background fill and text rendering involve full alpha blits. +That is, the alpha values of the foreground, background, and destination +target surface all affect the blit.

+

The return value is a rectangle giving the size and position of the +rendered text within the surface.

+

If an empty string is passed for text then the returned +Rect is zero width and the height of the font. +The rect will test False.

+

Optionally, text can be set None, which will re-render text +passed to a previous render_to(), get_rect(), render(), +render_raw(), or render_raw_to() call. Primarily, this +feature is an aid to using render_to() in combination with +get_rect(). An example:

+
def word_wrap(surf, text, font, color=(0, 0, 0)):
+    font.origin = True
+    words = text.split(' ')
+    width, height = surf.get_size()
+    line_spacing = font.get_sized_height() + 2
+    x, y = 0, line_spacing
+    space = font.get_rect(' ')
+    for word in words:
+        bounds = font.get_rect(word)
+        if x + bounds.width + bounds.x >= width:
+            x, y = 0, y + line_spacing
+        if x + bounds.width + bounds.x >= width:
+            raise ValueError("word too wide for the surface")
+        if y + bounds.height - bounds.y >= height:
+            raise ValueError("text to long for the surface")
+        font.render_to(surf, (x, y), None, color)
+        x += bounds.width + space.width
+    return x, y
+
+
+

When render_to() is called with the same +font properties ― size, style, strength, +wide, antialiased, vertical, rotation, +kerning, and use_bitmap_strikes ― as get_rect(), +render_to() will use the layout calculated by get_rect(). +Otherwise, render_to() will recalculate the layout if called +with a text string or one of the above properties has changed +after the get_rect() call.

+

If text is a char (byte) string, then its encoding is assumed to be +LATIN1.

+
+ +
+
+render_raw()
+
+
Return rendered text as a string of bytes
+
render_raw(text, style=STYLE_DEFAULT, rotation=0, size=0, invert=False) -> (bytes, (int, int))
+
+

Like render() but with the pixels returned as a byte string +of 8-bit gray-scale values. The foreground color is 255, the +background 0, useful as an alpha mask for a foreground pattern.

+
+ +
+
+render_raw_to()
+
+
Render text into an array of ints
+
render_raw_to(array, text, dest=None, style=STYLE_DEFAULT, rotation=0, size=0, invert=False) -> Rect
+
+

Render to an array object exposing an array struct interface. The array +must be two dimensional with integer items. The default dest value, +None, is equivalent to position (0, 0). See render_to(). +As with the other render methods, text can be None to +render a text string passed previously to another method.

+

The return value is a pygame.Rect()pygame object for storing rectangular coordinates giving the size and position of +the rendered text.

+
+ +
+
+style
+
+
The font's style flags
+
style -> int
+
+

Gets or sets the default style of the Font. This default style will be +used for all text rendering and size calculations unless overridden +specifically a render or get_rect() call. +The style value may be a bit-wise OR of one or more of the following +constants:

+
STYLE_NORMAL
+STYLE_UNDERLINE
+STYLE_OBLIQUE
+STYLE_STRONG
+STYLE_WIDE
+STYLE_DEFAULT
+
+
+

These constants may be found on the FreeType constants module. +Optionally, the default style can be modified or obtained accessing the +individual style attributes (underline, oblique, strong).

+

The STYLE_OBLIQUE and STYLE_STRONG styles are for +scalable fonts only. An attempt to set either for a bitmap font raises +an AttributeError. An attempt to set either for an inactive font, +as returned by Font.__new__(), raises a RuntimeError.

+

Assigning STYLE_DEFAULT to the style property leaves +the property unchanged, as this property defines the default. +The style property will never return STYLE_DEFAULT.

+
+ +
+
+underline
+
+
The state of the font's underline style flag
+
underline -> bool
+
+

Gets or sets whether the font will be underlined when drawing text. This +default style value will be used for all text rendering and size +calculations unless overridden specifically in a render or +get_rect() call, via the 'style' parameter.

+
+ +
+
+strong
+
+
The state of the font's strong style flag
+
strong -> bool
+
+

Gets or sets whether the font will be bold when drawing text. This +default style value will be used for all text rendering and size +calculations unless overridden specifically in a render or +get_rect() call, via the 'style' parameter.

+
+ +
+
+oblique
+
+
The state of the font's oblique style flag
+
oblique -> bool
+
+

Gets or sets whether the font will be rendered as oblique. This +default style value will be used for all text rendering and size +calculations unless overridden specifically in a render or +get_rect() call, via the style parameter.

+

The oblique style is only supported for scalable (outline) fonts. +An attempt to set this style on a bitmap font will raise an +AttributeError. If the font object is inactive, as returned by +Font.__new__(), setting this property raises a RuntimeError.

+
+ +
+
+wide
+
+
The state of the font's wide style flag
+
wide -> bool
+
+

Gets or sets whether the font will be stretched horizontally +when drawing text. It produces a result similar to +pygame.font.Fontcreate a new Font object from a file's bold. This style not available for +rotated text.

+
+ +
+
+strength
+
+
The strength associated with the strong or wide font styles
+
strength -> float
+
+

The amount by which a font glyph's size is enlarged for the +strong or wide transformations, as a fraction of the untransformed +size. For the wide style only the horizontal dimension is +increased. For strong text both the horizontal and vertical +dimensions are enlarged. A wide style of strength 0.08333 ( 1/12 ) is +equivalent to the pygame.font.Fontcreate a new Font object from a file bold style. +The default is 0.02778 ( 1/36 ).

+

The strength style is only supported for scalable (outline) fonts. +An attempt to set this property on a bitmap font will raise an +AttributeError. If the font object is inactive, as returned by +Font.__new__(), assignment to this property raises a RuntimeError.

+
+ +
+
+underline_adjustment
+
+
Adjustment factor for the underline position
+
underline_adjustment -> float
+
+

Gets or sets a factor which, when positive, is multiplied with the +font's underline offset to adjust the underline position. A negative +value turns an underline into a strike-through or overline. It is +multiplied with the ascender. Accepted values range between -2.0 and 2.0 +inclusive. A value of 0.5 closely matches Tango underlining. A value of +1.0 mimics pygame.font.Fontcreate a new Font object from a file underlining.

+
+ +
+
+fixed_width
+
+
Gets whether the font is fixed-width
+
fixed_width -> bool
+
+

Read only. Returns True if the font contains fixed-width +characters (for example Courier, Bitstream Vera Sans Mono, Andale Mono).

+
+ +
+
+fixed_sizes
+
+
the number of available bitmap sizes for the font
+
fixed_sizes -> int
+
+

Read only. Returns the number of point sizes for which the font contains +bitmap character images. If zero then the font is not a bitmap font. +A scalable font may contain pre-rendered point sizes as strikes.

+
+ +
+
+scalable
+
+
Gets whether the font is scalable
+
scalable -> bool
+
+

Read only. Returns True if the font contains outline glyphs. +If so, the point size is not limited to available bitmap sizes.

+
+ +
+
+use_bitmap_strikes
+
+
allow the use of embedded bitmaps in an outline font file
+
use_bitmap_strikes -> bool
+
+

Some scalable fonts include embedded bitmaps for particular point +sizes. This property controls whether or not those bitmap strikes +are used. Set it False to disable the loading of any bitmap +strike. Set it True, the default, to permit bitmap strikes +for a non-rotated render with no style other than wide or +underline. This property is ignored for bitmap fonts.

+

See also fixed_sizes and get_sizes().

+
+ +
+
+antialiased
+
+
Font anti-aliasing mode
+
antialiased -> bool
+
+

Gets or sets the font's anti-aliasing mode. This defaults to +True on all fonts, which are rendered with full 8 bit blending.

+

Set to False to do monochrome rendering. This should +provide a small speed gain and reduce cache memory size.

+
+ +
+
+kerning
+
+
Character kerning mode
+
kerning -> bool
+
+

Gets or sets the font's kerning mode. This defaults to False +on all fonts, which will be rendered without kerning.

+

Set to True to add kerning between character pairs, if supported +by the font, when positioning glyphs.

+
+ +
+
+vertical
+
+
Font vertical mode
+
vertical -> bool
+
+

Gets or sets whether the characters are laid out vertically rather +than horizontally. May be useful when rendering Kanji or some other +vertical script.

+

Set to True to switch to a vertical text layout. The default +is False, place horizontally.

+

Note that the Font class does not automatically determine +script orientation. Vertical layout must be selected explicitly.

+

Also note that several font formats (especially bitmap based ones) don't +contain the necessary metrics to draw glyphs vertically, so drawing in +those cases will give unspecified results.

+
+ +
+
+rotation
+
+
text rotation in degrees counterclockwise
+
rotation -> int
+
+

Gets or sets the baseline angle of the rendered text. The angle is +represented as integer degrees. The default angle is 0, with horizontal +text rendered along the X-axis, and vertical text along the Y-axis. +A positive value rotates these axes counterclockwise that many degrees. +A negative angle corresponds to a clockwise rotation. The rotation +value is normalized to a value within the range 0 to 359 inclusive +(eg. 390 -> 390 - 360 -> 30, -45 -> 360 + -45 -> 315, +720 -> 720 - (2 * 360) -> 0).

+

Only scalable (outline) fonts can be rotated. An attempt to change +the rotation of a bitmap font raises an AttributeError. +An attempt to change the rotation of an inactive font instance, as +returned by Font.__new__(), raises a RuntimeError.

+
+ +
+
+fgcolor
+
+
default foreground color
+
fgcolor -> Color
+
+

Gets or sets the default glyph rendering color. It is initially opaque +black ― (0, 0, 0, 255). Applies to render() and render_to().

+
+ +
+
+bgcolor
+
+
default background color
+
bgcolor -> Color
+
+

Gets or sets the default background rendering color. Initially it is +unset and text will render with a transparent background by default. +Applies to render() and render_to().

+
+ +
+

New in pygame 2.0.0.

+
+
+
+origin
+
+
Font render to text origin mode
+
origin -> bool
+
+

If set True, render_to() and render_raw_to() will +take the dest position to be that of the text origin, as opposed to +the top-left corner of the bounding box. See get_rect() for +details.

+
+ +
+
+pad
+
+
padded boundary mode
+
pad -> bool
+
+

If set True, then the text boundary rectangle will be inflated +to match that of font.Font. +Otherwise, the boundary rectangle is just large enough for the text.

+
+ +
+
+ucs4
+
+
Enable UCS-4 mode
+
ucs4 -> bool
+
+

Gets or sets the decoding of Unicode text. By default, the +freetype module performs UTF-16 surrogate pair decoding on Unicode text. +This allows 32-bit escape sequences ('Uxxxxxxxx') between 0x10000 and +0x10FFFF to represent their corresponding UTF-32 code points on Python +interpreters built with a UCS-2 Unicode type (on Windows, for instance). +It also means character values within the UTF-16 surrogate area (0xD800 +to 0xDFFF) are considered part of a surrogate pair. A malformed surrogate +pair will raise a UnicodeEncodeError. Setting ucs4 True turns +surrogate pair decoding off, allowing access the full UCS-4 character +range to a Python interpreter built with four-byte Unicode character +support.

+
+ +
+
+resolution
+
+
Pixel resolution in dots per inch
+
resolution -> int
+
+

Read only. Gets pixel size used in scaling font glyphs for this +Font instance.

+
+ +
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/gfxdraw.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/gfxdraw.html new file mode 100644 index 00000000..073e9fb7 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/gfxdraw.html @@ -0,0 +1,1056 @@ + + + + + + + + + pygame.gfxdraw — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.gfxdraw
+
+
pygame module for drawing shapes
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+draw a pixel
+draw a horizontal line
+draw a vertical line
+draw a line
+draw a rectangle
+draw a filled rectangle
+draw a circle
+draw an antialiased circle
+draw a filled circle
+draw an ellipse
+draw an antialiased ellipse
+draw a filled ellipse
+draw an arc
+draw a pie
+draw a trigon/triangle
+draw an antialiased trigon/triangle
+draw a filled trigon/triangle
+draw a polygon
+draw an antialiased polygon
+draw a filled polygon
+draw a textured polygon
+draw a Bezier curve
+

EXPERIMENTAL!: This API may change or disappear in later pygame releases. If +you use this, your code may break with the next pygame release.

+

The pygame package does not import gfxdraw automatically when loaded, so it +must imported explicitly to be used.

+
import pygame
+import pygame.gfxdraw
+
+
+

For all functions the arguments are strictly positional and integers are +accepted for coordinates and radii. The color argument can be one of the +following formats:

+
+
+
+

The functions rectangle() and box() will accept any (x, y, w, h) +sequence for their rect argument, though pygame.Rectpygame object for storing rectangular coordinates instances are +preferred.

+

To draw a filled antialiased shape, first use the antialiased (aa*) version +of the function, and then use the filled (filled_*) version. +For example:

+
col = (255, 0, 0)
+surf.fill((255, 255, 255))
+pygame.gfxdraw.aacircle(surf, x, y, 30, col)
+pygame.gfxdraw.filled_circle(surf, x, y, 30, col)
+
+
+
+

Note

+

For threading, each of the functions releases the GIL during the C part of +the call.

+
+
+

Note

+

See the pygame.drawpygame module for drawing shapes module for alternative draw methods. +The pygame.gfxdraw module differs from the pygame.drawpygame module for drawing shapes module in +the API it uses and the different draw functions available. +pygame.gfxdraw wraps the primitives from the library called SDL_gfx, +rather than using modified versions.

+
+
+

New in pygame 1.9.0.

+
+
+
+pygame.gfxdraw.pixel()
+
+
draw a pixel
+
pixel(surface, x, y, color) -> None
+
+

Draws a single pixel, at position (x ,y), on the given surface.

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • x (int) -- x coordinate of the pixel

  • +
  • y (int) -- y coordinate of the pixel

  • +
  • color (Color or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+ +
+
+pygame.gfxdraw.hline()
+
+
draw a horizontal line
+
hline(surface, x1, x2, y, color) -> None
+
+

Draws a straight horizontal line ((x1, y) to (x2, y)) on the given +surface. There are no endcaps.

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • x1 (int) -- x coordinate of one end of the line

  • +
  • x2 (int) -- x coordinate of the other end of the line

  • +
  • y (int) -- y coordinate of the line

  • +
  • color (Color or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+ +
+
+pygame.gfxdraw.vline()
+
+
draw a vertical line
+
vline(surface, x, y1, y2, color) -> None
+
+

Draws a straight vertical line ((x, y1) to (x, y2)) on the given +surface. There are no endcaps.

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • x (int) -- x coordinate of the line

  • +
  • y1 (int) -- y coordinate of one end of the line

  • +
  • y2 (int) -- y coordinate of the other end of the line

  • +
  • color (Color or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+ +
+
+pygame.gfxdraw.line()
+
+
draw a line
+
line(surface, x1, y1, x2, y2, color) -> None
+
+

Draws a straight line ((x1, y1) to (x2, y2)) on the given surface. +There are no endcaps.

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • x1 (int) -- x coordinate of one end of the line

  • +
  • y1 (int) -- y coordinate of one end of the line

  • +
  • x2 (int) -- x coordinate of the other end of the line

  • +
  • y2 (int) -- y coordinate of the other end of the line

  • +
  • color (Color or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+ +
+
+pygame.gfxdraw.rectangle()
+
+
draw a rectangle
+
rectangle(surface, rect, color) -> None
+
+

Draws an unfilled rectangle on the given surface. For a filled rectangle use +box().

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • rect (Rect) -- rectangle to draw, position and dimensions

  • +
  • color (Color or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+

Note

+

The rect.bottom and rect.right attributes of a pygame.Rectpygame object for storing rectangular coordinates +always lie one pixel outside of its actual border. Therefore, these +values will not be included as part of the drawing.

+
+
+ +
+
+pygame.gfxdraw.box()
+
+
draw a filled rectangle
+
box(surface, rect, color) -> None
+
+

Draws a filled rectangle on the given surface. For an unfilled rectangle use +rectangle().

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • rect (Rect) -- rectangle to draw, position and dimensions

  • +
  • color (Color or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+

Note

+

The rect.bottom and rect.right attributes of a pygame.Rectpygame object for storing rectangular coordinates +always lie one pixel outside of its actual border. Therefore, these +values will not be included as part of the drawing.

+
+
+

Note

+

The pygame.Surface.fill()fill Surface with a solid color method works just as well for drawing +filled rectangles. In fact pygame.Surface.fill()fill Surface with a solid color can be hardware +accelerated on some platforms with both software and hardware display +modes.

+
+
+ +
+
+pygame.gfxdraw.circle()
+
+
draw a circle
+
circle(surface, x, y, r, color) -> None
+
+

Draws an unfilled circle on the given surface. For a filled circle use +filled_circle().

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • x (int) -- x coordinate of the center of the circle

  • +
  • y (int) -- y coordinate of the center of the circle

  • +
  • r (int) -- radius of the circle

  • +
  • color (Color or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+ +
+
+pygame.gfxdraw.aacircle()
+
+
draw an antialiased circle
+
aacircle(surface, x, y, r, color) -> None
+
+

Draws an unfilled antialiased circle on the given surface.

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • x (int) -- x coordinate of the center of the circle

  • +
  • y (int) -- y coordinate of the center of the circle

  • +
  • r (int) -- radius of the circle

  • +
  • color (Color or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+ +
+
+pygame.gfxdraw.filled_circle()
+
+
draw a filled circle
+
filled_circle(surface, x, y, r, color) -> None
+
+

Draws a filled circle on the given surface. For an unfilled circle use +circle().

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • x (int) -- x coordinate of the center of the circle

  • +
  • y (int) -- y coordinate of the center of the circle

  • +
  • r (int) -- radius of the circle

  • +
  • color (Color or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+ +
+
+pygame.gfxdraw.ellipse()
+
+
draw an ellipse
+
ellipse(surface, x, y, rx, ry, color) -> None
+
+

Draws an unfilled ellipse on the given surface. For a filled ellipse use +filled_ellipse().

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • x (int) -- x coordinate of the center of the ellipse

  • +
  • y (int) -- y coordinate of the center of the ellipse

  • +
  • rx (int) -- horizontal radius of the ellipse

  • +
  • ry (int) -- vertical radius of the ellipse

  • +
  • color (Color or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+ +
+
+pygame.gfxdraw.aaellipse()
+
+
draw an antialiased ellipse
+
aaellipse(surface, x, y, rx, ry, color) -> None
+
+

Draws an unfilled antialiased ellipse on the given surface.

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • x (int) -- x coordinate of the center of the ellipse

  • +
  • y (int) -- y coordinate of the center of the ellipse

  • +
  • rx (int) -- horizontal radius of the ellipse

  • +
  • ry (int) -- vertical radius of the ellipse

  • +
  • color (Color or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+ +
+
+pygame.gfxdraw.filled_ellipse()
+
+
draw a filled ellipse
+
filled_ellipse(surface, x, y, rx, ry, color) -> None
+
+

Draws a filled ellipse on the given surface. For an unfilled ellipse use +ellipse().

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • x (int) -- x coordinate of the center of the ellipse

  • +
  • y (int) -- y coordinate of the center of the ellipse

  • +
  • rx (int) -- horizontal radius of the ellipse

  • +
  • ry (int) -- vertical radius of the ellipse

  • +
  • color (Color or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+ +
+
+pygame.gfxdraw.arc()
+
+
draw an arc
+
arc(surface, x, y, r, start_angle, stop_angle, color) -> None
+
+

Draws an arc on the given surface. For an arc with its endpoints connected +to its center use pie().

+

The two angle arguments are given in degrees and indicate the start and stop +positions of the arc. The arc is drawn in a clockwise direction from the +start_angle to the stop_angle. If start_angle == stop_angle, +nothing will be drawn

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • x (int) -- x coordinate of the center of the arc

  • +
  • y (int) -- y coordinate of the center of the arc

  • +
  • r (int) -- radius of the arc

  • +
  • start_angle (int) -- start angle in degrees

  • +
  • stop_angle (int) -- stop angle in degrees

  • +
  • color (Color or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+

Note

+

This function uses degrees while the pygame.draw.arc()draw an elliptical arc function +uses radians.

+
+
+ +
+
+pygame.gfxdraw.pie()
+
+
draw a pie
+
pie(surface, x, y, r, start_angle, stop_angle, color) -> None
+
+

Draws an unfilled pie on the given surface. A pie is an arc() with its +endpoints connected to its center.

+

The two angle arguments are given in degrees and indicate the start and stop +positions of the pie. The pie is drawn in a clockwise direction from the +start_angle to the stop_angle. If start_angle == stop_angle, +a straight line will be drawn from the center position at the given angle, +to a length of the radius.

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • x (int) -- x coordinate of the center of the pie

  • +
  • y (int) -- y coordinate of the center of the pie

  • +
  • r (int) -- radius of the pie

  • +
  • start_angle (int) -- start angle in degrees

  • +
  • stop_angle (int) -- stop angle in degrees

  • +
  • color (Color or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+ +
+
+pygame.gfxdraw.trigon()
+
+
draw a trigon/triangle
+
trigon(surface, x1, y1, x2, y2, x3, y3, color) -> None
+
+

Draws an unfilled trigon (triangle) on the given surface. For a filled +trigon use filled_trigon().

+

A trigon can also be drawn using polygon() e.g. +polygon(surface, ((x1, y1), (x2, y2), (x3, y3)), color)

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • x1 (int) -- x coordinate of the first corner of the trigon

  • +
  • y1 (int) -- y coordinate of the first corner of the trigon

  • +
  • x2 (int) -- x coordinate of the second corner of the trigon

  • +
  • y2 (int) -- y coordinate of the second corner of the trigon

  • +
  • x3 (int) -- x coordinate of the third corner of the trigon

  • +
  • y3 (int) -- y coordinate of the third corner of the trigon

  • +
  • color (Color or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+ +
+
+pygame.gfxdraw.aatrigon()
+
+
draw an antialiased trigon/triangle
+
aatrigon(surface, x1, y1, x2, y2, x3, y3, color) -> None
+
+

Draws an unfilled antialiased trigon (triangle) on the given surface.

+

An aatrigon can also be drawn using aapolygon() e.g. +aapolygon(surface, ((x1, y1), (x2, y2), (x3, y3)), color)

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • x1 (int) -- x coordinate of the first corner of the trigon

  • +
  • y1 (int) -- y coordinate of the first corner of the trigon

  • +
  • x2 (int) -- x coordinate of the second corner of the trigon

  • +
  • y2 (int) -- y coordinate of the second corner of the trigon

  • +
  • x3 (int) -- x coordinate of the third corner of the trigon

  • +
  • y3 (int) -- y coordinate of the third corner of the trigon

  • +
  • color (Color or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+ +
+
+pygame.gfxdraw.filled_trigon()
+
+
draw a filled trigon/triangle
+
filled_trigon(surface, x1, y1, x2, y2, x3, y3, color) -> None
+
+

Draws a filled trigon (triangle) on the given surface. For an unfilled +trigon use trigon().

+

A filled_trigon can also be drawn using filled_polygon() e.g. +filled_polygon(surface, ((x1, y1), (x2, y2), (x3, y3)), color)

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • x1 (int) -- x coordinate of the first corner of the trigon

  • +
  • y1 (int) -- y coordinate of the first corner of the trigon

  • +
  • x2 (int) -- x coordinate of the second corner of the trigon

  • +
  • y2 (int) -- y coordinate of the second corner of the trigon

  • +
  • x3 (int) -- x coordinate of the third corner of the trigon

  • +
  • y3 (int) -- y coordinate of the third corner of the trigon

  • +
  • color (Color or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+ +
+
+pygame.gfxdraw.polygon()
+
+
draw a polygon
+
polygon(surface, points, color) -> None
+
+

Draws an unfilled polygon on the given surface. For a filled polygon use +filled_polygon().

+

The adjacent coordinates in the points argument, as well as the first +and last points, will be connected by line segments. +e.g. For the points [(x1, y1), (x2, y2), (x3, y3)] a line segment will +be drawn from (x1, y1) to (x2, y2), from (x2, y2) to +(x3, y3), and from (x3, y3) to (x1, y1).

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • points (tuple(coordinate) or list(coordinate)) -- a sequence of 3 or more (x, y) coordinates, where each +coordinate in the sequence must be a +tuple/list/pygame.math.Vector2a 2-Dimensional Vector of 2 ints/floats (float values +will be truncated)

  • +
  • color (Color or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
Raises
+
    +
  • ValueError -- if len(points) < 3 (must have at least 3 points)

  • +
  • IndexError -- if len(coordinate) < 2 (each coordinate must have +at least 2 items)

  • +
+
+
+
+ +
+
+pygame.gfxdraw.aapolygon()
+
+
draw an antialiased polygon
+
aapolygon(surface, points, color) -> None
+
+

Draws an unfilled antialiased polygon on the given surface.

+

The adjacent coordinates in the points argument, as well as the first +and last points, will be connected by line segments. +e.g. For the points [(x1, y1), (x2, y2), (x3, y3)] a line segment will +be drawn from (x1, y1) to (x2, y2), from (x2, y2) to +(x3, y3), and from (x3, y3) to (x1, y1).

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • points (tuple(coordinate) or list(coordinate)) -- a sequence of 3 or more (x, y) coordinates, where each +coordinate in the sequence must be a +tuple/list/pygame.math.Vector2a 2-Dimensional Vector of 2 ints/floats (float values +will be truncated)

  • +
  • color (Color or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
Raises
+
    +
  • ValueError -- if len(points) < 3 (must have at least 3 points)

  • +
  • IndexError -- if len(coordinate) < 2 (each coordinate must have +at least 2 items)

  • +
+
+
+
+ +
+
+pygame.gfxdraw.filled_polygon()
+
+
draw a filled polygon
+
filled_polygon(surface, points, color) -> None
+
+

Draws a filled polygon on the given surface. For an unfilled polygon use +polygon().

+

The adjacent coordinates in the points argument, as well as the first +and last points, will be connected by line segments. +e.g. For the points [(x1, y1), (x2, y2), (x3, y3)] a line segment will +be drawn from (x1, y1) to (x2, y2), from (x2, y2) to +(x3, y3), and from (x3, y3) to (x1, y1).

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • points (tuple(coordinate) or list(coordinate)) -- a sequence of 3 or more (x, y) coordinates, where each +coordinate in the sequence must be a +tuple/list/pygame.math.Vector2a 2-Dimensional Vector of 2 ints/floats (float values +will be truncated)`

  • +
  • color (Color or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
Raises
+
    +
  • ValueError -- if len(points) < 3 (must have at least 3 points)

  • +
  • IndexError -- if len(coordinate) < 2 (each coordinate must have +at least 2 items)

  • +
+
+
+
+ +
+
+pygame.gfxdraw.textured_polygon()
+
+
draw a textured polygon
+
textured_polygon(surface, points, texture, tx, ty) -> None
+
+

Draws a textured polygon on the given surface. For better performance, the +surface and the texture should have the same format.

+

A per-pixel alpha texture blit to a per-pixel alpha surface will differ from +a pygame.Surface.blit()draw one image onto another blit. Also, a per-pixel alpha texture cannot be +used with an 8-bit per pixel destination.

+

The adjacent coordinates in the points argument, as well as the first +and last points, will be connected by line segments. +e.g. For the points [(x1, y1), (x2, y2), (x3, y3)] a line segment will +be drawn from (x1, y1) to (x2, y2), from (x2, y2) to +(x3, y3), and from (x3, y3) to (x1, y1).

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • points (tuple(coordinate) or list(coordinate)) -- a sequence of 3 or more (x, y) coordinates, where each +coordinate in the sequence must be a +tuple/list/pygame.math.Vector2a 2-Dimensional Vector of 2 ints/floats (float values +will be truncated)

  • +
  • texture (Surface) -- texture to draw on the polygon

  • +
  • tx (int) -- x offset of the texture

  • +
  • ty (int) -- y offset of the texture

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
Raises
+
    +
  • ValueError -- if len(points) < 3 (must have at least 3 points)

  • +
  • IndexError -- if len(coordinate) < 2 (each coordinate must have +at least 2 items)

  • +
+
+
+
+ +
+
+pygame.gfxdraw.bezier()
+
+
draw a Bezier curve
+
bezier(surface, points, steps, color) -> None
+
+

Draws a Bézier curve on the given surface.

+
+
Parameters
+
    +
  • surface (Surface) -- surface to draw on

  • +
  • points (tuple(coordinate) or list(coordinate)) -- a sequence of 3 or more (x, y) coordinates used to form a +curve, where each coordinate in the sequence must be a +tuple/list/pygame.math.Vector2a 2-Dimensional Vector of 2 ints/floats (float values +will be truncated)

  • +
  • steps (int) -- number of steps for the interpolation, the minimum is 2

  • +
  • color (Color or tuple(int, int, int, [int])) -- color to draw with, the alpha value is optional if using a +tuple (RGB[A])

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
Raises
+
    +
  • ValueError -- if steps < 2

  • +
  • ValueError -- if len(points) < 3 (must have at least 3 points)

  • +
  • IndexError -- if len(coordinate) < 2 (each coordinate must have +at least 2 items)

  • +
+
+
+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/image.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/image.html new file mode 100644 index 00000000..350d4117 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/image.html @@ -0,0 +1,564 @@ + + + + + + + + + pygame.image — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.image
+
+
pygame module for image transfer
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+load new image from a file (or file-like object)
+save an image to file (or file-like object)
+get version number of the SDL_Image library being used
+test if extended image formats can be loaded
+transfer image to byte buffer
+transfer image to byte buffer
+create new Surface from a byte buffer
+create new Surface from a byte buffer
+create a new Surface that shares data inside a bytes buffer
+load new BMP image from a file (or file-like object)
+load an image from a file (or file-like object)
+save a png/jpg image to file (or file-like object)
+

The image module contains functions for loading and saving pictures, as well as +transferring Surfaces to formats usable by other packages.

+

Note that there is no Image class; an image is loaded as a Surface object. The +Surface class allows manipulation (drawing lines, setting pixels, capturing +regions, etc.).

+

In the vast majority of installations, pygame is built to support extended +formats, using the SDL_Image library behind the scenes. However, some +installations may only support uncompressed BMP images. With full image +support, the pygame.image.load()load new image from a file (or file-like object) function can load the following +formats.

+
+
    +
  • BMP

  • +
  • GIF (non-animated)

  • +
  • JPEG

  • +
  • LBM (and PBM, PGM, PPM)

  • +
  • PCX

  • +
  • PNG

  • +
  • PNM

  • +
  • SVG (limited support, using Nano SVG)

  • +
  • TGA (uncompressed)

  • +
  • TIFF

  • +
  • WEBP

  • +
  • XPM

  • +
+
+
+

New in pygame 2.0: Loading SVG, WebP, PNM

+
+

Saving images only supports a limited set of formats. You can save to the +following formats.

+
+
    +
  • BMP

  • +
  • JPEG

  • +
  • PNG

  • +
  • TGA

  • +
+
+

JPEG and JPG, as well as TIF and TIFF refer to the same file format

+
+

New in pygame 1.8: Saving PNG and JPEG files.

+
+
+
+pygame.image.load()
+
+
load new image from a file (or file-like object)
+
load(filename) -> Surface
+
load(fileobj, namehint="") -> Surface
+
+

Load an image from a file source. You can pass either a filename, a Python +file-like object, or a pathlib.Path.

+

Pygame will automatically determine the image type (e.g., GIF or bitmap) +and create a new Surface object from the data. In some cases it will need to +know the file extension (e.g., GIF images should end in ".gif"). If you +pass a raw file-like object, you may also want to pass the original filename +as the namehint argument.

+

The returned Surface will contain the same color format, colorkey and alpha +transparency as the file it came from. You will often want to call +pygame.Surface.convert()change the pixel format of an image with no arguments, to create a copy that +will draw more quickly on the screen.

+

For alpha transparency, like in .png images, use the +pygame.Surface.convert_alpha()change the pixel format of an image including per pixel alphas method after loading so that the +image has per pixel transparency.

+

Pygame may not always be built to support all image formats. At minimum it +will support uncompressed BMP. If pygame.image.get_extended()test if extended image formats can be loaded +returns True, you should be able to load most images (including PNG, JPG +and GIF).

+

You should use os.path.join() for compatibility.

+
eg. asurf = pygame.image.load(os.path.join('data', 'bla.png'))
+
+
+
+ +
+
+pygame.image.save()
+
+
save an image to file (or file-like object)
+
save(Surface, filename) -> None
+
save(Surface, fileobj, namehint="") -> None
+
+

This will save your Surface as either a BMP, TGA, PNG, or +JPEG image. If the filename extension is unrecognized it will default to +TGA. Both TGA, and BMP file formats create uncompressed files. +You can pass a filename, a pathlib.Path or a Python file-like object. +For file-like object, the image is saved to TGA format unless +a namehint with a recognizable extension is passed in.

+
+

Note

+

When saving to a file-like object, it seems that for most formats, +the object needs to be flushed after saving to it to make loading +from it possible.

+
+
+

Changed in pygame 1.8: Saving PNG and JPEG files.

+
+
+

Changed in pygame 2.0.0: The namehint parameter was added to make it possible +to save other formats than TGA to a file-like object. +Saving to a file-like object with JPEG is possible.

+
+
+ +
+
+pygame.image.get_sdl_image_version()
+
+
get version number of the SDL_Image library being used
+
get_sdl_image_version(linked=True) -> None
+
get_sdl_image_version(linked=True) -> (major, minor, patch)
+
+

If pygame is built with extended image formats, then this function will +return the SDL_Image library's version number as a tuple of 3 integers +(major, minor, patch). If not, then it will return None.

+

linked=True is the default behavior and the function will return the +version of the library that Pygame is linked against, while linked=False +will return the version of the library that Pygame is compiled against.

+
+

New in pygame 2.0.0.

+
+
+

Changed in pygame 2.2.0: linked keyword argument added and default behavior changed from returning compiled version to returning linked version

+
+
+ +
+
+pygame.image.get_extended()
+
+
test if extended image formats can be loaded
+
get_extended() -> bool
+
+

If pygame is built with extended image formats this function will return +True. It is still not possible to determine which formats will be available, +but generally you will be able to load them all.

+
+ +
+
+pygame.image.tostring()
+
+
transfer image to byte buffer
+
tostring(Surface, format, flipped=False) -> bytes
+
+

Creates a string of bytes that can be transferred with the fromstring +or frombytes methods in other Python imaging packages. Some Python +image packages prefer their images in bottom-to-top format (PyOpenGL for +example). If you pass True for the flipped argument, the byte buffer +will be vertically flipped.

+

The format argument is a string of one of the following values. Note that +only 8-bit Surfaces can use the "P" format. The other formats will work for +any Surface. Also note that other Python image packages support more formats +than pygame.

+
+
    +
  • P, 8-bit palettized Surfaces

  • +
  • RGB, 24-bit image

  • +
  • RGBX, 32-bit image with unused space

  • +
  • RGBA, 32-bit image with an alpha channel

  • +
  • ARGB, 32-bit image with alpha channel first

  • +
  • BGRA, 32-bit image with alpha channel, red and blue channels swapped

  • +
  • RGBA_PREMULT, 32-bit image with colors scaled by alpha channel

  • +
  • ARGB_PREMULT, 32-bit image with colors scaled by alpha channel, alpha channel first

  • +
+
+
+

Note

+

it is preferred to use tobytes() as of pygame 2.1.3

+
+
+

New in pygame 2.1.3: BGRA format

+
+
+ +
+
+pygame.image.tobytes()
+
+
transfer image to byte buffer
+
tobytes(Surface, format, flipped=False) -> bytes
+
+

Creates a string of bytes that can be transferred with the fromstring +or frombytes methods in other Python imaging packages. Some Python +image packages prefer their images in bottom-to-top format (PyOpenGL for +example). If you pass True for the flipped argument, the byte buffer +will be vertically flipped.

+

The format argument is a string of one of the following values. Note that +only 8-bit Surfaces can use the "P" format. The other formats will work for +any Surface. Also note that other Python image packages support more formats +than pygame.

+
+
    +
  • P, 8-bit palettized Surfaces

  • +
  • RGB, 24-bit image

  • +
  • RGBX, 32-bit image with unused space

  • +
  • RGBA, 32-bit image with an alpha channel

  • +
  • ARGB, 32-bit image with alpha channel first

  • +
  • BGRA, 32-bit image with alpha channel, red and blue channels swapped

  • +
  • RGBA_PREMULT, 32-bit image with colors scaled by alpha channel

  • +
  • ARGB_PREMULT, 32-bit image with colors scaled by alpha channel, alpha channel first

  • +
+
+
+

Note

+

this function is an alias for tostring(). The use of this +function is recommended over tostring() as of pygame 2.1.3. +This function was introduced so it matches nicely with other +libraries (PIL, numpy, etc), and with people's expectations.

+
+
+

New in pygame 2.1.3.

+
+
+ +
+
+pygame.image.fromstring()
+
+
create new Surface from a byte buffer
+
fromstring(bytes, size, format, flipped=False) -> Surface
+
+

This function takes arguments similar to pygame.image.tostring()transfer image to byte buffer. +The size argument is a pair of numbers representing the width and height. +Once the new Surface is created it is independent from the memory of the +bytes passed in.

+

The bytes and format passed must compute to the exact size of image +specified. Otherwise a ValueError will be raised.

+

See the pygame.image.frombuffer()create a new Surface that shares data inside a bytes buffer method for a potentially faster +way to transfer images into pygame.

+
+

Note

+

it is preferred to use frombytes() as of pygame 2.1.3

+
+
+ +
+
+pygame.image.frombytes()
+
+
create new Surface from a byte buffer
+
frombytes(bytes, size, format, flipped=False) -> Surface
+
+

This function takes arguments similar to pygame.image.tobytes()transfer image to byte buffer. +The size argument is a pair of numbers representing the width and height. +Once the new Surface is created it is independent from the memory of the +bytes passed in.

+

The bytes and format passed must compute to the exact size of image +specified. Otherwise a ValueError will be raised.

+

See the pygame.image.frombuffer()create a new Surface that shares data inside a bytes buffer method for a potentially faster +way to transfer images into pygame.

+
+

Note

+

this function is an alias for fromstring(). The use of this +function is recommended over fromstring() as of pygame 2.1.3. +This function was introduced so it matches nicely with other +libraries (PIL, numpy, etc), and with people's expectations.

+
+
+

New in pygame 2.1.3.

+
+
+ +
+
+pygame.image.frombuffer()
+
+
create a new Surface that shares data inside a bytes buffer
+
frombuffer(buffer, size, format) -> Surface
+
+

Create a new Surface that shares pixel data directly from a buffer. This +buffer can be bytes, a bytearray, a memoryview, a +pygame.BufferProxypygame object to export a surface buffer through an array protocol, or any object that supports the buffer protocol. +This method takes similar arguments to pygame.image.fromstring()create new Surface from a byte buffer, but +is unable to vertically flip the source data.

+

This will run much faster than pygame.image.fromstring()create new Surface from a byte buffer, since no +pixel data must be allocated and copied.

+

It accepts the following 'format' arguments:

+
+
    +
  • P, 8-bit palettized Surfaces

  • +
  • RGB, 24-bit image

  • +
  • BGR, 24-bit image, red and blue channels swapped.

  • +
  • RGBX, 32-bit image with unused space

  • +
  • RGBA, 32-bit image with an alpha channel

  • +
  • ARGB, 32-bit image with alpha channel first

  • +
  • BGRA, 32-bit image with alpha channel, red and blue channels swapped

  • +
+
+
+

New in pygame 2.1.3: BGRA format

+
+
+ +
+
+pygame.image.load_basic()
+
+
load new BMP image from a file (or file-like object)
+
load_basic(file) -> Surface
+
+

Load an image from a file source. You can pass either a filename or a Python +file-like object, or a pathlib.Path.

+

This function only supports loading "basic" image format, ie BMP +format. +This function is always available, no matter how pygame was built.

+
+ +
+
+pygame.image.load_extended()
+
+
load an image from a file (or file-like object)
+
load_extended(filename) -> Surface
+
load_extended(fileobj, namehint="") -> Surface
+
+

This function is similar to pygame.image.load()load new image from a file (or file-like object), except that this +function can only be used if pygame was built with extended image format +support.

+
+

Changed in pygame 2.0.1: This function is always available, but raises an +NotImplementedError if extended image formats are +not supported. +Previously, this function may or may not be +available, depending on the state of extended image +format support.

+
+
+ +
+
+pygame.image.save_extended()
+
+
save a png/jpg image to file (or file-like object)
+
save_extended(Surface, filename) -> None
+
save_extended(Surface, fileobj, namehint="") -> None
+
+

This will save your Surface as either a PNG or JPEG image.

+

In case the image is being saved to a file-like object, this function +uses the namehint argument to determine the format of the file being +saved. Saves to JPEG in case the namehint was not specified while +saving to a file-like object.

+
+

Changed in pygame 2.0.1: This function is always available, but raises an +NotImplementedError if extended image formats are +not supported. +Previously, this function may or may not be +available, depending on the state of extended image +format support.

+
+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/joystick.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/joystick.html new file mode 100644 index 00000000..2dcf293a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/joystick.html @@ -0,0 +1,1148 @@ + + + + + + + + + pygame.joystick — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.joystick
+
+
Pygame module for interacting with joysticks, gamepads, and trackballs.
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + +
+Initialize the joystick module.
+Uninitialize the joystick module.
+Returns True if the joystick module is initialized.
+Returns the number of joysticks.
+Create a new Joystick object.
+

The joystick module manages the joystick devices on a computer. +Joystick devices include trackballs and video-game-style +gamepads, and the module allows the use of multiple buttons and "hats". +Computers may manage multiple joysticks at a time.

+

Each instance of the Joystick class represents one gaming device plugged +into the computer. If a gaming pad has multiple joysticks on it, then the +joystick object can actually represent multiple joysticks on that single +game device.

+

For a quick way to initialise the joystick module and get a list of Joystick instances +use the following code:

+
pygame.joystick.init()
+joysticks = [pygame.joystick.Joystick(x) for x in range(pygame.joystick.get_count())]
+
+
+

The following event types will be generated by the joysticks

+
JOYAXISMOTION JOYBALLMOTION JOYBUTTONDOWN JOYBUTTONUP JOYHATMOTION
+
+
+

And in pygame 2, which supports hotplugging:

+
JOYDEVICEADDED JOYDEVICEREMOVED
+
+
+

Note that in pygame 2, joysticks events use a unique "instance ID". The device index +passed in the constructor to a Joystick object is not unique after devices have +been added and removed. You must call Joystick.get_instance_id() to find +the instance ID that was assigned to a Joystick on opening.

+

The event queue needs to be pumped frequently for some of the methods to work. +So call one of pygame.event.get, pygame.event.wait, or pygame.event.pump regularly.

+

To be able to get joystick events and update the joystick objects while the window +is not in focus, you may set the SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS environment +variable. See environment variables for more details.

+
+
+pygame.joystick.init()
+
+
Initialize the joystick module.
+
init() -> None
+
+

This function is called automatically by pygame.init().

+

It initializes the joystick module. The module must be initialized before any +other functions will work.

+

It is safe to call this function more than once.

+
+ +
+
+pygame.joystick.quit()
+
+
Uninitialize the joystick module.
+
quit() -> None
+
+

Uninitialize the joystick module. After you call this any existing joystick +objects will no longer work.

+

It is safe to call this function more than once.

+
+ +
+
+pygame.joystick.get_init()
+
+
Returns True if the joystick module is initialized.
+
get_init() -> bool
+
+

Test if the pygame.joystick.init() function has been called.

+
+ +
+
+pygame.joystick.get_count()
+
+
Returns the number of joysticks.
+
get_count() -> count
+
+

Return the number of joystick devices on the system. The count will be 0 +if there are no joysticks on the system.

+

When you create Joystick objects using Joystick(id), you pass an integer +that must be lower than this count.

+
+ +
+
+pygame.joystick.Joystick
+
+
Create a new Joystick object.
+
Joystick(id) -> Joystick
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+initialize the Joystick
+uninitialize the Joystick
+check if the Joystick is initialized
+get the device index (deprecated)
+get the joystick instance id
+get the joystick GUID
+get the approximate power status of the device
+get the Joystick system name
+get the number of axes on a Joystick
+get the current position of an axis
+get the number of trackballs on a Joystick
+get the relative position of a trackball
+get the number of buttons on a Joystick
+get the current button state
+get the number of hat controls on a Joystick
+get the position of a joystick hat
+Start a rumbling effect
+Stop any rumble effect playing
+

Create a new joystick to access a physical device. The id argument must be a +value from 0 to pygame.joystick.get_count() - 1.

+

Joysticks are initialised on creation and are shut down when deallocated. +Once the device is initialized the pygame event queue will start receiving +events about its input.

+
+

Changed in pygame 2.0.0: Joystick objects are now opened immediately on creation.

+
+
+
+init()
+
+
initialize the Joystick
+
init() -> None
+
+

Initialize the joystick, if it has been closed. It is safe to call this +even if the joystick is already initialized.

+
+

Deprecated since pygame 2.0.0: In future it will not be possible to reinitialise a closed Joystick +object. Will be removed in Pygame 2.1.

+
+
+ +
+
+quit()
+
+
uninitialize the Joystick
+
quit() -> None
+
+

Close a Joystick object. After this the pygame event queue will no longer +receive events from the device.

+

It is safe to call this more than once.

+
+ +
+
+get_init()
+
+
check if the Joystick is initialized
+
get_init() -> bool
+
+

Return True if the Joystick object is currently initialised.

+
+ +
+
+get_id()
+
+
get the device index (deprecated)
+
get_id() -> int
+
+

Returns the original device index for this device. This is the same +value that was passed to the Joystick() constructor. This method can +safely be called while the Joystick is not initialized.

+
+

Deprecated since pygame 2.0.0: The original device index is not useful in pygame 2. Use +get_instance_id() instead. Will be removed in Pygame 2.1.

+
+
+ +
+
+get_instance_id() int
+
+
get the joystick instance id
+
get_instance_id() -> int
+
+

Get the joystick instance ID. This matches the instance_id field +that is given in joystick events.

+
+

New in pygame 2.0.0dev11.

+
+
+ +
+
+get_guid() str
+
+
get the joystick GUID
+
get_guid() -> str
+
+

Get the GUID string. This identifies the exact hardware of the joystick +device.

+
+

New in pygame 2.0.0dev11.

+
+
+ +
+
+get_power_level() str
+
+
get the approximate power status of the device
+
get_power_level() -> str
+
+

Get a string giving the power status of the device.

+

One of: empty, low, medium, full, wired, max, or +unknown.

+
+

New in pygame 2.0.0dev11.

+
+
+ +
+
+get_name()
+
+
get the Joystick system name
+
get_name() -> string
+
+

Returns the system name for this joystick device. It is unknown what name +the system will give to the Joystick, but it should be a unique name that +identifies the device. This method can safely be called while the +Joystick is not initialized.

+
+ +
+
+get_numaxes()
+
+
get the number of axes on a Joystick
+
get_numaxes() -> int
+
+

Returns the number of input axes are on a Joystick. There will usually be +two for the position. Controls like rudders and throttles are treated as +additional axes.

+

The pygame.JOYAXISMOTION events will be in the range from -1.0 +to 1.0. A value of 0.0 means the axis is centered. Gamepad devices +will usually be -1, 0, or 1 with no values in between. Older +analog joystick axes will not always use the full -1 to 1 range, +and the centered value will be some area around 0.

+

Analog joysticks usually have a bit of noise in their axis, which will +generate a lot of rapid small motion events.

+
+ +
+
+get_axis()
+
+
get the current position of an axis
+
get_axis(axis_number) -> float
+
+

Returns the current position of a joystick axis. The value will range +from -1 to 1 with a value of 0 being centered. You may want +to take into account some tolerance to handle jitter, and joystick drift +may keep the joystick from centering at 0 or using the full range of +position values.

+

The axis number must be an integer from 0 to get_numaxes() - 1.

+

When using gamepads both the control sticks and the analog triggers are +usually reported as axes.

+
+ +
+
+get_numballs()
+
+
get the number of trackballs on a Joystick
+
get_numballs() -> int
+
+

Returns the number of trackball devices on a Joystick. These devices work +similar to a mouse but they have no absolute position; they only have +relative amounts of movement.

+

The pygame.JOYBALLMOTION event will be sent when the trackball is +rolled. It will report the amount of movement on the trackball.

+
+ +
+
+get_ball()
+
+
get the relative position of a trackball
+
get_ball(ball_number) -> x, y
+
+

Returns the relative movement of a joystick button. The value is a x, y +pair holding the relative movement since the last call to get_ball.

+

The ball number must be an integer from 0 to get_numballs() - 1.

+
+ +
+
+get_numbuttons()
+
+
get the number of buttons on a Joystick
+
get_numbuttons() -> int
+
+

Returns the number of pushable buttons on the joystick. These buttons +have a boolean (on or off) state.

+

Buttons generate a pygame.JOYBUTTONDOWN and pygame.JOYBUTTONUP +event when they are pressed and released.

+
+ +
+
+get_button()
+
+
get the current button state
+
get_button(button) -> bool
+
+

Returns the current state of a joystick button.

+
+ +
+
+get_numhats()
+
+
get the number of hat controls on a Joystick
+
get_numhats() -> int
+
+

Returns the number of joystick hats on a Joystick. Hat devices are like +miniature digital joysticks on a joystick. Each hat has two axes of +input.

+

The pygame.JOYHATMOTION event is generated when the hat changes +position. The position attribute for the event contains a pair of +values that are either -1, 0, or 1. A position of (0, 0) +means the hat is centered.

+
+ +
+
+get_hat()
+
+
get the position of a joystick hat
+
get_hat(hat_number) -> x, y
+
+

Returns the current position of a position hat. The position is given as +two values representing the x and y position for the hat. (0, 0) +means centered. A value of -1 means left/down and a value of 1 means +right/up: so (-1, 0) means left; (1, 0) means right; (0, 1) means +up; (1, 1) means upper-right; etc.

+

This value is digital, i.e., each coordinate can be -1, 0 or 1 +but never in-between.

+

The hat number must be between 0 and get_numhats() - 1.

+
+ +
+
+rumble()
+
+
Start a rumbling effect
+
rumble(low_frequency, high_frequency, duration) -> bool
+
+

Start a rumble effect on the joystick, with the specified strength ranging +from 0 to 1. Duration is length of the effect, in ms. Setting the duration +to 0 will play the effect until another one overwrites it or +Joystick.stop_rumble() is called. If an effect is already +playing, then it will be overwritten.

+

Returns True if the rumble was played successfully or False if the +joystick does not support it or pygame.version.SDL()tupled integers of the SDL library version is below 2.0.9.

+
+

New in pygame 2.0.2.

+
+
+ +
+
+stop_rumble()
+
+
Stop any rumble effect playing
+
stop_rumble() -> None
+
+

Stops any rumble effect playing on the joystick. See +Joystick.rumble() for more information.

+
+

New in pygame 2.0.2.

+
+
+ +
+ +
+joystick module example +
+

Example code for joystick module.

+
+
+
import pygame
+
+pygame.init()
+
+
+# This is a simple class that will help us print to the screen.
+# It has nothing to do with the joysticks, just outputting the
+# information.
+class TextPrint:
+    def __init__(self):
+        self.reset()
+        self.font = pygame.font.Font(None, 25)
+
+    def tprint(self, screen, text):
+        text_bitmap = self.font.render(text, True, (0, 0, 0))
+        screen.blit(text_bitmap, (self.x, self.y))
+        self.y += self.line_height
+
+    def reset(self):
+        self.x = 10
+        self.y = 10
+        self.line_height = 15
+
+    def indent(self):
+        self.x += 10
+
+    def unindent(self):
+        self.x -= 10
+
+
+def main():
+    # Set the width and height of the screen (width, height), and name the window.
+    screen = pygame.display.set_mode((500, 700))
+    pygame.display.set_caption("Joystick example")
+
+    # Used to manage how fast the screen updates.
+    clock = pygame.time.Clock()
+
+    # Get ready to print.
+    text_print = TextPrint()
+
+    # This dict can be left as-is, since pygame will generate a
+    # pygame.JOYDEVICEADDED event for every joystick connected
+    # at the start of the program.
+    joysticks = {}
+
+    done = False
+    while not done:
+        # Event processing step.
+        # Possible joystick events: JOYAXISMOTION, JOYBALLMOTION, JOYBUTTONDOWN,
+        # JOYBUTTONUP, JOYHATMOTION, JOYDEVICEADDED, JOYDEVICEREMOVED
+        for event in pygame.event.get():
+            if event.type == pygame.QUIT:
+                done = True  # Flag that we are done so we exit this loop.
+
+            if event.type == pygame.JOYBUTTONDOWN:
+                print("Joystick button pressed.")
+                if event.button == 0:
+                    joystick = joysticks[event.instance_id]
+                    if joystick.rumble(0, 0.7, 500):
+                        print(f"Rumble effect played on joystick {event.instance_id}")
+
+            if event.type == pygame.JOYBUTTONUP:
+                print("Joystick button released.")
+
+            # Handle hotplugging
+            if event.type == pygame.JOYDEVICEADDED:
+                # This event will be generated when the program starts for every
+                # joystick, filling up the list without needing to create them manually.
+                joy = pygame.joystick.Joystick(event.device_index)
+                joysticks[joy.get_instance_id()] = joy
+                print(f"Joystick {joy.get_instance_id()} connencted")
+
+            if event.type == pygame.JOYDEVICEREMOVED:
+                del joysticks[event.instance_id]
+                print(f"Joystick {event.instance_id} disconnected")
+
+        # Drawing step
+        # First, clear the screen to white. Don't put other drawing commands
+        # above this, or they will be erased with this command.
+        screen.fill((255, 255, 255))
+        text_print.reset()
+
+        # Get count of joysticks.
+        joystick_count = pygame.joystick.get_count()
+
+        text_print.tprint(screen, f"Number of joysticks: {joystick_count}")
+        text_print.indent()
+
+        # For each joystick:
+        for joystick in joysticks.values():
+            jid = joystick.get_instance_id()
+
+            text_print.tprint(screen, f"Joystick {jid}")
+            text_print.indent()
+
+            # Get the name from the OS for the controller/joystick.
+            name = joystick.get_name()
+            text_print.tprint(screen, f"Joystick name: {name}")
+
+            guid = joystick.get_guid()
+            text_print.tprint(screen, f"GUID: {guid}")
+
+            power_level = joystick.get_power_level()
+            text_print.tprint(screen, f"Joystick's power level: {power_level}")
+
+            # Usually axis run in pairs, up/down for one, and left/right for
+            # the other. Triggers count as axes.
+            axes = joystick.get_numaxes()
+            text_print.tprint(screen, f"Number of axes: {axes}")
+            text_print.indent()
+
+            for i in range(axes):
+                axis = joystick.get_axis(i)
+                text_print.tprint(screen, f"Axis {i} value: {axis:>6.3f}")
+            text_print.unindent()
+
+            buttons = joystick.get_numbuttons()
+            text_print.tprint(screen, f"Number of buttons: {buttons}")
+            text_print.indent()
+
+            for i in range(buttons):
+                button = joystick.get_button(i)
+                text_print.tprint(screen, f"Button {i:>2} value: {button}")
+            text_print.unindent()
+
+            hats = joystick.get_numhats()
+            text_print.tprint(screen, f"Number of hats: {hats}")
+            text_print.indent()
+
+            # Hat position. All or nothing for direction, not a float like
+            # get_axis(). Position is a tuple of int values (x, y).
+            for i in range(hats):
+                hat = joystick.get_hat(i)
+                text_print.tprint(screen, f"Hat {i} value: {str(hat)}")
+            text_print.unindent()
+
+            text_print.unindent()
+
+        # Go ahead and update the screen with what we've drawn.
+        pygame.display.flip()
+
+        # Limit to 30 frames per second.
+        clock.tick(30)
+
+
+if __name__ == "__main__":
+    main()
+    # If you forget this line, the program will 'hang'
+    # on exit if running from IDLE.
+    pygame.quit()
+
+
+
+ +
+
+

Controller mappings are drawn from the underlying SDL library which pygame uses and they differ +between pygame 1 and pygame 2. Below are a couple of mappings for three popular controllers.

+

Axis and hat mappings are listed from -1 to +1.

+
+

Nintendo Switch Left Joy-Con (pygame 2.x)

+

The Nintendo Switch Left Joy-Con has 4 axes, 11 buttons, and 0 hats. The values for the 4 axes never change. +The controller is recognized as "Wireless Gamepad"

+
    +
  • Buttons:

    +
    D-pad Up        - Button 0
    +D-pad Down      - Button 1
    +D-pad Left      - Button 2
    +D-pad Right     - Button 3
    +SL              - Button 4
    +SR              - Button 5
    +-               - Button 8
    +Stick In        - Button 10
    +Capture         - Button 13
    +L               - Button 14
    +ZL              - Button 15
    +
    +
    +
  • +
  • Hat/JoyStick:

    +
    Down -> Up      - Y Axis
    +Left -> Right   - X Axis
    +
    +
    +
  • +
+
+
+

Nintendo Switch Right Joy-Con (pygame 2.x)

+

The Nintendo Switch Right Joy-Con has 4 axes, 11 buttons, and 0 hats. The values for the 4 axes never change. +The controller is recognized as "Wireless Gamepad"

+
    +
  • Buttons:

    +
    A Button        - Button 0
    +B Button        - Button 1
    +X Button        - Button 2
    +Y Button        - Button 3
    +SL              - Button 4
    +SR              - Button 5
    ++               - Button 9
    +Stick In        - Button 11
    +Home            - Button 12
    +R               - Button 14
    +ZR              - Button 15
    +
    +
    +
  • +
  • Hat/JoyStick:

    +
    Down -> Up      - Y Axis
    +Left -> Right   - X Axis
    +
    +
    +
  • +
+
+
+

Nintendo Switch Pro Controller (pygame 2.x)

+

The Nintendo Switch Pro Controller has 6 axes, 16 buttons, and 0 hats. +The controller is recognized as "Nintendo Switch Pro Controller".

+
    +
  • Left Stick:

    +
    Left -> Right   - Axis 0
    +Up -> Down      - Axis 1
    +
    +
    +
  • +
  • Right Stick:

    +
    Left -> Right   - Axis 2
    +Up -> Down      - Axis 3
    +
    +
    +
  • +
  • Left Trigger:

    +
    Out -> In       - Axis 4
    +
    +
    +
  • +
  • Right Trigger:

    +
    Out -> In       - Axis 5
    +
    +
    +
  • +
  • Buttons:

    +
    A Button        - Button 0
    +B Button        - Button 1
    +X Button        - Button 2
    +Y Button        - Button 3
    +- Button        - Button 4
    +Home Button     - Button 5
    ++ Button        - Button 6
    +L. Stick In     - Button 7
    +R. Stick In     - Button 8
    +Left Bumper     - Button 9
    +Right Bumper    - Button 10
    +D-pad Up        - Button 11
    +D-pad Down      - Button 12
    +D-pad Left      - Button 13
    +D-pad Right     - Button 14
    +Capture Button  - Button 15
    +
    +
    +
  • +
+
+
+

XBox 360 Controller (pygame 2.x)

+

The Xbox 360 controller mapping has 6 axes, 11 buttons and 1 hat. +The controller is recognized as "Xbox 360 Controller".

+
    +
  • Left Stick:

    +
    Left -> Right   - Axis 0
    +Up   -> Down    - Axis 1
    +
    +
    +
  • +
  • Right Stick:

    +
    Left -> Right   - Axis 3
    +Up   -> Down    - Axis 4
    +
    +
    +
  • +
  • Left Trigger:

    +
    Out -> In       - Axis 2
    +
    +
    +
  • +
  • Right Trigger:

    +
    Out -> In       - Axis 5
    +
    +
    +
  • +
  • Buttons:

    +
    A Button        - Button 0
    +B Button        - Button 1
    +X Button        - Button 2
    +Y Button        - Button 3
    +Left Bumper     - Button 4
    +Right Bumper    - Button 5
    +Back Button     - Button 6
    +Start Button    - Button 7
    +L. Stick In     - Button 8
    +R. Stick In     - Button 9
    +Guide Button    - Button 10
    +
    +
    +
  • +
  • Hat/D-pad:

    +
    Down -> Up      - Y Axis
    +Left -> Right   - X Axis
    +
    +
    +
  • +
+
+
+

Playstation 4 Controller (pygame 2.x)

+

The PlayStation 4 controller mapping has 6 axes and 16 buttons. +The controller is recognized as "PS4 Controller".

+
    +
  • Left Stick:

    +
    Left -> Right   - Axis 0
    +Up   -> Down    - Axis 1
    +
    +
    +
  • +
  • Right Stick:

    +
    Left -> Right   - Axis 2
    +Up   -> Down    - Axis 3
    +
    +
    +
  • +
  • Left Trigger:

    +
    Out -> In       - Axis 4
    +
    +
    +
  • +
  • Right Trigger:

    +
    Out -> In       - Axis 5
    +
    +
    +
  • +
  • Buttons:

    +
    Cross Button    - Button 0
    +Circle Button   - Button 1
    +Square Button   - Button 2
    +Triangle Button - Button 3
    +Share Button    - Button 4
    +PS Button       - Button 5
    +Options Button  - Button 6
    +L. Stick In     - Button 7
    +R. Stick In     - Button 8
    +Left Bumper     - Button 9
    +Right Bumper    - Button 10
    +D-pad Up        - Button 11
    +D-pad Down      - Button 12
    +D-pad Left      - Button 13
    +D-pad Right     - Button 14
    +Touch Pad Click - Button 15
    +
    +
    +
  • +
+
+
+

Playstation 5 Controller (pygame 2.x)

+

The PlayStation 5 controller mapping has 6 axes, 13 buttons, and 1 hat. +The controller is recognized as "Sony Interactive Entertainment Wireless Controller".

+
    +
  • Left Stick:

    +
    Left -> Right   - Axis 0
    +Up   -> Down    - Axis 1
    +
    +
    +
  • +
  • Right Stick:

    +
    Left -> Right   - Axis 3
    +Up   -> Down    - Axis 4
    +
    +
    +
  • +
  • Left Trigger:

    +
    Out -> In       - Axis 2
    +
    +
    +
  • +
  • Right Trigger:

    +
    Out -> In       - Axis 5
    +
    +
    +
  • +
  • Buttons:

    +
    Cross Button    - Button 0
    +Circle Button   - Button 1
    +Square Button   - Button 2
    +Triangle Button - Button 3
    +Left Bumper     - Button 4
    +Right Bumper    - Button 5
    +Left Trigger    - Button 6
    +Right Trigger   - Button 7
    +Share Button    - Button 8
    +Options Button  - Button 9
    +PS Button       - Button 10
    +Left Stick in   - Button 11
    +Right Stick in  - Button 12
    +
    +
    +
  • +
  • Hat/D-pad:

    +
    Down -> Up      - Y Axis
    +Left -> Right   - X Axis
    +
    +
    +
  • +
+
+
+

XBox 360 Controller (pygame 1.x)

+

The Xbox 360 controller mapping has 5 axes, 10 buttons, and 1 hat. +The controller is recognized as "Controller (XBOX 360 For Windows)".

+
    +
  • Left Stick:

    +
    Left -> Right   - Axis 0
    +Up   -> Down    - Axis 1
    +
    +
    +
  • +
  • Right Stick:

    +
    Left -> Right   - Axis 4
    +Up   -> Down    - Axis 3
    +
    +
    +
  • +
  • Left Trigger & Right Trigger:

    +
    RT -> LT        - Axis 2
    +
    +
    +
  • +
  • Buttons:

    +
    A Button        - Button 0
    +B Button        - Button 1
    +X Button        - Button 2
    +Y Button        - Button 3
    +Left Bumper     - Button 4
    +Right Bumper    - Button 5
    +Back Button     - Button 6
    +Start Button    - Button 7
    +L. Stick In     - Button 8
    +R. Stick In     - Button 9
    +
    +
    +
  • +
  • Hat/D-pad:

    +
    Down -> Up      - Y Axis
    +Left -> Right   - X Axis
    +
    +
    +
  • +
+
+
+

Playstation 4 Controller (pygame 1.x)

+

The PlayStation 4 controller mapping has 6 axes, 14 buttons, and 1 hat. +The controller is recognized as "Wireless Controller".

+
    +
  • Left Stick:

    +
    Left -> Right   - Axis 0
    +Up   -> Down    - Axis 1
    +
    +
    +
  • +
  • Right Stick:

    +
    Left -> Right   - Axis 2
    +Up   -> Down    - Axis 3
    +
    +
    +
  • +
  • Left Trigger:

    +
    Out -> In       - Axis 5
    +
    +
    +
  • +
  • Right Trigger:

    +
    Out -> In       - Axis 4
    +
    +
    +
  • +
  • Buttons:

    +
    Cross Button    - Button 0
    +Circle Button   - Button 1
    +Square Button   - Button 2
    +Triangle Button - Button 3
    +Left Bumper     - Button 4
    +Right Bumper    - Button 5
    +L. Trigger(Full)- Button 6
    +R. Trigger(Full)- Button 7
    +Share Button    - Button 8
    +Options Button  - Button 9
    +L. Stick In     - Button 10
    +R. Stick In     - Button 11
    +PS Button       - Button 12
    +Touch Pad Click - Button 13
    +
    +
    +
  • +
  • Hat/D-pad:

    +
    Down -> Up      - Y Axis
    +Left -> Right   - X Axis
    +
    +
    +
  • +
+
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/key.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/key.html new file mode 100644 index 00000000..13c888a5 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/key.html @@ -0,0 +1,649 @@ + + + + + + + + + pygame.key — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.key
+
+
pygame module to work with the keyboard
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+true if the display is receiving keyboard input from the system
+get the state of all keyboard buttons
+determine which modifier keys are being held
+temporarily set which modifier keys are pressed
+control how held keys are repeated
+see how held keys are repeated
+get the name of a key identifier
+get the key identifier from a key name
+start handling Unicode text input events
+stop handling Unicode text input events
+controls the position of the candidate list
+

This module contains functions for dealing with the keyboard.

+

The pygame.eventpygame module for interacting with events and queues queue gets pygame.KEYDOWN and pygame.KEYUP +events when the keyboard buttons are pressed and released. Both events have +key and mod attributes.

+
+
    +
  • key: an integer ID representing every key +on the keyboard

  • +
  • mod: a bitmask of all the modifier keys +that were in a pressed state when the event occurred

  • +
+
+

The pygame.KEYDOWN event has the additional attributes unicode and +scancode.

+
+
    +
  • unicode: a single character string that is the fully translated +character entered, this takes into account the shift and composition keys

  • +
  • scancode: the platform-specific key code, which could be different from +keyboard to keyboard, but is useful for key selection of weird keys like +the multimedia keys

  • +
+
+
+

New in pygame 2.0.0: The pygame.TEXTINPUT event is preferred to the unicode attribute +of pygame.KEYDOWN. The attribute text contains the input.

+
+

The following is a list of all the constants (from pygame.localspygame constants) used to +represent keyboard keys.

+

Portability note: The integers for key constants differ between pygame 1 and 2. +Always use key constants (K_a) rather than integers directly (97) so +that your key handling code works well on both pygame 1 and pygame 2.

+
pygame
+Constant      ASCII   Description
+---------------------------------
+K_BACKSPACE   \b      backspace
+K_TAB         \t      tab
+K_CLEAR               clear
+K_RETURN      \r      return
+K_PAUSE               pause
+K_ESCAPE      ^[      escape
+K_SPACE               space
+K_EXCLAIM     !       exclaim
+K_QUOTEDBL    "       quotedbl
+K_HASH        #       hash
+K_DOLLAR      $       dollar
+K_AMPERSAND   &       ampersand
+K_QUOTE               quote
+K_LEFTPAREN   (       left parenthesis
+K_RIGHTPAREN  )       right parenthesis
+K_ASTERISK    *       asterisk
+K_PLUS        +       plus sign
+K_COMMA       ,       comma
+K_MINUS       -       minus sign
+K_PERIOD      .       period
+K_SLASH       /       forward slash
+K_0           0       0
+K_1           1       1
+K_2           2       2
+K_3           3       3
+K_4           4       4
+K_5           5       5
+K_6           6       6
+K_7           7       7
+K_8           8       8
+K_9           9       9
+K_COLON       :       colon
+K_SEMICOLON   ;       semicolon
+K_LESS        <       less-than sign
+K_EQUALS      =       equals sign
+K_GREATER     >       greater-than sign
+K_QUESTION    ?       question mark
+K_AT          @       at
+K_LEFTBRACKET [       left bracket
+K_BACKSLASH   \       backslash
+K_RIGHTBRACKET ]      right bracket
+K_CARET       ^       caret
+K_UNDERSCORE  _       underscore
+K_BACKQUOTE   `       grave
+K_a           a       a
+K_b           b       b
+K_c           c       c
+K_d           d       d
+K_e           e       e
+K_f           f       f
+K_g           g       g
+K_h           h       h
+K_i           i       i
+K_j           j       j
+K_k           k       k
+K_l           l       l
+K_m           m       m
+K_n           n       n
+K_o           o       o
+K_p           p       p
+K_q           q       q
+K_r           r       r
+K_s           s       s
+K_t           t       t
+K_u           u       u
+K_v           v       v
+K_w           w       w
+K_x           x       x
+K_y           y       y
+K_z           z       z
+K_DELETE              delete
+K_KP0                 keypad 0
+K_KP1                 keypad 1
+K_KP2                 keypad 2
+K_KP3                 keypad 3
+K_KP4                 keypad 4
+K_KP5                 keypad 5
+K_KP6                 keypad 6
+K_KP7                 keypad 7
+K_KP8                 keypad 8
+K_KP9                 keypad 9
+K_KP_PERIOD   .       keypad period
+K_KP_DIVIDE   /       keypad divide
+K_KP_MULTIPLY *       keypad multiply
+K_KP_MINUS    -       keypad minus
+K_KP_PLUS     +       keypad plus
+K_KP_ENTER    \r      keypad enter
+K_KP_EQUALS   =       keypad equals
+K_UP                  up arrow
+K_DOWN                down arrow
+K_RIGHT               right arrow
+K_LEFT                left arrow
+K_INSERT              insert
+K_HOME                home
+K_END                 end
+K_PAGEUP              page up
+K_PAGEDOWN            page down
+K_F1                  F1
+K_F2                  F2
+K_F3                  F3
+K_F4                  F4
+K_F5                  F5
+K_F6                  F6
+K_F7                  F7
+K_F8                  F8
+K_F9                  F9
+K_F10                 F10
+K_F11                 F11
+K_F12                 F12
+K_F13                 F13
+K_F14                 F14
+K_F15                 F15
+K_NUMLOCK             numlock
+K_CAPSLOCK            capslock
+K_SCROLLOCK           scrollock
+K_RSHIFT              right shift
+K_LSHIFT              left shift
+K_RCTRL               right control
+K_LCTRL               left control
+K_RALT                right alt
+K_LALT                left alt
+K_RMETA               right meta
+K_LMETA               left meta
+K_LSUPER              left Windows key
+K_RSUPER              right Windows key
+K_MODE                mode shift
+K_HELP                help
+K_PRINT               print screen
+K_SYSREQ              sysrq
+K_BREAK               break
+K_MENU                menu
+K_POWER               power
+K_EURO                Euro
+K_AC_BACK             Android back button
+
+
+

The keyboard also has a list of modifier states (from pygame.localspygame constants) that +can be assembled by bitwise-ORing them together.

+
pygame
+Constant      Description
+-------------------------
+KMOD_NONE     no modifier keys pressed
+KMOD_LSHIFT   left shift
+KMOD_RSHIFT   right shift
+KMOD_SHIFT    left shift or right shift or both
+KMOD_LCTRL    left control
+KMOD_RCTRL    right control
+KMOD_CTRL     left control or right control or both
+KMOD_LALT     left alt
+KMOD_RALT     right alt
+KMOD_ALT      left alt or right alt or both
+KMOD_LMETA    left meta
+KMOD_RMETA    right meta
+KMOD_META     left meta or right meta or both
+KMOD_CAPS     caps lock
+KMOD_NUM      num lock
+KMOD_MODE     AltGr
+
+
+

The modifier information is contained in the mod attribute of the +pygame.KEYDOWN and pygame.KEYUP events. The mod attribute is a +bitmask of all the modifier keys that were in a pressed state when the event +occurred. The modifier information can be decoded using a bitwise AND (except +for KMOD_NONE, which should be compared using equals ==). For example:

+
for event in pygame.event.get():
+    if event.type == pygame.KEYDOWN or event.type == pygame.KEYUP:
+        if event.mod == pygame.KMOD_NONE:
+            print('No modifier keys were in a pressed state when this '
+                  'event occurred.')
+        else:
+            if event.mod & pygame.KMOD_LSHIFT:
+                print('Left shift was in a pressed state when this event '
+                      'occurred.')
+            if event.mod & pygame.KMOD_RSHIFT:
+                print('Right shift was in a pressed state when this event '
+                      'occurred.')
+            if event.mod & pygame.KMOD_SHIFT:
+                print('Left shift or right shift or both were in a '
+                      'pressed state when this event occurred.')
+
+
+
+
+pygame.key.get_focused()
+
+
true if the display is receiving keyboard input from the system
+
get_focused() -> bool
+
+

Returns True when the display window has keyboard focus from the +system. If the display needs to ensure it does not lose keyboard focus, it +can use pygame.event.set_grab()control the sharing of input devices with other applications to grab all input.

+
+ +
+
+pygame.key.get_pressed()
+
+
get the state of all keyboard buttons
+
get_pressed() -> bools
+
+

Returns a sequence of boolean values representing the state of every key on +the keyboard. Use the key constant values to index the array. A True +value means that the button is pressed.

+
+

Note

+

Getting the list of pushed buttons with this function is not the proper +way to handle text entry from the user. There is no way to know the order +of keys pressed, and rapidly pushed keys can be completely unnoticed +between two calls to pygame.key.get_pressed(). There is also no way to +translate these pushed keys into a fully translated character value. See +the pygame.KEYDOWN events on the pygame.eventpygame module for interacting with events and queues queue for this +functionality.

+
+
+

New in pygame 2.2.0: The collection of bools returned by get_pressed can not be iterated +over because the indexes of the internal tuple does not correpsond to the +keycodes.

+
+
+

New in pygame 2.5.0: Iteration over the collection of bools returned by get_pressed is now +restored. However it still does not make sense to iterate over it. Currently.

+
+
+ +
+
+pygame.key.get_mods()
+
+
determine which modifier keys are being held
+
get_mods() -> int
+
+

Returns a single integer representing a bitmask of all the modifier keys +being held. Using bitwise operators you can test if specific +modifier keys are pressed.

+
+ +
+
+pygame.key.set_mods()
+
+
temporarily set which modifier keys are pressed
+
set_mods(int) -> None
+
+

Create a bitmask of the modifier key constants +you want to impose on your program.

+
+ +
+
+pygame.key.set_repeat()
+
+
control how held keys are repeated
+
set_repeat() -> None
+
set_repeat(delay) -> None
+
set_repeat(delay, interval) -> None
+
+

When the keyboard repeat is enabled, keys that are held down will generate +multiple pygame.KEYDOWN events. The delay parameter is the number of +milliseconds before the first repeated pygame.KEYDOWN event will be sent. +After that, another pygame.KEYDOWN event will be sent every interval +milliseconds. If a delay value is provided and an interval value is +not provided or is 0, then the interval will be set to the same value as +delay.

+

To disable key repeat call this function with no arguments or with delay +set to 0.

+

When pygame is initialized the key repeat is disabled.

+
+
Raises
+

ValueError -- if delay or interval is < 0

+
+
+
+

Changed in pygame 2.0.0: A ValueError is now raised (instead of a +pygame.error) if delay or interval is < 0.

+
+
+ +
+
+pygame.key.get_repeat()
+
+
see how held keys are repeated
+
get_repeat() -> (delay, interval)
+
+

Get the delay and interval keyboard repeat values. Refer to +pygame.key.set_repeat()control how held keys are repeated for a description of these values.

+
+

New in pygame 1.8.

+
+
+ +
+
+pygame.key.name()
+
+
get the name of a key identifier
+
name(key, use_compat=True) -> str
+
+

Get the descriptive name of the button from a keyboard button id constant. +Returns an empty string ("") if the key is not found.

+

If use_compat argument is True (which is the default), this function +returns the legacy name of a key where applicable. The return value is +expected to be the same across different pygame versions (provided the +corresponding key constant exists and is unique). If the return value is +passed to the key_code function, the original constant will be returned.

+

Experimental: use_compat paramater still in development for testing and feedback. It may change. +Please leave use_compat feedback with authors

+

If this argument is False, the returned name may be prettier to display +and may cover a wider range of keys than with use_compat, but there are +no guarantees that this name will be the same across different pygame +versions. If the name returned is passed to the key_code function, the +original constant is returned back (this is an implementation detail which +may change later, do not rely on this)

+
+

Changed in pygame 2.1.3: Added use_compat argument and guaranteed API stability for it

+
+
+ +
+
+pygame.key.key_code()
+
+
get the key identifier from a key name
+
key_code(name=string) -> int
+
+

Get the key identifier code from the descriptive name of the key. This +returns an integer matching one of the K_* keycodes. For example:

+
>>> pygame.key.key_code("return") == pygame.K_RETURN
+True
+>>> pygame.key.key_code("0") == pygame.K_0
+True
+>>> pygame.key.key_code("space") == pygame.K_SPACE
+True
+
+
+
+
Raises
+

ValueError -- if the key name is not known.

+
+
+
+

New in pygame 2.0.0.

+
+
+ +
+
+pygame.key.start_text_input()
+
+
start handling Unicode text input events
+
start_text_input() -> None
+
+

Start receiving pygame.TEXTEDITING and pygame.TEXTINPUT +events. If applicable, show the on-screen keyboard or IME editor.

+

For many languages, key presses will automatically generate a +corresponding pygame.TEXTINPUT event. Special keys like +escape or function keys, and certain key combinations will not +generate pygame.TEXTINPUT events.

+

In other languages, entering a single symbol may require multiple +key presses, or a language-specific user interface. In this case, +pygame.TEXTINPUT events are preferable to pygame.KEYDOWN +events for text input.

+

A pygame.TEXTEDITING event is received when an IME composition +is started or changed. It contains the composition text, length, +and editing start position within the composition (attributes +text, length, and start, respectively). +When the composition is committed (or non-IME input is received), +a pygame.TEXTINPUT event is generated.

+

Text input events handling is on by default.

+
+

New in pygame 2.0.0.

+
+
+ +
+
+pygame.key.stop_text_input()
+
+
stop handling Unicode text input events
+
stop_text_input() -> None
+
+

Stop receiving pygame.TEXTEDITING and pygame.TEXTINPUT +events. If an on-screen keyboard or IME editor was shown with +pygame.key.start_text_input(), hide it again.

+

Text input events handling is on by default.

+

To avoid triggering the IME editor or the on-screen keyboard +when the user is holding down a key during gameplay, text input +should be disabled once text entry is finished, or when the user +clicks outside of a text box.

+
+

New in pygame 2.0.0.

+
+
+ +
+
+pygame.key.set_text_input_rect()
+
+
controls the position of the candidate list
+
set_text_input_rect(Rect) -> None
+
+

This sets the rectangle used for typing with an IME. +It controls where the candidate list will open, if supported.

+
+

New in pygame 2.0.0.

+
+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/locals.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/locals.html new file mode 100644 index 00000000..91ccfa5f --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/locals.html @@ -0,0 +1,159 @@ + + + + + + + + + pygame.locals — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.locals
+
+
pygame constants
+
+

This module contains various constants used by pygame. Its contents are +automatically placed in the pygame module namespace. However, an application +can use pygame.locals to include only the pygame constants with a from +pygame.locals import *.

+

Detailed descriptions of the various constants can be found throughout the +pygame documentation. Here are the locations of some of them.

+
+
+
+
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/mask.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/mask.html new file mode 100644 index 00000000..2364f198 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/mask.html @@ -0,0 +1,1121 @@ + + + + + + + + + pygame.mask — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.mask
+
+
pygame module for image masks.
+
+ +++++ + + + + + + + + + + + + + + +
+Creates a Mask from the given surface
+Creates a mask by thresholding Surfaces
+pygame object for representing 2D bitmasks
+

Useful for fast pixel perfect collision detection. A mask uses 1 bit per-pixel +to store which parts collide.

+
+

New in pygame 1.8.

+
+
+

Changed in pygame 2.0.2: Mask functions now support keyword arguments.

+
+
+

Changed in pygame 2.0.2: Mask functions that take positions or offsets now +support pygame.math.Vector2a 2-Dimensional Vector arguments.

+
+
+
+pygame.mask.from_surface()
+
+
Creates a Mask from the given surface
+
from_surface(surface) -> Mask
+
from_surface(surface, threshold=127) -> Mask
+
+

Creates a Mask object from the given surface by setting all the +opaque pixels and not setting the transparent pixels.

+

If the surface uses a color-key, then it is used to decide which bits in +the resulting mask are set. All the pixels that are not equal to the +color-key are set and the pixels equal to the color-key are not set.

+

If a color-key is not used, then the alpha value of each pixel is used to +decide which bits in the resulting mask are set. All the pixels that have an +alpha value greater than the threshold parameter are set and the +pixels with an alpha value less than or equal to the threshold are +not set.

+
+
Parameters
+
    +
  • surface (Surface) -- the surface to create the mask from

  • +
  • threshold (int) -- (optional) the alpha threshold (default is 127) to +compare with each surface pixel's alpha value, if the surface is +color-keyed this parameter is ignored

  • +
+
+
Returns
+

a newly created Mask object from the given surface

+
+
Return type
+

Mask

+
+
+
+

Note

+

This function is used to create the masks for +pygame.sprite.collide_mask()Collision detection between two sprites, using masks..

+
+
+ +
+
+pygame.mask.from_threshold()
+
+
Creates a mask by thresholding Surfaces
+
from_threshold(surface, color) -> Mask
+
from_threshold(surface, color, threshold=(0, 0, 0, 255), othersurface=None, palette_colors=1) -> Mask
+
+

This is a more featureful method of getting a Mask from a surface.

+

If the optional othersurface is not used, all the pixels within the +threshold of the color parameter are set in the resulting mask.

+

If the optional othersurface is used, every pixel in the first surface +that is within the threshold of the corresponding pixel in +othersurface is set in the resulting mask.

+
+
Parameters
+
    +
  • surface (Surface) -- the surface to create the mask from

  • +
  • color (Color or int or tuple(int, int, int, [int]) or list[int, int, int, [int]]) -- color used to check if the surface's pixels are within the +given threshold range, this parameter is ignored if the optional +othersurface parameter is supplied

  • +
  • threshold (Color or int or tuple(int, int, int, [int]) or list[int, int, int, [int]]) -- (optional) the threshold range used to check the difference +between two colors (default is (0, 0, 0, 255))

  • +
  • othersurface (Surface) -- (optional) used to check whether the pixels of +the first surface are within the given threshold range of the pixels +from this surface (default is None)

  • +
  • palette_colors (int) -- (optional) indicates whether to use the palette +colors or not, a nonzero value causes the palette colors to be used and a +0 causes them not to be used (default is 1)

  • +
+
+
Returns
+

a newly created Mask object from the given surface

+
+
Return type
+

Mask

+
+
+
+ +
+
+pygame.mask.Mask
+
+
pygame object for representing 2D bitmasks
+
Mask(size=(width, height)) -> Mask
+
Mask(size=(width, height), fill=False) -> Mask
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Returns a new copy of the mask
+Returns the size of the mask
+Returns a Rect based on the size of the mask
+Gets the bit at the given position
+Sets the bit at the given position
+Returns the point of intersection
+Returns the number of overlapping set bits
+Returns a mask of the overlapping set bits
+Sets all bits to 1
+Sets all bits to 0
+Flips all the bits
+Resizes a mask
+Draws a mask onto another
+Erases a mask from another
+Returns the number of set bits
+Returns the centroid of the set bits
+Returns the orientation of the set bits
+Returns a list of points outlining an object
+Returns the convolution of this mask with another mask
+Returns a mask containing a connected component
+Returns a list of masks of connected components
+Returns a list of bounding rects of connected components
+Returns a surface with the mask drawn on it
+

A Mask object is used to represent a 2D bitmask. Each bit in +the mask represents a pixel. 1 is used to indicate a set bit and 0 is used +to indicate an unset bit. Set bits in a mask can be used to detect collisions +with other masks and their set bits.

+

A filled mask has all of its bits set to 1, conversely an +unfilled/cleared/empty mask has all of its bits set to 0. Masks can be +created unfilled (default) or filled by using the fill parameter. Masks +can also be cleared or filled using the pygame.mask.Mask.clear()Sets all bits to 0 and +pygame.mask.Mask.fill()Sets all bits to 1 methods respectively.

+

A mask's coordinates start in the top left corner at (0, 0) just like +pygame.Surfacepygame object for representing images. Individual bits can be accessed using the +pygame.mask.Mask.get_at()Gets the bit at the given position and pygame.mask.Mask.set_at()Sets the bit at the given position +methods.

+

The methods overlap(), overlap_area(), overlap_mask(), +draw(), erase(), and convolve() use an offset parameter +to indicate the offset of another mask's top left corner from the calling +mask's top left corner. The calling mask's top left corner is considered to +be the origin (0, 0). Offsets are a sequence of two values +(x_offset, y_offset). Positive and negative offset values are supported.

+
           0 to x (x_offset)
+           :    :
+   0 ..... +----:---------+
+   to      |    :         |
+   y .......... +-----------+
+(y_offset) |    | othermask |
+           |    +-----------+
+           | calling_mask |
+           +--------------+
+
+
+
+
Parameters
+
    +
  • size -- the dimensions of the mask (width and height)

  • +
  • fill (bool) -- (optional) create an unfilled mask (default: False) or +filled mask (True)

  • +
+
+
Returns
+

a newly created Mask object

+
+
Return type
+

Mask

+
+
+
+

Changed in pygame 2.0.0: Shallow copy support added. The Mask class supports the special +method __copy__() and shallow copying via copy.copy(mask).

+
+
+

Changed in pygame 2.0.0: Subclassing support added. The Mask class +can be used as a base class.

+
+
+

Changed in pygame 1.9.5: Added support for keyword arguments.

+
+
+

Changed in pygame 1.9.5: Added the optional keyword parameter fill.

+
+
+

Changed in pygame 1.9.5: Added support for masks with a width and/or a +height of 0.

+
+
+
+copy()
+
+
Returns a new copy of the mask
+
copy() -> Mask
+
+
+
Returns
+

a new copy of this mask, the new mask will have the same width, +height, and set/unset bits as the original

+
+
Return type
+

Mask

+
+
+
+

Note

+

If a mask subclass needs to copy any instance specific attributes +then it should override the __copy__() method. The overridden +__copy__() method needs to call super().__copy__() and then +copy the required data as in the following example code.

+
class SubMask(pygame.mask.Mask):
+    def __copy__(self):
+        new_mask = super().__copy__()
+        # Do any SubMask attribute copying here.
+        return new_mask
+
+
+
+
+

New in pygame 2.0.0.

+
+
+ +
+
+get_size()
+
+
Returns the size of the mask
+
get_size() -> (width, height)
+
+
+
Returns
+

the size of the mask, (width, height)

+
+
Return type
+

tuple(int, int)

+
+
+
+ +
+
+get_rect()
+
+
Returns a Rect based on the size of the mask
+
get_rect(**kwargs) -> Rect
+
+

Returns a new pygame.Rect()pygame object for storing rectangular coordinates object based on the size of this mask. +The rect's default position will be (0, 0) and its default width and +height will be the same as this mask's. The rect's attributes can be +altered via pygame.Rect()pygame object for storing rectangular coordinates attribute keyword arguments/values passed +into this method. As an example, a_mask.get_rect(center=(10, 5)) would +create a pygame.Rect()pygame object for storing rectangular coordinates based on the mask's size centered at the +given position.

+
+
Parameters
+

kwargs (dict) -- pygame.Rect()pygame object for storing rectangular coordinates attribute keyword arguments/values +that will be applied to the rect

+
+
Returns
+

a new pygame.Rect()pygame object for storing rectangular coordinates object based on the size of this mask +with any pygame.Rect()pygame object for storing rectangular coordinates attribute keyword arguments/values applied +to it

+
+
Return type
+

Rect

+
+
+
+

New in pygame 2.0.0.

+
+
+ +
+
+get_at()
+
+
Gets the bit at the given position
+
get_at(pos) -> int
+
+
+
Parameters
+

pos -- the position of the bit to get (x, y)

+
+
Returns
+

1 if the bit is set, 0 if the bit is not set

+
+
Return type
+

int

+
+
Raises
+

IndexError -- if the position is outside of the mask's bounds

+
+
+
+ +
+
+set_at()
+
+
Sets the bit at the given position
+
set_at(pos) -> None
+
set_at(pos, value=1) -> None
+
+
+
Parameters
+
    +
  • pos -- the position of the bit to set (x, y)

  • +
  • value (int) -- any nonzero int will set the bit to 1, 0 will set the +bit to 0 (default is 1)

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
Raises
+

IndexError -- if the position is outside of the mask's bounds

+
+
+
+ +
+
+overlap()
+
+
Returns the point of intersection
+
overlap(other, offset) -> (x, y)
+
overlap(other, offset) -> None
+
+

Returns the first point of intersection encountered between this mask and +other. A point of intersection is 2 overlapping set bits.

+

The current algorithm searches the overlapping area in +sizeof(unsigned long int) * CHAR_BIT bit wide column blocks (the value +of sizeof(unsigned long int) * CHAR_BIT is platform dependent, for +clarity it will be referred to as W). Starting at the top left corner +it checks bits 0 to W - 1 of the first row ((0, 0) to +(W - 1, 0)) then continues to the next row ((0, 1) to +(W - 1, 1)). Once this entire column block is checked, it continues to +the next one (W to 2 * W - 1). This is repeated until it finds a +point of intersection or the entire overlapping area is checked.

+
+
Parameters
+
    +
  • other (Mask) -- the other mask to overlap with this mask

  • +
  • offset -- the offset of other from this mask, for more +details refer to the Mask offset notes

  • +
+
+
Returns
+

point of intersection or None if no intersection

+
+
Return type
+

tuple(int, int) or NoneType

+
+
+
+ +
+
+overlap_area()
+
+
Returns the number of overlapping set bits
+
overlap_area(other, offset) -> numbits
+
+

Returns the number of overlapping set bits between between this mask and +other.

+

This can be useful for collision detection. An approximate collision +normal can be found by calculating the gradient of the overlapping area +through the finite difference.

+
dx = mask.overlap_area(other, (x + 1, y)) - mask.overlap_area(other, (x - 1, y))
+dy = mask.overlap_area(other, (x, y + 1)) - mask.overlap_area(other, (x, y - 1))
+
+
+
+
Parameters
+
    +
  • other (Mask) -- the other mask to overlap with this mask

  • +
  • offset -- the offset of other from this mask, for more +details refer to the Mask offset notes

  • +
+
+
Returns
+

the number of overlapping set bits

+
+
Return type
+

int

+
+
+
+ +
+
+overlap_mask()
+
+
Returns a mask of the overlapping set bits
+
overlap_mask(other, offset) -> Mask
+
+

Returns a Mask, the same size as this mask, containing the +overlapping set bits between this mask and other.

+
+
Parameters
+
    +
  • other (Mask) -- the other mask to overlap with this mask

  • +
  • offset -- the offset of other from this mask, for more +details refer to the Mask offset notes

  • +
+
+
Returns
+

a newly created Mask with the overlapping bits set

+
+
Return type
+

Mask

+
+
+
+ +
+
+fill()
+
+
Sets all bits to 1
+
fill() -> None
+
+

Sets all bits in the mask to 1.

+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+ +
+
+clear()
+
+
Sets all bits to 0
+
clear() -> None
+
+

Sets all bits in the mask to 0.

+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+ +
+
+invert()
+
+
Flips all the bits
+
invert() -> None
+
+

Flips all of the bits in the mask. All the set bits are cleared to 0 and +all the unset bits are set to 1.

+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+ +
+
+scale()
+
+
Resizes a mask
+
scale((width, height)) -> Mask
+
+

Creates a new Mask of the requested size with its bits scaled +from this mask.

+
+
Parameters
+

size -- the width and height (size) of the mask to create

+
+
Returns
+

a new Mask object with its bits scaled from this mask

+
+
Return type
+

Mask

+
+
Raises
+

ValueError -- if width < 0 or height < 0

+
+
+
+ +
+
+draw()
+
+
Draws a mask onto another
+
draw(other, offset) -> None
+
+

Performs a bitwise OR, drawing othermask onto this mask.

+
+
Parameters
+
    +
  • other (Mask) -- the mask to draw onto this mask

  • +
  • offset -- the offset of other from this mask, for more +details refer to the Mask offset notes

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+ +
+
+erase()
+
+
Erases a mask from another
+
erase(other, offset) -> None
+
+

Erases (clears) all bits set in other from this mask.

+
+
Parameters
+
    +
  • other (Mask) -- the mask to erase from this mask

  • +
  • offset -- the offset of other from this mask, for more +details refer to the Mask offset notes

  • +
+
+
Returns
+

None

+
+
Return type
+

NoneType

+
+
+
+ +
+
+count()
+
+
Returns the number of set bits
+
count() -> bits
+
+
+
Returns
+

the number of set bits in the mask

+
+
Return type
+

int

+
+
+
+ +
+
+centroid()
+
+
Returns the centroid of the set bits
+
centroid() -> (x, y)
+
+

Finds the centroid (the center mass of the set bits) for this mask.

+
+
Returns
+

a coordinate tuple indicating the centroid of the mask, it will +return (0, 0) if the mask has no bits set

+
+
Return type
+

tuple(int, int)

+
+
+
+ +
+
+angle()
+
+
Returns the orientation of the set bits
+
angle() -> theta
+
+

Finds the approximate orientation (from -90 to 90 degrees) of the set bits +in the mask. This works best if performed on a mask with only one +connected component.

+
+
Returns
+

the orientation of the set bits in the mask, it will return +0.0 if the mask has no bits set

+
+
Return type
+

float

+
+
+
+

Note

+

See connected_component() for details on how a connected +component is calculated.

+
+
+ +
+
+outline()
+
+
Returns a list of points outlining an object
+
outline() -> [(x, y), ...]
+
outline(every=1) -> [(x, y), ...]
+
+

Returns a list of points of the outline of the first connected component +encountered in the mask. To find a connected component, the mask is +searched per row (left to right) starting in the top left corner.

+

The every optional parameter skips set bits in the outline. For +example, setting it to 10 would return a list of every 10th set bit in the +outline.

+
+
Parameters
+

every (int) -- (optional) indicates the number of bits to skip over in +the outline (default is 1)

+
+
Returns
+

a list of points outlining the first connected component +encountered, an empty list is returned if the mask has no bits set

+
+
Return type
+

list[tuple(int, int)]

+
+
+
+

Note

+

See connected_component() for details on how a connected +component is calculated.

+
+
+ +
+
+convolve()
+
+
Returns the convolution of this mask with another mask
+
convolve(other) -> Mask
+
convolve(other, output=None, offset=(0, 0)) -> Mask
+
+

Convolve this mask with the given other Mask.

+
+
Parameters
+
    +
  • other (Mask) -- mask to convolve this mask with

  • +
  • output (Mask or NoneType) -- (optional) mask for output (default is None)

  • +
  • offset -- the offset of other from this mask, (default is +(0, 0))

  • +
+
+
Returns
+

a Mask with the (i - offset[0], j - offset[1]) bit +set, if shifting other (such that its bottom right corner is at +(i, j)) causes it to overlap with this mask

+

If an output Mask is specified, the output is drawn onto it and +it is returned. Otherwise a mask of size (MAX(0, width + other mask's +width - 1), MAX(0, height + other mask's height - 1)) is created and +returned.

+

+
+
Return type
+

Mask

+
+
+
+ +
+
+connected_component()
+
+
Returns a mask containing a connected component
+
connected_component() -> Mask
+
connected_component(pos) -> Mask
+
+

A connected component is a group (1 or more) of connected set bits +(orthogonally and diagonally). The SAUF algorithm, which checks 8 point +connectivity, is used to find a connected component in the mask.

+

By default this method will return a Mask containing the largest +connected component in the mask. Optionally, a bit coordinate can be +specified and the connected component containing it will be returned. If +the bit at the given location is not set, the returned Mask will +be empty (no bits set).

+
+
Parameters
+

pos -- (optional) selects the connected component that contains the +bit at this position

+
+
Returns
+

a Mask object (same size as this mask) with the largest +connected component from this mask, if this mask has no bits set then +an empty mask will be returned

+

If the pos parameter is provided then the mask returned will have +the connected component that contains this position. An empty mask will +be returned if the pos parameter selects an unset bit.

+

+
+
Return type
+

Mask

+
+
Raises
+

IndexError -- if the optional pos parameter is outside of the +mask's bounds

+
+
+
+ +
+
+connected_components()
+
+
Returns a list of masks of connected components
+
connected_components() -> [Mask, ...]
+
connected_components(minimum=0) -> [Mask, ...]
+
+

Provides a list containing a Mask object for each connected +component.

+
+
Parameters
+

minimum (int) -- (optional) indicates the minimum number of bits (to +filter out noise) per connected component (default is 0, which equates +to no minimum and is equivalent to setting it to 1, as a connected +component must have at least 1 bit set)

+
+
Returns
+

a list containing a Mask object for each connected +component, an empty list is returned if the mask has no bits set

+
+
Return type
+

list[Mask]

+
+
+
+

Note

+

See connected_component() for details on how a connected +component is calculated.

+
+
+ +
+
+get_bounding_rects()
+
+
Returns a list of bounding rects of connected components
+
get_bounding_rects() -> [Rect, ...]
+
+

Provides a list containing a bounding rect for each connected component.

+
+
Returns
+

a list containing a bounding rect for each connected component, +an empty list is returned if the mask has no bits set

+
+
Return type
+

list[Rect]

+
+
+
+

Note

+

See connected_component() for details on how a connected +component is calculated.

+
+
+ +
+
+to_surface()
+
+
Returns a surface with the mask drawn on it
+
to_surface() -> Surface
+
to_surface(surface=None, setsurface=None, unsetsurface=None, setcolor=(255, 255, 255, 255), unsetcolor=(0, 0, 0, 255), dest=(0, 0)) -> Surface
+
+

Draws this mask on the given surface. Set bits (bits set to 1) and unset +bits (bits set to 0) can be drawn onto a surface.

+
+
Parameters
+
    +
  • surface (Surface or None) -- (optional) Surface to draw mask onto, if no surface is +provided one will be created (default is None, which will cause a +surface with the parameters +Surface(size=mask.get_size(), flags=SRCALPHA, depth=32) to be +created, drawn on, and returned)

  • +
  • setsurface (Surface or None) -- (optional) use this surface's color values to draw +set bits (default is None), if this surface is smaller than the +mask any bits outside its bounds will use the setcolor value

  • +
  • unsetsurface (Surface or None) -- (optional) use this surface's color values to draw +unset bits (default is None), if this surface is smaller than the +mask any bits outside its bounds will use the unsetcolor value

  • +
  • setcolor (Color or str or int or tuple(int, int, int, [int]) or +list(int, int, int, [int]) or None) -- (optional) color to draw set bits (default is +(255, 255, 255, 255), white), use None to skip drawing the set +bits, the setsurface parameter (if set) will takes precedence over +this parameter

  • +
  • unsetcolor (Color or str or int or tuple(int, int, int, [int]) or +list(int, int, int, [int]) or None) -- (optional) color to draw unset bits (default is +(0, 0, 0, 255), black), use None to skip drawing the unset +bits, the unsetsurface parameter (if set) will takes precedence +over this parameter

  • +
  • dest (Rect or tuple(int, int) or list(int, int) or Vector2(int, int)) -- (optional) surface destination of where to position the +topleft corner of the mask being drawn (default is (0, 0)), if a +Rect is used as the dest parameter, its x and y attributes +will be used as the destination, NOTE1: rects with a negative width +or height value will not be normalized before using their x and +y values, NOTE2: this destination value is only used to +position the mask on the surface, it does not offset the setsurface +and unsetsurface from the mask, they are always aligned with the +mask (i.e. position (0, 0) on the mask always corresponds to +position (0, 0) on the setsurface and unsetsurface)

  • +
+
+
Returns
+

the surface parameter (or a newly created surface if no +surface parameter was provided) with this mask drawn on it

+
+
Return type
+

Surface

+
+
Raises
+

ValueError -- if the setsurface parameter or unsetsurface +parameter does not have the same format (bytesize/bitsize/alpha) as +the surface parameter

+
+
+
+

Note

+

To skip drawing the set bits, both setsurface and setcolor must +be None. The setsurface parameter defaults to None, but +setcolor defaults to a color value and therefore must be set to +None.

+
+
+

Note

+

To skip drawing the unset bits, both unsetsurface and +unsetcolor must be None. The unsetsurface parameter +defaults to None, but unsetcolor defaults to a color value and +therefore must be set to None.

+
+
+

New in pygame 2.0.0.

+
+
+ +
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/math.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/math.html new file mode 100644 index 00000000..32fca037 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/math.html @@ -0,0 +1,1835 @@ + + + + + + + + + pygame.math — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.math
+
+
pygame module for vector classes
+
+ +++++ + + + + + + + + + + + + + + + + + + +
+returns value clamped to min and max.
+interpolates between two values by a weight.
+a 2-Dimensional Vector
+a 3-Dimensional Vector
+

The pygame math module currently provides Vector classes in two and three +dimensions, Vector2 and Vector3 respectively.

+

They support the following numerical operations: vec + vec, vec - vec, +vec * number, number * vec, vec / number, vec // number, vec += vec, +vec -= vec, vec *= number, vec /= number, vec //= number, round(vec, ndigits=0).

+

All these operations will be performed elementwise. +In addition vec * vec will perform a scalar-product (a.k.a. dot-product). +If you want to multiply every element from vector v with every element from +vector w you can use the elementwise method: v.elementwise() * w

+

The coordinates of a vector can be retrieved or set using attributes or +subscripts

+
v = pygame.Vector3()
+
+v.x = 5
+v[1] = 2 * v.x
+print(v[1]) # 10
+
+v.x == v[0]
+v.y == v[1]
+v.z == v[2]
+
+
+

Multiple coordinates can be set using slices or swizzling

+
v = pygame.Vector2()
+v.xy = 1, 2
+v[:] = 1, 2
+
+
+
+

New in pygame 1.9.2pre.

+
+
+

Changed in pygame 1.9.4: Removed experimental notice.

+
+
+

Changed in pygame 1.9.4: Allow scalar construction like GLSL Vector2(2) == Vector2(2.0, 2.0)

+
+
+

Changed in pygame 1.9.4: pygame.mathpygame module for vector classes import not required. More convenient pygame.Vector2 and pygame.Vector3.

+
+
+

Changed in pygame 2.2.0: round returns a new vector with components rounded to the specified digits.

+
+
+
+pygame.math.clamp()
+
+
returns value clamped to min and max.
+
clamp(value, min, max) -> float
+
+

Experimental: feature still in development available for testing and feedback. It may change. +Please leave clamp feedback with authors

+

Clamps a numeric value so that it's no lower than min, and no higher +than max.

+
+

New in pygame 2.1.3.

+
+
+ +
+
+pygame.math.lerp()
+
+
interpolates between two values by a weight.
+
lerp(a, b, weight) -> float
+
+

Linearly interpolates between a and b by weight using the formula a + (b-a) * weight.

+

If weight is 0.5, lerp will return the value half-way between a +and b. When a = 10 and b = 20, lerp(a, b, 0.5) will return 15. You +can think of weight as the percentage of interpolation from a to b, 0.0 +being 0% and 1.0 being 100%.

+

lerp can be used for many things. You could rotate a sprite by a weight with +angle = lerp(0, 360, weight). You could even scale an enemy's attack value +based on the level you're playing:

+
FINAL_LEVEL = 10
+current_level = 2
+
+attack = lerp(10, 50, current_level/MAX_LEVEL) # 18
+
+
+

If you're on level 0, attack will be 10, if you're on level 10, +attack will be 50. If you're on level 5, the +result of current_level/MAX_LEVEL will be 0.5 +which represents 50%, therefore attack will be 30, which is the midpoint of 10 and 50.

+

Raises a ValueError if weight is outside the range of [0, 1].

+
+

New in pygame 2.1.3.

+
+
+ +
+
+pygame.math.Vector2
+
+
a 2-Dimensional Vector
+
Vector2() -> Vector2(0, 0)
+
Vector2(int) -> Vector2
+
Vector2(float) -> Vector2
+
Vector2(Vector2) -> Vector2
+
Vector2(x, y) -> Vector2
+
Vector2((x, y)) -> Vector2
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+calculates the dot- or scalar-product with the other vector
+calculates the cross- or vector-product
+returns the Euclidean magnitude of the vector.
+returns the squared magnitude of the vector.
+returns the Euclidean length of the vector.
+returns the squared Euclidean length of the vector.
+returns a vector with the same direction but length 1.
+normalizes the vector in place so that its length is 1.
+tests if the vector is normalized i.e. has length == 1.
+scales the vector to a given length.
+returns a vector reflected of a given normal.
+reflect the vector of a given normal in place.
+calculates the Euclidean distance to a given vector.
+calculates the squared Euclidean distance to a given vector.
+returns a vector moved toward the target by a given distance.
+moves the vector toward its target at a given distance.
+returns a linear interpolation to the given vector.
+returns a spherical interpolation to the given vector.
+The next operation will be performed elementwise.
+rotates a vector by a given angle in degrees.
+rotates a vector by a given angle in radians.
+rotates the vector by a given angle in degrees in place.
+rotates the vector by a given angle in radians in place.
+rotates the vector by a given angle in radians in place.
+calculates the angle to a given vector in degrees.
+returns a tuple with radial distance and azimuthal angle.
+Creates a Vector2(x, y) or sets x and y from a polar coordinates tuple.
+projects a vector onto another.
+Returns a copy of itself.
+Returns a copy of a vector with the magnitude clamped between max_length and min_length.
+Clamps the vector's magnitude between max_length and min_length
+Sets the coordinates of the vector.
+Determines the tolerance of vector calculations.
+

Some general information about the Vector2 class.

+
+

Changed in pygame 2.1.3: Inherited methods of vector subclasses now correctly return an instance of the +subclass instead of the superclass

+
+
+
+dot()
+
+
calculates the dot- or scalar-product with the other vector
+
dot(Vector2) -> float
+
+
+ +
+
+cross()
+
+
calculates the cross- or vector-product
+
cross(Vector2) -> float
+
+

calculates the third component of the cross-product.

+
+ +
+
+magnitude()
+
+
returns the Euclidean magnitude of the vector.
+
magnitude() -> float
+
+

calculates the magnitude of the vector which follows from the +theorem: vec.magnitude() == math.sqrt(vec.x**2 + vec.y**2)

+
+ +
+
+magnitude_squared()
+
+
returns the squared magnitude of the vector.
+
magnitude_squared() -> float
+
+

calculates the magnitude of the vector which follows from the +theorem: vec.magnitude_squared() == vec.x**2 + vec.y**2. This +is faster than vec.magnitude() because it avoids the square root.

+
+ +
+
+length()
+
+
returns the Euclidean length of the vector.
+
length() -> float
+
+

calculates the Euclidean length of the vector which follows from the +Pythagorean theorem: vec.length() == math.sqrt(vec.x**2 + vec.y**2)

+
+ +
+
+length_squared()
+
+
returns the squared Euclidean length of the vector.
+
length_squared() -> float
+
+

calculates the Euclidean length of the vector which follows from the +Pythagorean theorem: vec.length_squared() == vec.x**2 + vec.y**2. +This is faster than vec.length() because it avoids the square root.

+
+ +
+
+normalize()
+
+
returns a vector with the same direction but length 1.
+
normalize() -> Vector2
+
+

Returns a new vector that has length equal to 1 and the same +direction as self.

+
+ +
+
+normalize_ip()
+
+
normalizes the vector in place so that its length is 1.
+
normalize_ip() -> None
+
+

Normalizes the vector so that it has length equal to 1. +The direction of the vector is not changed.

+
+ +
+
+is_normalized()
+
+
tests if the vector is normalized i.e. has length == 1.
+
is_normalized() -> Bool
+
+

Returns True if the vector has length equal to 1. Otherwise +it returns False.

+
+ +
+
+scale_to_length()
+
+
scales the vector to a given length.
+
scale_to_length(float) -> None
+
+

Scales the vector so that it has the given length. The direction of the +vector is not changed. You can also scale to length 0. If the vector +is the zero vector (i.e. has length 0 thus no direction) a +ValueError is raised.

+
+ +
+
+reflect()
+
+
returns a vector reflected of a given normal.
+
reflect(Vector2) -> Vector2
+
+

Returns a new vector that points in the direction as if self would bounce +of a surface characterized by the given surface normal. The length of the +new vector is the same as self's.

+
+ +
+
+reflect_ip()
+
+
reflect the vector of a given normal in place.
+
reflect_ip(Vector2) -> None
+
+

Changes the direction of self as if it would have been reflected of a +surface with the given surface normal.

+
+ +
+
+distance_to()
+
+
calculates the Euclidean distance to a given vector.
+
distance_to(Vector2) -> float
+
+
+ +
+
+distance_squared_to()
+
+
calculates the squared Euclidean distance to a given vector.
+
distance_squared_to(Vector2) -> float
+
+
+ +
+
+move_towards()
+
+
returns a vector moved toward the target by a given distance.
+
move_towards(Vector2, float) -> Vector2
+
+

Experimental: feature still in development available for testing and feedback. It may change. +Please leave move_towards feedback with authors

+

Returns a Vector which is moved towards the given Vector by a given +distance and does not overshoot past its target Vector. +The first parameter determines the target Vector, while the second +parameter determines the delta distance. If the distance is in the +negatives, then it will move away from the target Vector.

+
+

New in pygame 2.1.3.

+
+
+ +
+
+move_towards_ip()
+
+
moves the vector toward its target at a given distance.
+
move_towards_ip(Vector2, float) -> None
+
+

Experimental: feature still in development available for testing and feedback. It may change. +Please leave move_towards_ip feedback with authors

+

Moves itself toward the given Vector at a given distance and does not +overshoot past its target Vector. +The first parameter determines the target Vector, while the second +parameter determines the delta distance. If the distance is in the +negatives, then it will move away from the target Vector.

+
+

New in pygame 2.1.3.

+
+
+ +
+
+lerp()
+
+
returns a linear interpolation to the given vector.
+
lerp(Vector2, float) -> Vector2
+
+

Returns a Vector which is a linear interpolation between self and the +given Vector. The second parameter determines how far between self and +other the result is going to be. It must be a value between 0 and 1 +where 0 means self and 1 means other will be returned.

+
+ +
+
+slerp()
+
+
returns a spherical interpolation to the given vector.
+
slerp(Vector2, float) -> Vector2
+
+

Calculates the spherical interpolation from self to the given Vector. The +second argument - often called t - must be in the range [-1, 1]. It +parametrizes where - in between the two vectors - the result should be. +If a negative value is given the interpolation will not take the +complement of the shortest path.

+
+ +
+
+elementwise()
+
+
The next operation will be performed elementwise.
+
elementwise() -> VectorElementwiseProxy
+
+

Applies the following operation to each element of the vector.

+
+ +
+
+rotate()
+
+
rotates a vector by a given angle in degrees.
+
rotate(angle) -> Vector2
+
+

Returns a vector which has the same length as self but is rotated +counterclockwise by the given angle in degrees. +(Note that due to pygame's inverted y coordinate system, the rotation +will look clockwise if displayed).

+
+ +
+
+rotate_rad()
+
+
rotates a vector by a given angle in radians.
+
rotate_rad(angle) -> Vector2
+
+

Returns a vector which has the same length as self but is rotated +counterclockwise by the given angle in radians. +(Note that due to pygame's inverted y coordinate system, the rotation +will look clockwise if displayed).

+
+

New in pygame 2.0.0.

+
+
+ +
+
+rotate_ip()
+
+
rotates the vector by a given angle in degrees in place.
+
rotate_ip(angle) -> None
+
+

Rotates the vector counterclockwise by the given angle in degrees. The +length of the vector is not changed. +(Note that due to pygame's inverted y coordinate system, the rotation +will look clockwise if displayed).

+
+ +
+
+rotate_ip_rad()
+
+
rotates the vector by a given angle in radians in place.
+
rotate_ip_rad(angle) -> None
+
+

DEPRECATED: Use rotate_rad_ip() instead.

+
+

New in pygame 2.0.0.

+
+
+

Deprecated since pygame 2.1.1.

+
+
+ +
+
+rotate_rad_ip()
+
+
rotates the vector by a given angle in radians in place.
+
rotate_rad_ip(angle) -> None
+
+

Rotates the vector counterclockwise by the given angle in radians. The +length of the vector is not changed. +(Note that due to pygame's inverted y coordinate system, the rotation +will look clockwise if displayed).

+
+

New in pygame 2.1.1.

+
+
+ +
+
+angle_to()
+
+
calculates the angle to a given vector in degrees.
+
angle_to(Vector2) -> float
+
+

Returns the angle from self to the passed Vector2 that would rotate self +to be aligned with the passed Vector2 without crossing over the negative +x-axis.

+
+angle_to image +
+

Example demonstrating the angle returned

+
+
+
+ +
+
+as_polar()
+
+
returns a tuple with radial distance and azimuthal angle.
+
as_polar() -> (r, phi)
+
+

Returns a tuple (r, phi) where r is the radial distance, and phi +is the azimuthal angle.

+
+ +
+
+from_polar()
+
+
Creates a Vector2(x, y) or sets x and y from a polar coordinates tuple.
+
Vector2.from_polar((r, phi)) -> Vector2
+
Vector2().from_polar((r, phi)) -> None
+
+

If used from the class creates a Vector2(x,y), else sets x and y. +The values of x and y are defined from a tuple (r, phi) where r +is the radial distance, and phi is the azimuthal angle.

+
+ +
+
+project()
+
+
projects a vector onto another.
+
project(Vector2) -> Vector2
+
+

Returns the projected vector. This is useful for collision detection in finding the components in a certain direction (e.g. in direction of the wall). +For a more detailed explanation see Wikipedia.

+
+

New in pygame 2.0.2.

+
+
+ +
+
+copy()
+
+
Returns a copy of itself.
+
copy() -> Vector2
+
+

Returns a new Vector2 having the same dimensions.

+
+

New in pygame 2.1.1.

+
+
+ +
+
+clamp_magnitude()
+
+
Returns a copy of a vector with the magnitude clamped between max_length and min_length.
+
clamp_magnitude(max_length) -> Vector2
+
clamp_magnitude(min_length, max_length) -> Vector2
+
+

Experimental: feature still in development available for testing and feedback. It may change. +Please leave clamp_magnitude feedback with authors

+

Returns a new copy of a vector with the magnitude clamped between +max_length and min_length. If only one argument is passed, it is +taken to be the max_length

+

This function raises ValueError if min_length is greater than +max_length, or if either of these values are negative.

+
+

New in pygame 2.1.3.

+
+
+ +
+
+clamp_magnitude_ip()
+
+
Clamps the vector's magnitude between max_length and min_length
+
clamp_magnitude_ip(max_length) -> None
+
clamp_magnitude_ip(min_length, max_length) -> None
+
+

Experimental: feature still in development available for testing and feedback. It may change. +Please leave clamp_magnitude_ip feedback with authors

+

Clamps the vector's magnitude between max_length and min_length. +If only one argument is passed, it is taken to be the max_length

+

This function raises ValueError if min_length is greater than +max_length, or if either of these values are negative.

+
+

New in pygame 2.1.3.

+
+
+ +
+
+update()
+
+
Sets the coordinates of the vector.
+
update() -> None
+
update(int) -> None
+
update(float) -> None
+
update(Vector2) -> None
+
update(x, y) -> None
+
update((x, y)) -> None
+
+

Sets coordinates x and y in place.

+
+

New in pygame 1.9.5.

+
+
+ +
+
+epsilon
+
+
Determines the tolerance of vector calculations.
+
+

Both Vector classes have a value named epsilon that defaults to 1e-6. +This value acts as a numerical margin in various methods to account for floating point +arithmetic errors. Specifically, epsilon is used in the following places:

+
+
    +
  • comparing Vectors (== and !=)

  • +
  • the is_normalized method (if the square of the length is within epsilon of 1, it's normalized)

  • +
  • slerping (a Vector with a length of <epsilon is considered a zero vector, and can't slerp with that)

  • +
  • reflection (can't reflect over the zero vector)

  • +
  • projection (can't project onto the zero vector)

  • +
  • rotation (only used when rotating by a multiple of 90 degrees)

  • +
+
+

While it's possible to change epsilon for a specific instance of a Vector, all the other Vectors +will retain the default value. Changing epsilon on a specific instance however could lead to some +asymmetric behavior where symmetry would be expected, such as

+
u = pygame.Vector2(0, 1)
+v = pygame.Vector2(0, 1.2)
+u.epsilon = 0.5 # don't set it nearly this large
+
+print(u == v) # >> True
+print(v == u) # >> False
+
+
+

You'll probably never have to change epsilon from the default value, but in rare situations you might +find that either the margin is too large or too small, in which case changing epsilon slightly +might help you out.

+
+ +
+ +
+
+pygame.math.Vector3
+
+
a 3-Dimensional Vector
+
Vector3() -> Vector3(0, 0, 0)
+
Vector3(int) -> Vector3
+
Vector3(float) -> Vector3
+
Vector3(Vector3) -> Vector3
+
Vector3(x, y, z) -> Vector3
+
Vector3((x, y, z)) -> Vector3
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+calculates the dot- or scalar-product with the other vector
+calculates the cross- or vector-product
+returns the Euclidean magnitude of the vector.
+returns the squared Euclidean magnitude of the vector.
+returns the Euclidean length of the vector.
+returns the squared Euclidean length of the vector.
+returns a vector with the same direction but length 1.
+normalizes the vector in place so that its length is 1.
+tests if the vector is normalized i.e. has length == 1.
+scales the vector to a given length.
+returns a vector reflected of a given normal.
+reflect the vector of a given normal in place.
+calculates the Euclidean distance to a given vector.
+calculates the squared Euclidean distance to a given vector.
+returns a vector moved toward the target by a given distance.
+moves the vector toward its target at a given distance.
+returns a linear interpolation to the given vector.
+returns a spherical interpolation to the given vector.
+The next operation will be performed elementwise.
+rotates a vector by a given angle in degrees.
+rotates a vector by a given angle in radians.
+rotates the vector by a given angle in degrees in place.
+rotates the vector by a given angle in radians in place.
+rotates the vector by a given angle in radians in place.
+rotates a vector around the x-axis by the angle in degrees.
+rotates a vector around the x-axis by the angle in radians.
+rotates the vector around the x-axis by the angle in degrees in place.
+rotates the vector around the x-axis by the angle in radians in place.
+rotates the vector around the x-axis by the angle in radians in place.
+rotates a vector around the y-axis by the angle in degrees.
+rotates a vector around the y-axis by the angle in radians.
+rotates the vector around the y-axis by the angle in degrees in place.
+rotates the vector around the y-axis by the angle in radians in place.
+rotates the vector around the y-axis by the angle in radians in place.
+rotates a vector around the z-axis by the angle in degrees.
+rotates a vector around the z-axis by the angle in radians.
+rotates the vector around the z-axis by the angle in degrees in place.
+rotates the vector around the z-axis by the angle in radians in place.
+rotates the vector around the z-axis by the angle in radians in place.
+calculates the angle to a given vector in degrees.
+returns a tuple with radial distance, inclination and azimuthal angle.
+Creates a Vector3(x, y, z) or sets x, y and z from a spherical coordinates 3-tuple.
+projects a vector onto another.
+Returns a copy of itself.
+Returns a copy of a vector with the magnitude clamped between max_length and min_length.
+Clamps the vector's magnitude between max_length and min_length
+Sets the coordinates of the vector.
+Determines the tolerance of vector calculations.
+

Some general information about the Vector3 class.

+
+

Changed in pygame 2.1.3: Inherited methods of vector subclasses now correctly return an instance of the +subclass instead of the superclass

+
+
+
+dot()
+
+
calculates the dot- or scalar-product with the other vector
+
dot(Vector3) -> float
+
+
+ +
+
+cross()
+
+
calculates the cross- or vector-product
+
cross(Vector3) -> Vector3
+
+

calculates the cross-product.

+
+ +
+
+magnitude()
+
+
returns the Euclidean magnitude of the vector.
+
magnitude() -> float
+
+

calculates the magnitude of the vector which follows from the +theorem: vec.magnitude() == math.sqrt(vec.x**2 + vec.y**2 + vec.z**2)

+
+ +
+
+magnitude_squared()
+
+
returns the squared Euclidean magnitude of the vector.
+
magnitude_squared() -> float
+
+

calculates the magnitude of the vector which follows from the +theorem: +vec.magnitude_squared() == vec.x**2 + vec.y**2 + vec.z**2. +This is faster than vec.magnitude() because it avoids the +square root.

+
+ +
+
+length()
+
+
returns the Euclidean length of the vector.
+
length() -> float
+
+

calculates the Euclidean length of the vector which follows from the +Pythagorean theorem: +vec.length() == math.sqrt(vec.x**2 + vec.y**2 + vec.z**2)

+
+ +
+
+length_squared()
+
+
returns the squared Euclidean length of the vector.
+
length_squared() -> float
+
+

calculates the Euclidean length of the vector which follows from the +Pythagorean theorem: +vec.length_squared() == vec.x**2 + vec.y**2 + vec.z**2. +This is faster than vec.length() because it avoids the square root.

+
+ +
+
+normalize()
+
+
returns a vector with the same direction but length 1.
+
normalize() -> Vector3
+
+

Returns a new vector that has length equal to 1 and the same +direction as self.

+
+ +
+
+normalize_ip()
+
+
normalizes the vector in place so that its length is 1.
+
normalize_ip() -> None
+
+

Normalizes the vector so that it has length equal to 1. The +direction of the vector is not changed.

+
+ +
+
+is_normalized()
+
+
tests if the vector is normalized i.e. has length == 1.
+
is_normalized() -> Bool
+
+

Returns True if the vector has length equal to 1. Otherwise it +returns False.

+
+ +
+
+scale_to_length()
+
+
scales the vector to a given length.
+
scale_to_length(float) -> None
+
+

Scales the vector so that it has the given length. The direction of the +vector is not changed. You can also scale to length 0. If the vector +is the zero vector (i.e. has length 0 thus no direction) a +ValueError is raised.

+
+ +
+
+reflect()
+
+
returns a vector reflected of a given normal.
+
reflect(Vector3) -> Vector3
+
+

Returns a new vector that points in the direction as if self would bounce +of a surface characterized by the given surface normal. The length of the +new vector is the same as self's.

+
+ +
+
+reflect_ip()
+
+
reflect the vector of a given normal in place.
+
reflect_ip(Vector3) -> None
+
+

Changes the direction of self as if it would have been reflected of a +surface with the given surface normal.

+
+ +
+
+distance_to()
+
+
calculates the Euclidean distance to a given vector.
+
distance_to(Vector3) -> float
+
+
+ +
+
+distance_squared_to()
+
+
calculates the squared Euclidean distance to a given vector.
+
distance_squared_to(Vector3) -> float
+
+
+ +
+
+move_towards()
+
+
returns a vector moved toward the target by a given distance.
+
move_towards(Vector3, float) -> Vector3
+
+

Experimental: feature still in development available for testing and feedback. It may change. +Please leave move_towards feedback with authors

+

Returns a Vector which is moved towards the given Vector by a given +distance and does not overshoot past its target Vector. +The first parameter determines the target Vector, while the second +parameter determines the delta distance. If the distance is in the +negatives, then it will move away from the target Vector.

+
+

New in pygame 2.1.3.

+
+
+ +
+
+move_towards_ip()
+
+
moves the vector toward its target at a given distance.
+
move_towards_ip(Vector3, float) -> None
+
+

Experimental: feature still in development available for testing and feedback. It may change. +Please leave move_towards_ip feedback with authors

+

Moves itself toward the given Vector at a given distance and does not +overshoot past its target Vector. +The first parameter determines the target Vector, while the second +parameter determines the delta distance. If the distance is in the +negatives, then it will move away from the target Vector.

+
+

New in pygame 2.1.3.

+
+
+ +
+
+lerp()
+
+
returns a linear interpolation to the given vector.
+
lerp(Vector3, float) -> Vector3
+
+

Returns a Vector which is a linear interpolation between self and the +given Vector. The second parameter determines how far between self an +other the result is going to be. It must be a value between 0 and +1, where 0 means self and 1 means other will be returned.

+
+ +
+
+slerp()
+
+
returns a spherical interpolation to the given vector.
+
slerp(Vector3, float) -> Vector3
+
+

Calculates the spherical interpolation from self to the given Vector. The +second argument - often called t - must be in the range [-1, 1]. It +parametrizes where - in between the two vectors - the result should be. +If a negative value is given the interpolation will not take the +complement of the shortest path.

+
+ +
+
+elementwise()
+
+
The next operation will be performed elementwise.
+
elementwise() -> VectorElementwiseProxy
+
+

Applies the following operation to each element of the vector.

+
+ +
+
+rotate()
+
+
rotates a vector by a given angle in degrees.
+
rotate(angle, Vector3) -> Vector3
+
+

Returns a vector which has the same length as self but is rotated +counterclockwise by the given angle in degrees around the given axis. +(Note that due to pygame's inverted y coordinate system, the rotation +will look clockwise if displayed).

+
+ +
+
+rotate_rad()
+
+
rotates a vector by a given angle in radians.
+
rotate_rad(angle, Vector3) -> Vector3
+
+

Returns a vector which has the same length as self but is rotated +counterclockwise by the given angle in radians around the given axis. +(Note that due to pygame's inverted y coordinate system, the rotation +will look clockwise if displayed).

+
+

New in pygame 2.0.0.

+
+
+ +
+
+rotate_ip()
+
+
rotates the vector by a given angle in degrees in place.
+
rotate_ip(angle, Vector3) -> None
+
+

Rotates the vector counterclockwise around the given axis by the given +angle in degrees. The length of the vector is not changed. +(Note that due to pygame's inverted y coordinate system, the rotation +will look clockwise if displayed).

+
+ +
+
+rotate_ip_rad()
+
+
rotates the vector by a given angle in radians in place.
+
rotate_ip_rad(angle, Vector3) -> None
+
+

DEPRECATED: Use rotate_rad_ip() instead.

+
+

New in pygame 2.0.0.

+
+
+

Deprecated since pygame 2.1.1.

+
+
+ +
+
+rotate_rad_ip()
+
+
rotates the vector by a given angle in radians in place.
+
rotate_rad_ip(angle, Vector3) -> None
+
+

Rotates the vector counterclockwise around the given axis by the given +angle in radians. The length of the vector is not changed. +(Note that due to pygame's inverted y coordinate system, the rotation +will look clockwise if displayed).

+
+

New in pygame 2.1.1.

+
+
+ +
+
+rotate_x()
+
+
rotates a vector around the x-axis by the angle in degrees.
+
rotate_x(angle) -> Vector3
+
+

Returns a vector which has the same length as self but is rotated +counterclockwise around the x-axis by the given angle in degrees. +(Note that due to pygame's inverted y coordinate system, the rotation +will look clockwise if displayed).

+
+ +
+
+rotate_x_rad()
+
+
rotates a vector around the x-axis by the angle in radians.
+
rotate_x_rad(angle) -> Vector3
+
+

Returns a vector which has the same length as self but is rotated +counterclockwise around the x-axis by the given angle in radians. +(Note that due to pygame's inverted y coordinate system, the rotation +will look clockwise if displayed).

+
+

New in pygame 2.0.0.

+
+
+ +
+
+rotate_x_ip()
+
+
rotates the vector around the x-axis by the angle in degrees in place.
+
rotate_x_ip(angle) -> None
+
+

Rotates the vector counterclockwise around the x-axis by the given angle +in degrees. The length of the vector is not changed. +(Note that due to pygame's inverted y coordinate system, the rotation +will look clockwise if displayed).

+
+ +
+
+rotate_x_ip_rad()
+
+
rotates the vector around the x-axis by the angle in radians in place.
+
rotate_x_ip_rad(angle) -> None
+
+

DEPRECATED: Use rotate_x_rad_ip() instead.

+
+

New in pygame 2.0.0.

+
+
+

Deprecated since pygame 2.1.1.

+
+
+ +
+
+rotate_x_rad_ip()
+
+
rotates the vector around the x-axis by the angle in radians in place.
+
rotate_x_rad_ip(angle) -> None
+
+

Rotates the vector counterclockwise around the x-axis by the given angle +in radians. The length of the vector is not changed. +(Note that due to pygame's inverted y coordinate system, the rotation +will look clockwise if displayed).

+
+

New in pygame 2.1.1.

+
+
+ +
+
+rotate_y()
+
+
rotates a vector around the y-axis by the angle in degrees.
+
rotate_y(angle) -> Vector3
+
+

Returns a vector which has the same length as self but is rotated +counterclockwise around the y-axis by the given angle in degrees. +(Note that due to pygame's inverted y coordinate system, the rotation +will look clockwise if displayed).

+
+ +
+
+rotate_y_rad()
+
+
rotates a vector around the y-axis by the angle in radians.
+
rotate_y_rad(angle) -> Vector3
+
+

Returns a vector which has the same length as self but is rotated +counterclockwise around the y-axis by the given angle in radians. +(Note that due to pygame's inverted y coordinate system, the rotation +will look clockwise if displayed).

+
+

New in pygame 2.0.0.

+
+
+ +
+
+rotate_y_ip()
+
+
rotates the vector around the y-axis by the angle in degrees in place.
+
rotate_y_ip(angle) -> None
+
+

Rotates the vector counterclockwise around the y-axis by the given angle +in degrees. The length of the vector is not changed. +(Note that due to pygame's inverted y coordinate system, the rotation +will look clockwise if displayed).

+
+ +
+
+rotate_y_ip_rad()
+
+
rotates the vector around the y-axis by the angle in radians in place.
+
rotate_y_ip_rad(angle) -> None
+
+

DEPRECATED: Use rotate_y_rad_ip() instead.

+
+

New in pygame 2.0.0.

+
+
+

Deprecated since pygame 2.1.1.

+
+
+ +
+
+rotate_y_rad_ip()
+
+
rotates the vector around the y-axis by the angle in radians in place.
+
rotate_y_rad_ip(angle) -> None
+
+

Rotates the vector counterclockwise around the y-axis by the given angle +in radians. The length of the vector is not changed. +(Note that due to pygame's inverted y coordinate system, the rotation +will look clockwise if displayed).

+
+

New in pygame 2.1.1.

+
+
+ +
+
+rotate_z()
+
+
rotates a vector around the z-axis by the angle in degrees.
+
rotate_z(angle) -> Vector3
+
+

Returns a vector which has the same length as self but is rotated +counterclockwise around the z-axis by the given angle in degrees. +(Note that due to pygame's inverted y coordinate system, the rotation +will look clockwise if displayed).

+
+ +
+
+rotate_z_rad()
+
+
rotates a vector around the z-axis by the angle in radians.
+
rotate_z_rad(angle) -> Vector3
+
+

Returns a vector which has the same length as self but is rotated +counterclockwise around the z-axis by the given angle in radians. +(Note that due to pygame's inverted y coordinate system, the rotation +will look clockwise if displayed).

+
+

New in pygame 2.0.0.

+
+
+ +
+
+rotate_z_ip()
+
+
rotates the vector around the z-axis by the angle in degrees in place.
+
rotate_z_ip(angle) -> None
+
+

Rotates the vector counterclockwise around the z-axis by the given angle +in degrees. The length of the vector is not changed. +(Note that due to pygame's inverted y coordinate system, the rotation +will look clockwise if displayed).

+
+ +
+
+rotate_z_ip_rad()
+
+
rotates the vector around the z-axis by the angle in radians in place.
+
rotate_z_ip_rad(angle) -> None
+
+

DEPRECATED: Use rotate_z_rad_ip() instead.

+
+

Deprecated since pygame 2.1.1.

+
+
+ +
+
+rotate_z_rad_ip()
+
+
rotates the vector around the z-axis by the angle in radians in place.
+
rotate_z_rad_ip(angle) -> None
+
+

Rotates the vector counterclockwise around the z-axis by the given angle +in radians. The length of the vector is not changed. +(Note that due to pygame's inverted y coordinate system, the rotation +will look clockwise if displayed).

+
+

New in pygame 2.1.1.

+
+
+ +
+
+angle_to()
+
+
calculates the angle to a given vector in degrees.
+
angle_to(Vector3) -> float
+
+

Returns the angle between self and the given vector.

+
+ +
+
+as_spherical()
+
+
returns a tuple with radial distance, inclination and azimuthal angle.
+
as_spherical() -> (r, theta, phi)
+
+

Returns a tuple (r, theta, phi) where r is the radial distance, theta is +the inclination angle and phi is the azimuthal angle.

+
+ +
+
+from_spherical()
+
+
Creates a Vector3(x, y, z) or sets x, y and z from a spherical coordinates 3-tuple.
+
Vector3.from_spherical((r, theta, phi)) -> Vector3
+
Vector3().from_spherical((r, theta, phi)) -> None
+
+

If used from the class creates a Vector3(x, y, z), else sets x, y, and z. +The values of x, y, and z are from a tuple (r, theta, phi) where r is the radial +distance, theta is the inclination angle and phi is the azimuthal angle.

+
+ +
+
+project()
+
+
projects a vector onto another.
+
project(Vector3) -> Vector3
+
+

Returns the projected vector. This is useful for collision detection in finding the components in a certain direction (e.g. in direction of the wall). +For a more detailed explanation see Wikipedia.

+
+

New in pygame 2.0.2.

+
+
+ +
+
+copy()
+
+
Returns a copy of itself.
+
copy() -> Vector3
+
+

Returns a new Vector3 having the same dimensions.

+
+

New in pygame 2.1.1.

+
+
+ +
+
+clamp_magnitude()
+
+
Returns a copy of a vector with the magnitude clamped between max_length and min_length.
+
clamp_magnitude(max_length) -> Vector3
+
clamp_magnitude(min_length, max_length) -> Vector3
+
+

Experimental: feature still in development available for testing and feedback. It may change. +Please leave clamp_magnitude feedback with authors

+

Returns a new copy of a vector with the magnitude clamped between +max_length and min_length. If only one argument is passed, it is +taken to be the max_length

+

This function raises ValueError if min_length is greater than +max_length, or if either of these values are negative.

+
+

New in pygame 2.1.3.

+
+
+ +
+
+clamp_magnitude_ip()
+
+
Clamps the vector's magnitude between max_length and min_length
+
clamp_magnitude_ip(max_length) -> None
+
clamp_magnitude_ip(min_length, max_length) -> None
+
+

Experimental: feature still in development available for testing and feedback. It may change. +Please leave clamp_magnitude_ip feedback with authors

+

Clamps the vector's magnitude between max_length and min_length. +If only one argument is passed, it is taken to be the max_length

+

This function raises ValueError if min_length is greater than +max_length, or if either of these values are negative.

+
+

New in pygame 2.1.3.

+
+
+ +
+
+update()
+
+
Sets the coordinates of the vector.
+
update() -> None
+
update(int) -> None
+
update(float) -> None
+
update(Vector3) -> None
+
update(x, y, z) -> None
+
update((x, y, z)) -> None
+
+

Sets coordinates x, y, and z in place.

+
+

New in pygame 1.9.5.

+
+
+ +
+
+epsilon
+
+
Determines the tolerance of vector calculations.
+
+

With lengths within this number, vectors are considered equal. For more information see pygame.math.Vector2.epsilonDetermines the tolerance of vector calculations.

+
+ +
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/midi.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/midi.html new file mode 100644 index 00000000..2663b846 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/midi.html @@ -0,0 +1,843 @@ + + + + + + + + + pygame.midi — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.midi
+
+
pygame module for interacting with midi input and output.
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+initialize the midi module
+uninitialize the midi module
+returns True if the midi module is currently initialized
+Input is used to get midi input from midi devices.
+Output is used to send midi to an output device
+gets the number of devices.
+gets default input device number
+gets default output device number
+returns information about a midi device
+converts midi events to pygame events
+returns the current time in ms of the PortMidi timer
+Converts a frequency into a MIDI note. Rounds to the closest midi note.
+Converts a midi note to a frequency.
+Returns the Ansi Note name for a midi number.
+exception that pygame.midi functions and classes can raise
+
+

New in pygame 1.9.0.

+
+

The midi module can send output to midi devices and get input from midi +devices. It can also list midi devices on the system.

+

The midi module supports real and virtual midi devices.

+

It uses the portmidi library. Is portable to which ever platforms portmidi +supports (currently Windows, Mac OS X, and Linux).

+

This uses pyportmidi for now, but may use its own bindings at some point in the +future. The pyportmidi bindings are included with pygame.

+
+

+
+
+

New in pygame 2.0.0.

+
+

These are pygame events (pygame.eventpygame module for interacting with events and queues) reserved for midi use. The +MIDIIN event is used by pygame.midi.midis2events()converts midi events to pygame events when converting +midi events to pygame events.

+
MIDIIN
+MIDIOUT
+
+
+
+

+
+
+
+pygame.midi.init()
+
+
initialize the midi module
+
init() -> None
+
+

Initializes the pygame.midipygame module for interacting with midi input and output. module. Must be called before using the +pygame.midipygame module for interacting with midi input and output. module.

+

It is safe to call this more than once.

+
+ +
+
+pygame.midi.quit()
+
+
uninitialize the midi module
+
quit() -> None
+
+

Uninitializes the pygame.midipygame module for interacting with midi input and output. module. If pygame.midi.init()initialize the midi module was +called to initialize the pygame.midipygame module for interacting with midi input and output. module, then this function will +be called automatically when your program exits.

+

It is safe to call this function more than once.

+
+ +
+
+pygame.midi.get_init()
+
+
returns True if the midi module is currently initialized
+
get_init() -> bool
+
+

Gets the initialization state of the pygame.midipygame module for interacting with midi input and output. module.

+
+
Returns
+

True if the pygame.midipygame module for interacting with midi input and output. module is currently initialized.

+
+
Return type
+

bool

+
+
+
+

New in pygame 1.9.5.

+
+
+ +
+
+pygame.midi.Input
+
+
Input is used to get midi input from midi devices.
+
Input(device_id) -> None
+
Input(device_id, buffer_size) -> None
+
+ +++++ + + + + + + + + + + + + + + +
+closes a midi stream, flushing any pending buffers.
+returns True if there's data, or False if not.
+reads num_events midi events from the buffer.
+
+
Parameters
+
    +
  • device_id (int) -- midi device id

  • +
  • buffer_size (int) -- (optional) the number of input events to be buffered

  • +
+
+
+
+
+close()
+
+
closes a midi stream, flushing any pending buffers.
+
close() -> None
+
+

PortMidi attempts to close open streams when the application exits.

+
+

Note

+

This is particularly difficult under Windows.

+
+
+ +
+
+poll()
+
+
returns True if there's data, or False if not.
+
poll() -> bool
+
+

Used to indicate if any data exists.

+
+
Returns
+

True if there is data, False otherwise

+
+
Return type
+

bool

+
+
Raises
+

MidiException -- on error

+
+
+
+ +
+
+read()
+
+
reads num_events midi events from the buffer.
+
read(num_events) -> midi_event_list
+
+

Reads from the input buffer and gives back midi events.

+
+
Parameters
+

num_events (int) -- number of input events to read

+
+
Returns
+

the format for midi_event_list is +[[[status, data1, data2, data3], timestamp], ...]

+
+
Return type
+

list

+
+
+
+ +
+ +
+
+pygame.midi.Output
+
+
Output is used to send midi to an output device
+
Output(device_id) -> None
+
Output(device_id, latency=0) -> None
+
Output(device_id, buffer_size=256) -> None
+
Output(device_id, latency, buffer_size) -> None
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+terminates outgoing messages immediately
+closes a midi stream, flushing any pending buffers.
+turns a midi note off (note must be on)
+turns a midi note on (note must be off)
+select an instrument, with a value between 0 and 127
+modify the pitch of a channel.
+writes a list of midi data to the Output
+writes up to 3 bytes of midi data to the Output
+writes a timestamped system-exclusive midi message.
+

The buffer_size specifies the number of output events to be buffered +waiting for output. In some cases (see below) PortMidi does not buffer +output at all and merely passes data to a lower-level API, in which case +buffersize is ignored.

+

latency is the delay in milliseconds applied to timestamps to determine +when the output should actually occur. If latency is <<0, 0 is assumed.

+

If latency is zero, timestamps are ignored and all output is delivered +immediately. If latency is greater than zero, output is delayed until the +message timestamp plus the latency. In some cases, PortMidi can obtain +better timing than your application by passing timestamps along to the +device driver or hardware. Latency may also help you to synchronize midi +data to audio data by matching midi latency to the audio buffer latency.

+
+

Note

+

Time is measured relative to the time source indicated by time_proc. +Timestamps are absolute, not relative delays or offsets.

+
+
+
+abort()
+
+
terminates outgoing messages immediately
+
abort() -> None
+
+

The caller should immediately close the output port; this call may result +in transmission of a partial midi message. There is no abort for Midi +input because the user can simply ignore messages in the buffer and close +an input device at any time.

+
+ +
+
+close()
+
+
closes a midi stream, flushing any pending buffers.
+
close() -> None
+
+

PortMidi attempts to close open streams when the application exits.

+
+

Note

+

This is particularly difficult under Windows.

+
+
+ +
+
+note_off()
+
+
turns a midi note off (note must be on)
+
note_off(note, velocity=None, channel=0) -> None
+
+

Turn a note off in the output stream. The note must already be on for +this to work correctly.

+
+ +
+
+note_on()
+
+
turns a midi note on (note must be off)
+
note_on(note, velocity=None, channel=0) -> None
+
+

Turn a note on in the output stream. The note must already be off for +this to work correctly.

+
+ +
+
+set_instrument()
+
+
select an instrument, with a value between 0 and 127
+
set_instrument(instrument_id, channel=0) -> None
+
+

Select an instrument.

+
+ +
+
+pitch_bend()
+
+
modify the pitch of a channel.
+
set_instrument(value=0, channel=0) -> None
+
+

Adjust the pitch of a channel. The value is a signed integer +from -8192 to +8191. For example, 0 means "no change", +4096 is +typically a semitone higher, and -8192 is 1 whole tone lower (though +the musical range corresponding to the pitch bend range can also be +changed in some synthesizers).

+

If no value is given, the pitch bend is returned to "no change".

+
+

New in pygame 1.9.4.

+
+
+ +
+
+write()
+
+
writes a list of midi data to the Output
+
write(data) -> None
+
+

Writes series of MIDI information in the form of a list.

+
+
Parameters
+

data (list) -- data to write, the expected format is +[[[status, data1=0, data2=0, ...], timestamp], ...] +with the data# fields being optional

+
+
Raises
+

IndexError -- if more than 1024 elements in the data list

+
+
+

Example:

+
# Program change at time 20000 and 500ms later send note 65 with
+# velocity 100.
+write([[[0xc0, 0, 0], 20000], [[0x90, 60, 100], 20500]])
+
+
+
+

Note

+
    +
  • Timestamps will be ignored if latency = 0

  • +
  • To get a note to play immediately, send MIDI info with timestamp +read from function Time

  • +
  • Optional data fields: write([[[0xc0, 0, 0], 20000]]) is +equivalent to write([[[0xc0], 20000]])

  • +
+
+
+ +
+
+write_short()
+
+
writes up to 3 bytes of midi data to the Output
+
write_short(status) -> None
+
write_short(status, data1=0, data2=0) -> None
+
+

Output MIDI information of 3 bytes or less. The data fields are +optional and assumed to be 0 if omitted.

+

Examples of status byte values:

+
0xc0  # program change
+0x90  # note on
+# etc.
+
+
+

Example:

+
# note 65 on with velocity 100
+write_short(0x90, 65, 100)
+
+
+
+ +
+
+write_sys_ex()
+
+
writes a timestamped system-exclusive midi message.
+
write_sys_ex(when, msg) -> None
+
+

Writes a timestamped system-exclusive midi message.

+
+
Parameters
+
    +
  • msg (list[int] or str) -- midi message

  • +
  • when -- timestamp in milliseconds

  • +
+
+
+

Example:

+
midi_output.write_sys_ex(0, '\xF0\x7D\x10\x11\x12\x13\xF7')
+
+# is equivalent to
+
+midi_output.write_sys_ex(pygame.midi.time(),
+                         [0xF0, 0x7D, 0x10, 0x11, 0x12, 0x13, 0xF7])
+
+
+
+ +
+ +
+
+pygame.midi.get_count()
+
+
gets the number of devices.
+
get_count() -> num_devices
+
+

Device ids range from 0 to get_count() - 1

+
+ +
+
+pygame.midi.get_default_input_id()
+
+
gets default input device number
+
get_default_input_id() -> default_id
+
+

The following describes the usage details for this function and the +get_default_output_id() function.

+

Return the default device ID or -1 if there are no devices. The result +can be passed to the Input/Output class.

+

On a PC the user can specify a default device by setting an environment +variable. To use device #1, for example:

+
set PM_RECOMMENDED_INPUT_DEVICE=1
+or
+set PM_RECOMMENDED_OUTPUT_DEVICE=1
+
+
+

The user should first determine the available device ID by using the +supplied application "testin" or "testout".

+

In general, the registry is a better place for this kind of info. With +USB devices that can come and go, using integers is not very reliable +for device identification. Under Windows, if PM_RECOMMENDED_INPUT_DEVICE +(or PM_RECOMMENDED_OUTPUT_DEVICE) is NOT found in the environment, +then the default device is obtained by looking for a string in the registry +under:

+
HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Input_Device
+or
+HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Output_Device
+
+
+

The number of the first device with a substring that matches the +string exactly is returned. For example, if the string in the registry is +"USB" and device 1 is named "In USB MidiSport 1x1", then that will be +the default input because it contains the string "USB".

+

In addition to the name, get_device_info() returns "interf", which is +the interface name. The "interface" is the underlying software system or +API used by PortMidi to access devices. Supported interfaces:

+
MMSystem   # the only Win32 interface currently supported
+ALSA       # the only Linux interface currently supported
+CoreMIDI   # the only Mac OS X interface currently supported
+# DirectX - not implemented
+# OSS     - not implemented
+
+
+

To specify both the interface and the device name in the registry, separate +the two with a comma and a space. The string before the comma must be a +substring of the "interf" string and the string after the space must be a +substring of the "name" name string in order to match the device. e.g.:

+
MMSystem, In USB MidiSport 1x1
+
+
+
+

Note

+

In the current release, the default is simply the first device (the +input or output device with the lowest PmDeviceID).

+
+
+ +
+
+pygame.midi.get_default_output_id()
+
+
gets default output device number
+
get_default_output_id() -> default_id
+
+

See get_default_input_id() for usage details.

+
+ +
+
+pygame.midi.get_device_info()
+
+
returns information about a midi device
+
get_device_info(an_id) -> (interf, name, input, output, opened)
+
get_device_info(an_id) -> None
+
+

Gets the device info for a given id.

+
+
Parameters
+

an_id (int) -- id of the midi device being queried

+
+
Returns
+

if the id is out of range None is returned, otherwise +a tuple of (interf, name, input, output, opened) is returned.

+
+
    +
  • interf: string describing the device interface (e.g. 'ALSA')

  • +
  • name: string name of the device (e.g. 'Midi Through Port-0')

  • +
  • input: 1 if the device is an input device, otherwise 0

  • +
  • output: 1 if the device is an output device, otherwise 0

  • +
  • opened: 1 if the device is opened, otherwise 0

  • +
+
+

+
+
Return type
+

tuple or None

+
+
+
+ +
+
+pygame.midi.midis2events()
+
+
converts midi events to pygame events
+
midis2events(midi_events, device_id) -> [Event, ...]
+
+

Takes a sequence of midi events and returns list of pygame events.

+

The midi_events data is expected to be a sequence of +((status, data1, data2, data3), timestamp) midi events (all values +required).

+
+
Returns
+

a list of pygame events of event type MIDIIN

+
+
Return type
+

list

+
+
+
+ +
+
+pygame.midi.time()
+
+
returns the current time in ms of the PortMidi timer
+
time() -> time
+
+

The time is reset to 0 when the pygame.midipygame module for interacting with midi input and output. module is initialized.

+
+ +
+
+pygame.midi.frequency_to_midi()
+
+
Converts a frequency into a MIDI note. Rounds to the closest midi note.
+
frequency_to_midi(midi_note) -> midi_note
+
+

example:

+
frequency_to_midi(27.5) == 21
+
+
+
+

New in pygame 1.9.5.

+
+
+ +
+
+pygame.midi.midi_to_frequency()
+
+
Converts a midi note to a frequency.
+
midi_to_frequency(midi_note) -> frequency
+
+

example:

+
midi_to_frequency(21) == 27.5
+
+
+
+

New in pygame 1.9.5.

+
+
+ +
+
+pygame.midi.midi_to_ansi_note()
+
+
Returns the Ansi Note name for a midi number.
+
midi_to_ansi_note(midi_note) -> ansi_note
+
+

example:

+
midi_to_ansi_note(21) == 'A0'
+
+
+
+

New in pygame 1.9.5.

+
+
+ +
+
+exception pygame.midi.MidiException
+
+
exception that pygame.midi functions and classes can raise
+
MidiException(errno) -> None
+
+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/mixer.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/mixer.html new file mode 100644 index 00000000..ef7ddd28 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/mixer.html @@ -0,0 +1,994 @@ + + + + + + + + + pygame.mixer — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.mixer
+
+
pygame module for loading and playing sounds
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+initialize the mixer module
+preset the mixer init arguments
+uninitialize the mixer
+test if the mixer is initialized
+stop playback of all sound channels
+temporarily stop playback of all sound channels
+resume paused playback of sound channels
+fade out the volume on all sounds before stopping
+set the total number of playback channels
+get the total number of playback channels
+reserve channels from being automatically used
+find an unused channel
+test if any sound is being mixed
+get the mixer's SDL version
+Create a new Sound object from a file or buffer object
+Create a Channel object for controlling playback
+

This module contains classes for loading Sound objects and controlling +playback. The mixer module is optional and depends on SDL_mixer. Your program +should test that pygame.mixerpygame module for loading and playing sounds is available and initialized before using +it.

+

The mixer module has a limited number of channels for playback of sounds. +Usually programs tell pygame to start playing audio and it selects an available +channel automatically. The default is 8 simultaneous channels, but complex +programs can get more precise control over the number of channels and their +use.

+

All sound playback is mixed in background threads. When you begin to play a +Sound object, it will return immediately while the sound continues to play. A +single Sound object can also be actively played back multiple times.

+

The mixer also has a special streaming channel. This is for music playback and +is accessed through the pygame.mixer.musicpygame module for controlling streamed audio module. Consider using this +module for playing long running music. Unlike mixer module, the music module +streams the music from the files without loading music at once into memory.

+

The mixer module must be initialized like other pygame modules, but it has some +extra conditions. The pygame.mixer.init() function takes several optional +arguments to control the playback rate and sample size. Pygame will default to +reasonable values, but pygame cannot perform Sound resampling, so the mixer +should be initialized to match the values of your audio resources.

+

NOTE: For less laggy sound use a smaller buffer size. The default +is set to reduce the chance of scratchy sounds on some computers. You can +change the default buffer by calling pygame.mixer.pre_init()preset the mixer init arguments before +pygame.mixer.init()initialize the mixer module or pygame.init()initialize all imported pygame modules is called. For example: +pygame.mixer.pre_init(44100,-16,2, 1024)

+
+
+pygame.mixer.init()
+
+
initialize the mixer module
+
init(frequency=44100, size=-16, channels=2, buffer=512, devicename=None, allowedchanges=AUDIO_ALLOW_FREQUENCY_CHANGE | AUDIO_ALLOW_CHANNELS_CHANGE) -> None
+
+

Initialize the mixer module for Sound loading and playback. The default +arguments can be overridden to provide specific audio mixing. Keyword +arguments are accepted. For backwards compatibility, argument values of +0 are replaced with the startup defaults, except for allowedchanges, +where -1 is used. (startup defaults may be changed by a pre_init() call).

+

The size argument represents how many bits are used for each audio sample. +If the value is negative then signed sample values will be used. Positive +values mean unsigned audio samples will be used. An invalid value raises an +exception.

+

The channels argument is used to specify whether to use mono or stereo. 1 +for mono and 2 for stereo.

+

The buffer argument controls the number of internal samples used in the +sound mixer. The default value should work for most cases. It can be lowered +to reduce latency, but sound dropout may occur. It can be raised to larger +values to ensure playback never skips, but it will impose latency on sound +playback. The buffer size must be a power of two (if not it is rounded up to +the next nearest power of 2).

+

Some platforms require the pygame.mixerpygame module for loading and playing sounds module to be initialized +after the display modules have initialized. The top level pygame.init() +takes care of this automatically, but cannot pass any arguments to the mixer +init. To solve this, mixer has a function pygame.mixer.pre_init() to set +the proper defaults before the toplevel init is used.

+

When using allowedchanges=0 it will convert the samples at runtime to match +what the hardware supports. For example a sound card may not +support 16bit sound samples, so instead it will use 8bit samples internally. +If AUDIO_ALLOW_FORMAT_CHANGE is supplied, then the requested format will +change to the closest that SDL2 supports.

+

Apart from 0, allowedchanged accepts the following constants ORed together:

+
+
    +
  • AUDIO_ALLOW_FREQUENCY_CHANGE

  • +
  • AUDIO_ALLOW_FORMAT_CHANGE

  • +
  • AUDIO_ALLOW_CHANNELS_CHANGE

  • +
  • AUDIO_ALLOW_ANY_CHANGE

  • +
+
+

It is safe to call this more than once, but after the mixer is initialized +you cannot change the playback arguments without first calling +pygame.mixer.quit().

+
+

Changed in pygame 1.8: The default buffersize changed from 1024 to 3072.

+
+
+

Changed in pygame 1.9.1: The default buffersize changed from 3072 to 4096.

+
+
+

Changed in pygame 2.0.0: The default buffersize changed from 4096 to 512.

+
+
+

Changed in pygame 2.0.0: The default frequency changed from 22050 to 44100.

+
+
+

Changed in pygame 2.0.0: size can be 32 (32-bit floats).

+
+
+

Changed in pygame 2.0.0: channels can also be 4 or 6.

+
+
+

New in pygame 2.0.0: allowedchanges, devicename arguments added

+
+
+ +
+
+pygame.mixer.pre_init()
+
+
preset the mixer init arguments
+
pre_init(frequency=44100, size=-16, channels=2, buffer=512, devicename=None, allowedchanges=AUDIO_ALLOW_FREQUENCY_CHANGE | AUDIO_ALLOW_CHANNELS_CHANGE) -> None
+
+

Call pre_init to change the defaults used when the real +pygame.mixer.init() is called. Keyword arguments are accepted. The best +way to set custom mixer playback values is to call +pygame.mixer.pre_init() before calling the top level pygame.init(). +For backwards compatibility, argument values of 0 are replaced with the +startup defaults, except for allowedchanges, where -1 is used.

+
+

Changed in pygame 1.8: The default buffersize changed from 1024 to 3072.

+
+
+

Changed in pygame 1.9.1: The default buffersize changed from 3072 to 4096.

+
+
+

Changed in pygame 2.0.0: The default buffersize changed from 4096 to 512.

+
+
+

Changed in pygame 2.0.0: The default frequency changed from 22050 to 44100.

+
+
+

New in pygame 2.0.0: allowedchanges, devicename arguments added

+
+
+ +
+
+pygame.mixer.quit()
+
+
uninitialize the mixer
+
quit() -> None
+
+

This will uninitialize pygame.mixerpygame module for loading and playing sounds. All playback will stop and any +loaded Sound objects may not be compatible with the mixer if it is +reinitialized later.

+
+ +
+
+pygame.mixer.get_init()
+
+
test if the mixer is initialized
+
get_init() -> (frequency, format, channels)
+
+

If the mixer is initialized, this returns the playback arguments it is +using. If the mixer has not been initialized this returns None.

+
+ +
+
+pygame.mixer.stop()
+
+
stop playback of all sound channels
+
stop() -> None
+
+

This will stop all playback of all active mixer channels.

+
+ +
+
+pygame.mixer.pause()
+
+
temporarily stop playback of all sound channels
+
pause() -> None
+
+

This will temporarily stop all playback on the active mixer channels. The +playback can later be resumed with pygame.mixer.unpause()

+
+ +
+
+pygame.mixer.unpause()
+
+
resume paused playback of sound channels
+
unpause() -> None
+
+

This will resume all active sound channels after they have been paused.

+
+ +
+
+pygame.mixer.fadeout()
+
+
fade out the volume on all sounds before stopping
+
fadeout(time) -> None
+
+

This will fade out the volume on all active channels over the time argument +in milliseconds. After the sound is muted the playback will stop.

+
+ +
+
+pygame.mixer.set_num_channels()
+
+
set the total number of playback channels
+
set_num_channels(count) -> None
+
+

Sets the number of available channels for the mixer. The default value is 8. +The value can be increased or decreased. If the value is decreased, sounds +playing on the truncated channels are stopped.

+
+ +
+
+pygame.mixer.get_num_channels()
+
+
get the total number of playback channels
+
get_num_channels() -> count
+
+

Returns the number of currently active playback channels.

+
+ +
+
+pygame.mixer.set_reserved()
+
+
reserve channels from being automatically used
+
set_reserved(count) -> count
+
+

The mixer can reserve any number of channels that will not be automatically +selected for playback by Sounds. This means that whenever you play a Sound +without specifying a channel, a reserved channel will never be used. If sounds +are currently playing on the reserved channels they will not be stopped.

+

This allows the application to reserve a specific number of channels for +important sounds that must not be dropped or have a guaranteed channel to +play on.

+

Will return number of channels actually reserved, this may be less than requested +depending on the number of channels previously allocated.

+
+ +
+
+pygame.mixer.find_channel()
+
+
find an unused channel
+
find_channel(force=False) -> Channel
+
+

This will find and return an inactive Channel object. If there are no +inactive Channels this function will return None. If there are no +inactive channels and the force argument is True, this will find the +Channel with the longest running Sound and return it.

+
+ +
+
+pygame.mixer.get_busy()
+
+
test if any sound is being mixed
+
get_busy() -> bool
+
+

Returns True if the mixer is busy mixing any channels. If the mixer is +idle then this return False.

+
+ +
+
+pygame.mixer.get_sdl_mixer_version()
+
+
get the mixer's SDL version
+
get_sdl_mixer_version() -> (major, minor, patch)
+
get_sdl_mixer_version(linked=True) -> (major, minor, patch)
+
+
+
Parameters
+

linked (bool) -- if True (default) the linked version number is +returned, otherwise the compiled version number is returned

+
+
Returns
+

the mixer's SDL library version number (linked or compiled +depending on the linked parameter) as a tuple of 3 integers +(major, minor, patch)

+
+
Return type
+

tuple

+
+
+
+

Note

+

The linked and compile version numbers should be the same.

+
+
+

New in pygame 2.0.0.

+
+
+ +
+
+pygame.mixer.Sound
+
+
Create a new Sound object from a file or buffer object
+
Sound(filename) -> Sound
+
Sound(file=filename) -> Sound
+
Sound(file=pathlib_path) -> Sound
+
Sound(buffer) -> Sound
+
Sound(buffer=buffer) -> Sound
+
Sound(object) -> Sound
+
Sound(file=object) -> Sound
+
Sound(array=object) -> Sound
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+begin sound playback
+stop sound playback
+stop sound playback after fading out
+set the playback volume for this Sound
+get the playback volume
+count how many times this Sound is playing
+get the length of the Sound
+return a bytestring copy of the Sound samples.
+

Load a new sound buffer from a filename, a python file object or a readable +buffer object. Limited resampling will be performed to help the sample match +the initialize arguments for the mixer. A Unicode string can only be a file +pathname. A bytes object can be either a pathname or a buffer object. +Use the 'file' or 'buffer' keywords to avoid ambiguity; otherwise Sound may +guess wrong. If the array keyword is used, the object is expected to export +a new buffer interface (The object is checked for a buffer interface first.)

+

The Sound object represents actual sound sample data. Methods that change +the state of the Sound object will the all instances of the Sound playback. +A Sound object also exports a new buffer interface.

+

The Sound can be loaded from an OGG audio file or from an uncompressed +WAV.

+

Note: The buffer will be copied internally, no data will be shared between +it and the Sound object.

+

For now buffer and array support is consistent with sndarray.make_sound +for Numeric arrays, in that sample sign and byte order are ignored. This +will change, either by correctly handling sign and byte order, or by raising +an exception when different. Also, source samples are truncated to fit the +audio sample size. This will not change.

+
+

New in pygame 1.8: pygame.mixer.Sound(buffer)

+
+
+

New in pygame 1.9.2: pygame.mixer.SoundCreate a new Sound object from a file or buffer object keyword arguments and array interface support

+
+
+

New in pygame 2.0.1: pathlib.Path support on Python 3.

+
+
+
+play()
+
+
begin sound playback
+
play(loops=0, maxtime=0, fade_ms=0) -> Channel
+
+

Begin playback of the Sound (i.e., on the computer's speakers) on an +available Channel. This will forcibly select a Channel, so playback may +cut off a currently playing sound if necessary.

+

The loops argument controls how many times the sample will be repeated +after being played the first time. A value of 5 means that the sound will +be played once, then repeated five times, and so is played a total of six +times. The default value (zero) means the Sound is not repeated, and so +is only played once. If loops is set to -1 the Sound will loop +indefinitely (though you can still call stop() to stop it).

+

The maxtime argument can be used to stop playback after a given number of +milliseconds.

+

The fade_ms argument will make the sound start playing at 0 volume and +fade up to full volume over the time given. The sample may end before the +fade-in is complete.

+

This returns the Channel object for the channel that was selected.

+
+ +
+
+stop()
+
+
stop sound playback
+
stop() -> None
+
+

This will stop the playback of this Sound on any active Channels.

+
+ +
+
+fadeout()
+
+
stop sound playback after fading out
+
fadeout(time) -> None
+
+

This will stop playback of the sound after fading it out over the time +argument in milliseconds. The Sound will fade and stop on all actively +playing channels.

+
+ +
+
+set_volume()
+
+
set the playback volume for this Sound
+
set_volume(value) -> None
+
+

This will set the playback volume (loudness) for this Sound. This will +immediately affect the Sound if it is playing. It will also affect any +future playback of this Sound.

+
+
Parameters
+

value (float) --

volume in the range of 0.0 to 1.0 (inclusive)

+
+
If value < 0.0, the volume will not be changed
+
If value > 1.0, the volume will be set to 1.0
+
+

+
+
+
+ +
+
+get_volume()
+
+
get the playback volume
+
get_volume() -> value
+
+

Return a value from 0.0 to 1.0 representing the volume for this Sound.

+
+ +
+
+get_num_channels()
+
+
count how many times this Sound is playing
+
get_num_channels() -> count
+
+

Return the number of active channels this sound is playing on.

+
+ +
+
+get_length()
+
+
get the length of the Sound
+
get_length() -> seconds
+
+

Return the length of this Sound in seconds.

+
+ +
+
+get_raw()
+
+
return a bytestring copy of the Sound samples.
+
get_raw() -> bytes
+
+

Return a copy of the Sound object buffer as a bytes.

+
+

New in pygame 1.9.2.

+
+
+ +
+ +
+
+pygame.mixer.Channel
+
+
Create a Channel object for controlling playback
+
Channel(id) -> Channel
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+play a Sound on a specific Channel
+stop playback on a Channel
+temporarily stop playback of a channel
+resume pause playback of a channel
+stop playback after fading channel out
+set the volume of a playing channel
+get the volume of the playing channel
+check if the channel is active
+get the currently playing Sound
+queue a Sound object to follow the current
+return any Sound that is queued
+have the channel send an event when playback stops
+get the event a channel sends when playback stops
+

Return a Channel object for one of the current channels. The id must be a +value from 0 to the value of pygame.mixer.get_num_channels().

+

The Channel object can be used to get fine control over the playback of +Sounds. A channel can only playback a single Sound at time. Using channels +is entirely optional since pygame can manage them by default.

+
+
+play()
+
+
play a Sound on a specific Channel
+
play(Sound, loops=0, maxtime=0, fade_ms=0) -> None
+
+

This will begin playback of a Sound on a specific Channel. If the Channel +is currently playing any other Sound it will be stopped.

+

The loops argument has the same meaning as in Sound.play(): it is the +number of times to repeat the sound after the first time. If it is 3, the +sound will be played 4 times (the first time, then three more). If loops +is -1 then the playback will repeat indefinitely.

+

As in Sound.play(), the maxtime argument can be used to stop playback +of the Sound after a given number of milliseconds.

+

As in Sound.play(), the fade_ms argument can be used fade in the +sound.

+
+ +
+
+stop()
+
+
stop playback on a Channel
+
stop() -> None
+
+

Stop sound playback on a channel. After playback is stopped the channel +becomes available for new Sounds to play on it.

+
+ +
+
+pause()
+
+
temporarily stop playback of a channel
+
pause() -> None
+
+

Temporarily stop the playback of sound on a channel. It can be resumed at +a later time with Channel.unpause()

+
+ +
+
+unpause()
+
+
resume pause playback of a channel
+
unpause() -> None
+
+

Resume the playback on a paused channel.

+
+ +
+
+fadeout()
+
+
stop playback after fading channel out
+
fadeout(time) -> None
+
+

Stop playback of a channel after fading out the sound over the given time +argument in milliseconds.

+
+ +
+
+set_volume()
+
+
set the volume of a playing channel
+
set_volume(value) -> None
+
set_volume(left, right) -> None
+
+

Set the volume (loudness) of a playing sound. When a channel starts to +play its volume value is reset. This only affects the current sound. The +value argument is between 0.0 and 1.0.

+

If one argument is passed, it will be the volume of both speakers. If two +arguments are passed and the mixer is in stereo mode, the first argument +will be the volume of the left speaker and the second will be the volume +of the right speaker. (If the second argument is None, the first +argument will be the volume of both speakers.)

+

If the channel is playing a Sound on which set_volume() has also been +called, both calls are taken into account. For example:

+
sound = pygame.mixer.Sound("s.wav")
+channel = s.play()      # Sound plays at full volume by default
+sound.set_volume(0.9)   # Now plays at 90% of full volume.
+sound.set_volume(0.6)   # Now plays at 60% (previous value replaced).
+channel.set_volume(0.5) # Now plays at 30% (0.6 * 0.5).
+
+
+
+ +
+
+get_volume()
+
+
get the volume of the playing channel
+
get_volume() -> value
+
+

Return the volume of the channel for the current playing sound. This does +not take into account stereo separation used by +Channel.set_volume(). The Sound object also has its own volume +which is mixed with the channel.

+
+ +
+
+get_busy()
+
+
check if the channel is active
+
get_busy() -> bool
+
+

Returns True if the channel is actively mixing sound. If the channel +is idle this returns False.

+
+ +
+
+get_sound()
+
+
get the currently playing Sound
+
get_sound() -> Sound
+
+

Return the actual Sound object currently playing on this channel. If the +channel is idle None is returned.

+
+ +
+
+queue()
+
+
queue a Sound object to follow the current
+
queue(Sound) -> None
+
+

When a Sound is queued on a Channel, it will begin playing immediately +after the current Sound is finished. Each channel can only have a single +Sound queued at a time. The queued Sound will only play if the current +playback finished automatically. It is cleared on any other call to +Channel.stop() or Channel.play().

+

If there is no sound actively playing on the Channel then the Sound will +begin playing immediately.

+
+ +
+
+get_queue()
+
+
return any Sound that is queued
+
get_queue() -> Sound
+
+

If a Sound is already queued on this channel it will be returned. Once +the queued sound begins playback it will no longer be on the queue.

+
+ +
+
+set_endevent()
+
+
have the channel send an event when playback stops
+
set_endevent() -> None
+
set_endevent(type) -> None
+
+

When an endevent is set for a channel, it will send an event to the +pygame queue every time a sound finishes playing on that channel (not +just the first time). Use pygame.event.get() to retrieve the endevent +once it's sent.

+

Note that if you called Sound.play(n) or Channel.play(sound,n), +the end event is sent only once: after the sound has been played "n+1" +times (see the documentation of Sound.play).

+

If Channel.stop() or Channel.play() is called while the sound was +still playing, the event will be posted immediately.

+

The type argument will be the event id sent to the queue. This can be any +valid event type, but a good choice would be a value between +pygame.locals.USEREVENT and pygame.locals.NUMEVENTS. If no type +argument is given then the Channel will stop sending endevents.

+
+ +
+
+get_endevent()
+
+
get the event a channel sends when playback stops
+
get_endevent() -> type
+
+

Returns the event type to be sent every time the Channel finishes +playback of a Sound. If there is no endevent the function returns +pygame.NOEVENT.

+
+ +
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/mouse.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/mouse.html new file mode 100644 index 00000000..3af97d1a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/mouse.html @@ -0,0 +1,404 @@ + + + + + + + + + pygame.mouse — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.mouse
+
+
pygame module to work with the mouse
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+get the state of the mouse buttons
+get the mouse cursor position
+get the amount of mouse movement
+set the mouse cursor position
+hide or show the mouse cursor
+get the current visibility state of the mouse cursor
+check if the display is receiving mouse input
+set the mouse cursor to a new cursor
+get the current mouse cursor
+

The mouse functions can be used to get the current state of the mouse device. +These functions can also alter the system cursor for the mouse.

+

When the display mode is set, the event queue will start receiving mouse +events. The mouse buttons generate pygame.MOUSEBUTTONDOWN and +pygame.MOUSEBUTTONUP events when they are pressed and released. These +events contain a button attribute representing which button was pressed. The +mouse wheel will generate pygame.MOUSEBUTTONDOWN and +pygame.MOUSEBUTTONUP events when rolled. The button will be set to 4 +when the wheel is rolled up, and to button 5 when the wheel is rolled down. +Whenever the mouse is moved it generates a pygame.MOUSEMOTION event. The +mouse movement is broken into small and accurate motion events. As the mouse +is moving many motion events will be placed on the queue. Mouse motion events +that are not properly cleaned from the event queue are the primary reason the +event queue fills up.

+

If the mouse cursor is hidden, and input is grabbed to the current display the +mouse will enter a virtual input mode, where the relative movements of the +mouse will never be stopped by the borders of the screen. See the functions +pygame.mouse.set_visible() and pygame.event.set_grab() to get this +configured.

+

Mouse Wheel Behavior in pygame 2

+

There is proper functionality for mouse wheel behaviour with pygame 2 supporting +pygame.MOUSEWHEEL events. The new events support horizontal and vertical +scroll movements, with signed integer values representing the amount scrolled +(x and y), as well as flipped direction (the set positive and +negative values for each axis is flipped). Read more about SDL2 +input-related changes here https://wiki.libsdl.org/MigrationGuide#input

+

In pygame 2, the mouse wheel functionality can be used by listening for the +pygame.MOUSEWHEEL type of an event (Bear in mind they still emit +pygame.MOUSEBUTTONDOWN events like in pygame 1.x, as well). +When this event is triggered, a developer can access the appropriate Event object +with pygame.event.get(). The object can be used to access data about the mouse +scroll, such as which (it will tell you what exact mouse device trigger the event).

+
+
Code example of mouse scroll (tested on 2.0.0.dev7)
+
# Taken from husano896's PR thread (slightly modified)
+import pygame
+from pygame.locals import *
+pygame.init()
+screen = pygame.display.set_mode((640, 480))
+clock = pygame.time.Clock()
+
+def main():
+   while True:
+      for event in pygame.event.get():
+            if event.type == QUIT:
+               pygame.quit()
+               return
+            elif event.type == MOUSEWHEEL:
+               print(event)
+               print(event.x, event.y)
+               print(event.flipped)
+               print(event.which)
+               # can access properties with
+               # proper notation(ex: event.y)
+      clock.tick(60)
+
+# Execute game:
+main()
+
+
+
+
+
+pygame.mouse.get_pressed()
+
+
get the state of the mouse buttons
+
get_pressed(num_buttons=3) -> (button1, button2, button3)
+
get_pressed(num_buttons=5) -> (button1, button2, button3, button4, button5)
+
+

Returns a sequence of booleans representing the state of all the mouse +buttons. A true value means the mouse is currently being pressed at the time +of the call.

+

Note, to get all of the mouse events it is better to use either +pygame.event.wait() or pygame.event.get() and check all of those +events to see if they are MOUSEBUTTONDOWN, MOUSEBUTTONUP, or +MOUSEMOTION.

+

Note, that on X11 some X servers use middle button emulation. When you +click both buttons 1 and 3 at the same time a 2 button event +can be emitted.

+

Note, remember to call pygame.event.get() before this function. +Otherwise it will not work as expected.

+

To support five button mice, an optional parameter num_buttons has been +added in pygame 2. When this is set to 5, button4 and button5 +are added to the returned tuple. Only 3 and 5 are valid values +for this parameter.

+
+

Changed in pygame 2.0.0: num_buttons argument added

+
+
+ +
+
+pygame.mouse.get_pos()
+
+
get the mouse cursor position
+
get_pos() -> (x, y)
+
+

Returns the x and y position of the mouse cursor. The position is +relative to the top-left corner of the display. The cursor position can be +located outside of the display window, but is always constrained to the +screen.

+
+ +
+
+pygame.mouse.get_rel()
+
+
get the amount of mouse movement
+
get_rel() -> (x, y)
+
+

Returns the amount of movement in x and y since the previous call to +this function. The relative movement of the mouse cursor is constrained to +the edges of the screen, but see the virtual input mouse mode for a way +around this. Virtual input mode is described at the top of the page.

+
+ +
+
+pygame.mouse.set_pos()
+
+
set the mouse cursor position
+
set_pos([x, y]) -> None
+
+

Set the current mouse position to arguments given. If the mouse cursor is +visible it will jump to the new coordinates. Moving the mouse will generate +a new pygame.MOUSEMOTION event.

+
+ +
+
+pygame.mouse.set_visible()
+
+
hide or show the mouse cursor
+
set_visible(bool) -> bool
+
+

If the bool argument is true, the mouse cursor will be visible. This will +return the previous visible state of the cursor.

+
+ +
+
+pygame.mouse.get_visible()
+
+
get the current visibility state of the mouse cursor
+
get_visible() -> bool
+
+

Get the current visibility state of the mouse cursor. True if the mouse is +visible, False otherwise.

+
+

New in pygame 2.0.0.

+
+
+ +
+
+pygame.mouse.get_focused()
+
+
check if the display is receiving mouse input
+
get_focused() -> bool
+
+

Returns true when pygame is receiving mouse input events (or, in windowing +terminology, is "active" or has the "focus").

+

This method is most useful when working in a window. By contrast, in +full-screen mode, this method always returns true.

+

Note: under MS Windows, the window that has the mouse focus also has the +keyboard focus. But under X-Windows, one window can receive mouse events and +another receive keyboard events. pygame.mouse.get_focused() indicates +whether the pygame window receives mouse events.

+
+ +
+
+pygame.mouse.set_cursor()
+
+
set the mouse cursor to a new cursor
+
set_cursor(pygame.cursors.Cursor) -> None
+
set_cursor(size, hotspot, xormasks, andmasks) -> None
+
set_cursor(hotspot, surface) -> None
+
set_cursor(constant) -> None
+
+

Set the mouse cursor to something new. This function accepts either an explicit +Cursor object or arguments to create a Cursor object.

+

See pygame.cursors.Cursorpygame object representing a cursor for help creating cursors and for examples.

+
+

Changed in pygame 2.0.1.

+
+
+ +
+
+pygame.mouse.get_cursor()
+
+
get the current mouse cursor
+
get_cursor() -> pygame.cursors.Cursor
+
+

Get the information about the mouse system cursor. The return value contains +the same data as the arguments passed into pygame.mouse.set_cursor()set the mouse cursor to a new cursor.

+
+

Note

+

Code that unpacked a get_cursor() call into +size, hotspot, xormasks, andmasks will still work, +assuming the call returns an old school type cursor.

+
+
+

Changed in pygame 2.0.1.

+
+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/music.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/music.html new file mode 100644 index 00000000..8fcf3153 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/music.html @@ -0,0 +1,500 @@ + + + + + + + + + pygame.mixer.music — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.mixer.music
+
+
pygame module for controlling streamed audio
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Load a music file for playback
+Unload the currently loaded music to free up resources
+Start the playback of the music stream
+restart music
+stop the music playback
+temporarily stop music playback
+resume paused music
+stop music playback after fading out
+set the music volume
+get the music volume
+check if the music stream is playing
+set position to play from
+get the music play time
+queue a sound file to follow the current
+have the music send an event when playback stops
+get the event a channel sends when playback stops
+

The music module is closely tied to pygame.mixerpygame module for loading and playing sounds. Use the music module +to control the playback of music in the sound mixer.

+

The difference between the music playback and regular Sound playback is that +the music is streamed, and never actually loaded all at once. The mixer system +only supports a single music stream at once.

+

On older pygame versions, MP3 support was limited under Mac and Linux. This +changed in pygame v2.0.2 which got improved MP3 support. Consider using +OGG file format for music as that can give slightly better compression than +MP3 in most cases.

+
+
+pygame.mixer.music.load()
+
+
Load a music file for playback
+
load(filename) -> None
+
load(fileobj, namehint="") -> None
+
+

This will load a music filename/file object and prepare it for playback. If +a music stream is already playing it will be stopped. This does not start +the music playing.

+

If you are loading from a file object, the namehint parameter can be used to specify +the type of music data in the object. For example: load(fileobj, "ogg").

+
+

Changed in pygame 2.0.2: Added optional namehint argument

+
+
+ +
+
+pygame.mixer.music.unload()
+
+
Unload the currently loaded music to free up resources
+
unload() -> None
+
+

This closes resources like files for any music that may be loaded.

+
+

New in pygame 2.0.0.

+
+
+ +
+
+pygame.mixer.music.play()
+
+
Start the playback of the music stream
+
play(loops=0, start=0.0, fade_ms=0) -> None
+
+

This will play the loaded music stream. If the music is already playing it +will be restarted.

+

loops is an optional integer argument, which is 0 by default, which +indicates how many times to repeat the music. The music repeats indefinitely if +this argument is set to -1.

+

start is an optional float argument, which is 0.0 by default, which +denotes the position in time from which the music starts playing. The starting +position depends on the format of the music played. MP3 and OGG use +the position as time in seconds. For MP3 files the start time position +selected may not be accurate as things like variable bit rate encoding and ID3 +tags can throw off the timing calculations. For MOD music it is the pattern +order number. Passing a start position will raise a NotImplementedError if +the start position cannot be set.

+

fade_ms is an optional integer argument, which is 0 by default, +which denotes the period of time (in milliseconds) over which the music +will fade up from volume level 0.0 to full volume (or the volume level +previously set by set_volume()). The sample may end before the fade-in +is complete. If the music is already streaming fade_ms is ignored.

+
+

Changed in pygame 2.0.0: Added optional fade_ms argument

+
+
+ +
+
+pygame.mixer.music.rewind()
+
+
restart music
+
rewind() -> None
+
+

Resets playback of the current music to the beginning. If pause() has +previously been used to pause the music, the music will remain paused.

+
+

Note

+

rewind() supports a limited number of file types and notably +WAV files are NOT supported. For unsupported file types use play() +which will restart the music that's already playing (note that this +will start the music playing again even if previously paused).

+
+
+ +
+
+pygame.mixer.music.stop()
+
+
stop the music playback
+
stop() -> None
+
+

Stops the music playback if it is currently playing. +endevent will be triggered, if set. +It won't unload the music.

+
+ +
+
+pygame.mixer.music.pause()
+
+
temporarily stop music playback
+
pause() -> None
+
+

Temporarily stop playback of the music stream. It can be resumed with the +unpause() function.

+
+ +
+
+pygame.mixer.music.unpause()
+
+
resume paused music
+
unpause() -> None
+
+

This will resume the playback of a music stream after it has been paused.

+
+ +
+
+pygame.mixer.music.fadeout()
+
+
stop music playback after fading out
+
fadeout(time) -> None
+
+

Fade out and stop the currently playing music.

+

The time argument denotes the integer milliseconds for which the +fading effect is generated.

+

Note, that this function blocks until the music has faded out. Calls +to fadeout() and set_volume() will have no effect during +this time. If an event was set using set_endevent() it will be +called after the music has faded.

+
+ +
+
+pygame.mixer.music.set_volume()
+
+
set the music volume
+
set_volume(volume) -> None
+
+

Set the volume of the music playback.

+

The volume argument is a float between 0.0 and 1.0 that sets +the volume level. When new music is loaded the volume is reset to full +volume. If volume is a negative value it will be ignored and the +volume will remain set at the current level. If the volume argument +is greater than 1.0, the volume will be set to 1.0.

+
+ +
+
+pygame.mixer.music.get_volume()
+
+
get the music volume
+
get_volume() -> value
+
+

Returns the current volume for the mixer. The value will be between 0.0 +and 1.0.

+
+ +
+
+pygame.mixer.music.get_busy()
+
+
check if the music stream is playing
+
get_busy() -> bool
+
+

Returns True when the music stream is actively playing. When the music is +idle this returns False. In pygame 2.0.1 and above this function returns +False when the music is paused. In pygame 1 it returns True when the music +is paused.

+
+

Changed in pygame 2.0.1: Returns False when music paused.

+
+
+ +
+
+pygame.mixer.music.set_pos()
+
+
set position to play from
+
set_pos(pos) -> None
+
+

This sets the position in the music file where playback will start. +The meaning of "pos", a float (or a number that can be converted to a float), +depends on the music format.

+

For MOD files, pos is the integer pattern number in the module. +For OGG it is the absolute position, in seconds, from +the beginning of the sound. For MP3 files, it is the relative position, +in seconds, from the current position. For absolute positioning in an MP3 +file, first call rewind().

+

Other file formats are unsupported. Newer versions of SDL_mixer have +better positioning support than earlier ones. An SDLError is raised if a +particular format does not support positioning.

+

Function set_pos() calls underlining SDL_mixer function +Mix_SetMusicPosition.

+
+

New in pygame 1.9.2.

+
+
+ +
+
+pygame.mixer.music.get_pos()
+
+
get the music play time
+
get_pos() -> time
+
+

This gets the number of milliseconds that the music has been playing for. +The returned time only represents how long the music has been playing; it +does not take into account any starting position offsets.

+
+ +
+
+pygame.mixer.music.queue()
+
+
queue a sound file to follow the current
+
queue(filename) -> None
+
queue(fileobj, namehint="", loops=0) -> None
+
+

This will load a sound file and queue it. A queued sound file will begin as +soon as the current sound naturally ends. Only one sound can be queued at a +time. Queuing a new sound while another sound is queued will result in the +new sound becoming the queued sound. Also, if the current sound is ever +stopped or changed, the queued sound will be lost.

+

If you are loading from a file object, the namehint parameter can be used to specify +the type of music data in the object. For example: queue(fileobj, "ogg").

+

The following example will play music by Bach six times, then play music by +Mozart once:

+
pygame.mixer.music.load('bach.ogg')
+pygame.mixer.music.play(5)        # Plays six times, not five!
+pygame.mixer.music.queue('mozart.ogg')
+
+
+
+

Changed in pygame 2.0.2: Added optional namehint argument

+
+
+ +
+
+pygame.mixer.music.set_endevent()
+
+
have the music send an event when playback stops
+
set_endevent() -> None
+
set_endevent(type) -> None
+
+

This causes pygame to signal (by means of the event queue) when the music is +done playing. The argument determines the type of event that will be queued.

+

The event will be queued every time the music finishes, not just the first +time. To stop the event from being queued, call this method with no +argument.

+
+ +
+
+pygame.mixer.music.get_endevent()
+
+
get the event a channel sends when playback stops
+
get_endevent() -> type
+
+

Returns the event type to be sent every time the music finishes playback. If +there is no endevent the function returns pygame.NOEVENT.

+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/overlay.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/overlay.html new file mode 100644 index 00000000..ebf516e3 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/overlay.html @@ -0,0 +1,229 @@ + + + + + + + + + pygame.Overlay — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

Warning

+

This module is non functional in pygame 2.0 and above, unless you have manually compiled pygame with SDL1. +This module will not be supported in the future.

+
+
+
+pygame.Overlay
+
+
pygame object for video overlay graphics
+
Overlay(format, (width, height)) -> Overlay
+
+ +++++ + + + + + + + + + + + + + + +
+set the overlay pixel data
+control where the overlay is displayed
+test if the Overlay is hardware accelerated
+

The Overlay objects provide support for accessing hardware video overlays. +Video overlays do not use standard RGB pixel formats, and can use +multiple resolutions of data to create a single image.

+

The Overlay objects represent lower level access to the display hardware. To +use the object you must understand the technical details of video overlays.

+

The Overlay format determines the type of pixel data used. Not all hardware +will support all types of overlay formats. Here is a list of available +format types:

+
YV12_OVERLAY, IYUV_OVERLAY, YUY2_OVERLAY, UYVY_OVERLAY, YVYU_OVERLAY
+
+
+

The width and height arguments control the size for the overlay image data. +The overlay image can be displayed at any size, not just the resolution of +the overlay.

+

The overlay objects are always visible, and always show above the regular +display contents.

+
+
+display()
+
+
set the overlay pixel data
+
display((y, u, v)) -> None
+
display() -> None
+
+

Display the YUV data in SDL's overlay planes. The y, u, and v arguments +are strings of binary data. The data must be in the correct format used +to create the Overlay.

+

If no argument is passed in, the Overlay will simply be redrawn with the +current data. This can be useful when the Overlay is not really hardware +accelerated.

+

The strings are not validated, and improperly sized strings could crash +the program.

+
+ +
+
+set_location()
+
+
control where the overlay is displayed
+
set_location(rect) -> None
+
+

Set the location for the overlay. The overlay will always be shown +relative to the main display Surface. This does not actually redraw the +overlay, it will be updated on the next call to Overlay.display().

+
+ +
+
+get_hardware()
+
+
test if the Overlay is hardware accelerated
+
get_hardware(rect) -> int
+
+

Returns a True value when the Overlay is hardware accelerated. If the +platform does not support acceleration, software rendering is used.

+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/pixelarray.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/pixelarray.html new file mode 100644 index 00000000..545d75ae --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/pixelarray.html @@ -0,0 +1,484 @@ + + + + + + + + + pygame.PixelArray — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.PixelArray
+
+
pygame object for direct pixel access of surfaces
+
PixelArray(Surface) -> PixelArray
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Gets the Surface the PixelArray uses.
+Returns the byte size of a pixel array item
+Returns the number of dimensions.
+Returns the array size.
+Returns byte offsets for each array dimension.
+Creates a new Surface from the current PixelArray.
+Replaces the passed color in the PixelArray with another one.
+Extracts the passed color from the PixelArray.
+Compares the PixelArray with another one.
+Exchanges the x and y axis.
+Closes the PixelArray, and releases Surface lock.
+

The PixelArray wraps a Surface and provides direct access to the +surface's pixels. A pixel array can be one or two dimensional. +A two dimensional array, like its surface, is indexed [column, row]. +Pixel arrays support slicing, both for returning a subarray or +for assignment. A pixel array sliced on a single column or row +returns a one dimensional pixel array. Arithmetic and other operations +are not supported. A pixel array can be safely assigned to itself. +Finally, pixel arrays export an array struct interface, allowing +them to interact with pygame.pixelcopypygame module for general pixel array copying methods and NumPy +arrays.

+

A PixelArray pixel item can be assigned a raw integer values, a +pygame.Colorpygame object for color representations instance, or a (r, g, b[, a]) tuple.

+
pxarray[x, y] = 0xFF00FF
+pxarray[x, y] = pygame.Color(255, 0, 255)
+pxarray[x, y] = (255, 0, 255)
+
+
+

However, only a pixel's integer value is returned. So, to compare a pixel +to a particular color the color needs to be first mapped using +the Surface.map_rgb() method of the Surface object for which the +PixelArray was created.

+
pxarray = pygame.PixelArray(surface)
+# Check, if the first pixel at the topleft corner is blue
+if pxarray[0, 0] == surface.map_rgb((0, 0, 255)):
+    ...
+
+
+

When assigning to a range of of pixels, a non tuple sequence of colors or +a PixelArray can be used as the value. For a sequence, the length must +match the PixelArray width.

+
pxarray[a:b] = 0xFF00FF                   # set all pixels to 0xFF00FF
+pxarray[a:b] = (0xFF00FF, 0xAACCEE, ... ) # first pixel = 0xFF00FF,
+                                          # second pixel  = 0xAACCEE, ...
+pxarray[a:b] = [(255, 0, 255), (170, 204, 238), ...] # same as above
+pxarray[a:b] = [(255, 0, 255), 0xAACCEE, ...]        # same as above
+pxarray[a:b] = otherarray[x:y]            # slice sizes must match
+
+
+

For PixelArray assignment, if the right hand side array has a row length +of 1, then the column is broadcast over the target array's rows. An +array of height 1 is broadcast over the target's columns, and is equivalent +to assigning a 1D PixelArray.

+

Subscript slices can also be used to assign to a rectangular subview of +the target PixelArray.

+
# Create some new PixelArray objects providing a different view
+# of the original array/surface.
+newarray = pxarray[2:4, 3:5]
+otherarray = pxarray[::2, ::2]
+
+
+

Subscript slices can also be used to do fast rectangular pixel manipulations +instead of iterating over the x or y axis. The

+
pxarray[::2, :] = (0, 0, 0)               # Make even columns black.
+pxarray[::2] = (0, 0, 0)                  # Same as [::2, :]
+
+
+

During its lifetime, the PixelArray locks the surface, thus you explicitly +have to close() it once its not used any more and the surface should perform +operations in the same scope. It is best to use it as a context manager +using the with PixelArray(surf) as pixel_array: style. So it works on pypy too.

+

A simple : slice index for the column can be omitted.

+
pxarray[::2, ...] = (0, 0, 0)             # Same as pxarray[::2, :]
+pxarray[...] = (255, 0, 0)                # Same as pxarray[:]
+
+
+

A note about PixelArray to PixelArray assignment, for arrays with an +item size of 3 (created from 24 bit surfaces) pixel values are translated +from the source to the destinations format. The red, green, and blue +color elements of each pixel are shifted to match the format of the +target surface. For all other pixel sizes no such remapping occurs. +This should change in later pygame releases, where format conversions +are performed for all pixel sizes. To avoid code breakage when full mapped +copying is implemented it is suggested PixelArray to PixelArray copies be +only between surfaces of identical format.

+
+

New in pygame 1.9.4:

+
    +
  • close() method was added. For explicitly cleaning up.

  • +
  • being able to use PixelArray as a context manager for cleanup.

  • +
  • both of these are useful for when working without reference counting (pypy).

  • +
+
+
+

New in pygame 1.9.2:

+
    +
  • array struct interface

  • +
  • transpose method

  • +
  • broadcasting for a length 1 dimension

  • +
+
+
+

Changed in pygame 1.9.2:

+
    +
  • A 2D PixelArray can have a length 1 dimension. +Only an integer index on a 2D PixelArray returns a 1D array.

  • +
  • For assignment, a tuple can only be a color. Any other sequence type +is a sequence of colors.

  • +
+
+
+
+surface
+
+
Gets the Surface the PixelArray uses.
+
surface -> Surface
+
+

The Surface the PixelArray was created for.

+
+ +
+
+itemsize
+
+
Returns the byte size of a pixel array item
+
itemsize -> int
+
+

This is the same as Surface.get_bytesize() for the +pixel array's surface.

+
+

New in pygame 1.9.2.

+
+
+ +
+
+ndim
+
+
Returns the number of dimensions.
+
ndim -> int
+
+

A pixel array can be 1 or 2 dimensional.

+
+

New in pygame 1.9.2.

+
+
+ +
+
+shape
+
+
Returns the array size.
+
shape -> tuple of int's
+
+

A tuple or length ndim giving the length of each +dimension. Analogous to Surface.get_size().

+
+

New in pygame 1.9.2.

+
+
+ +
+
+strides
+
+
Returns byte offsets for each array dimension.
+
strides -> tuple of int's
+
+

A tuple or length ndim byte counts. When a stride is +multiplied by the corresponding index it gives the offset +of that index from the start of the array. A stride is negative +for an array that has is inverted (has a negative step).

+
+

New in pygame 1.9.2.

+
+
+ +
+
+make_surface()
+
+
Creates a new Surface from the current PixelArray.
+
make_surface() -> Surface
+
+

Creates a new Surface from the current PixelArray. Depending on the +current PixelArray the size, pixel order etc. will be different from the +original Surface.

+
# Create a new surface flipped around the vertical axis.
+sf = pxarray[:,::-1].make_surface ()
+
+
+
+

New in pygame 1.8.1.

+
+
+ +
+
+replace()
+
+
Replaces the passed color in the PixelArray with another one.
+
replace(color, repcolor, distance=0, weights=(0.299, 0.587, 0.114)) -> None
+
+

Replaces the pixels with the passed color in the PixelArray by changing +them them to the passed replacement color.

+

It uses a simple weighted Euclidean distance formula to calculate the +distance between the colors. The distance space ranges from 0.0 to 1.0 +and is used as threshold for the color detection. This causes the +replacement to take pixels with a similar, but not exactly identical +color, into account as well.

+

This is an in place operation that directly affects the pixels of the +PixelArray.

+
+

New in pygame 1.8.1.

+
+
+ +
+
+extract()
+
+
Extracts the passed color from the PixelArray.
+
extract(color, distance=0, weights=(0.299, 0.587, 0.114)) -> PixelArray
+
+

Extracts the passed color by changing all matching pixels to white, while +non-matching pixels are changed to black. This returns a new PixelArray +with the black/white color mask.

+

It uses a simple weighted Euclidean distance formula to calculate the +distance between the colors. The distance space ranges from 0.0 to 1.0 +and is used as threshold for the color detection. This causes the +extraction to take pixels with a similar, but not exactly identical +color, into account as well.

+
+

New in pygame 1.8.1.

+
+
+ +
+
+compare()
+
+
Compares the PixelArray with another one.
+
compare(array, distance=0, weights=(0.299, 0.587, 0.114)) -> PixelArray
+
+

Compares the contents of the PixelArray with those from the passed in +PixelArray. It returns a new PixelArray with a black/white color mask +that indicates the differences (black) of both arrays. Both PixelArray +objects must have identical bit depths and dimensions.

+

It uses a simple weighted Euclidean distance formula to calculate the +distance between the colors. The distance space ranges from 0.0 to 1.0 +and is used as a threshold for the color detection. This causes the +comparison to mark pixels with a similar, but not exactly identical +color, as white.

+
+

New in pygame 1.8.1.

+
+
+ +
+
+transpose()
+
+
Exchanges the x and y axis.
+
transpose() -> PixelArray
+
+

This method returns a new view of the pixel array with the rows and +columns swapped. So for a (w, h) sized array a (h, w) slice is returned. +If an array is one dimensional, then a length 1 x dimension is added, +resulting in a 2D pixel array.

+
+

New in pygame 1.9.2.

+
+
+ +
+
+close()
+
+
Closes the PixelArray, and releases Surface lock.
+
close() -> PixelArray
+
+

This method is for explicitly closing the PixelArray, and releasing +a lock on the Surface.

+
+

New in pygame 1.9.4.

+
+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/pixelcopy.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/pixelcopy.html new file mode 100644 index 00000000..38d2094f --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/pixelcopy.html @@ -0,0 +1,260 @@ + + + + + + + + + pygame.pixelcopy — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.pixelcopy
+
+
pygame module for general pixel array copying
+
+ +++++ + + + + + + + + + + + + + + + + + + +
+copy surface pixels to an array object
+copy an array object to a surface
+copy an array to another array, using surface format
+Copy an array to a new surface
+

The pygame.pixelcopy module contains functions for copying between +surfaces and objects exporting an array structure interface. It is a backend +for pygame.surfarraypygame module for accessing surface pixel data using array interfaces, adding NumPy support. But pixelcopy is more +general, and intended for direct use.

+

The array struct interface exposes an array's data in a standard way. +It was introduced in NumPy. In Python 2.7 and above it is replaced by the +new buffer protocol, though the buffer protocol is still a work in progress. +The array struct interface, on the other hand, is stable and works with earlier +Python versions. So for now the array struct interface is the predominate way +pygame handles array introspection.

+

For 2d arrays of integer pixel values, the values are mapped to the +pixel format of the related surface. To get the actual color of a pixel +value use pygame.Surface.unmap_rgb()convert a mapped integer color value into a Color. 2d arrays can only be used +directly between surfaces having the same pixel layout.

+

New in pygame 1.9.2.

+
+
+pygame.pixelcopy.surface_to_array()
+
+
copy surface pixels to an array object
+
surface_to_array(array, surface, kind='P', opaque=255, clear=0) -> None
+
+

The surface_to_array function copies pixels from a Surface object +to a 2D or 3D array. Depending on argument kind and the target array +dimension, a copy may be raw pixel value, RGB, a color component slice, +or colorkey alpha transparency value. Recognized kind values are the +single character codes 'P', 'R', 'G', 'B', 'A', and 'C'. Kind codes are case +insensitive, so 'p' is equivalent to 'P'. The first two dimensions +of the target must be the surface size (w, h).

+

The default 'P' kind code does a direct raw integer pixel (mapped) value +copy to a 2D array and a 'RGB' pixel component (unmapped) copy to a 3D array +having shape (w, h, 3). For an 8 bit colormap surface this means the +table index is copied to a 2D array, not the table value itself. A 2D +array's item size must be at least as large as the surface's pixel +byte size. The item size of a 3D array must be at least one byte.

+

For the 'R', 'G', 'B', and 'A' copy kinds a single color component +of the unmapped surface pixels are copied to the target 2D array. +For kind 'A' and surfaces with source alpha (the surface was created with +the SRCALPHA flag), has a colorkey +(set with Surface.set_colorkey()), +or has a blanket alpha +(set with Surface.set_alpha()) +then the alpha values are those expected for a SDL surface. +If a surface has no explicit alpha value, then the target array +is filled with the value of the optional opaque surface_to_array +argument (default 255: not transparent).

+

Copy kind 'C' is a special case for alpha copy of a source surface +with colorkey. Unlike the 'A' color component copy, the clear +argument value is used for colorkey matches, opaque otherwise. +By default, a match has alpha 0 (totally transparent), while everything +else is alpha 255 (totally opaque). It is a more general implementation +of pygame.surfarray.array_colorkey()Copy the colorkey values into a 2d array.

+

Specific to surface_to_array, a ValueError is raised for target arrays +with incorrect shape or item size. A TypeError is raised for an incorrect +kind code. Surface specific problems, such as locking, raise a pygame.error.

+
+ +
+
+pygame.pixelcopy.array_to_surface()
+
+
copy an array object to a surface
+
array_to_surface(<surface>, <array>) -> None
+
+

See pygame.surfarray.blit_array()Blit directly from a array values.

+
+ +
+
+pygame.pixelcopy.map_array()
+
+
copy an array to another array, using surface format
+
map_array(<array>, <array>, <surface>) -> None
+
+

Map an array of color element values - (w, h, ..., 3) - to an array of +pixels - (w, h) according to the format of <surface>.

+
+ +
+
+pygame.pixelcopy.make_surface()
+
+
Copy an array to a new surface
+
pygame.pixelcopy.make_surface(array) -> Surface
+
+

Create a new Surface that best resembles the data and format of the array. +The array can be 2D or 3D with any sized integer values.

+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/pygame.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/pygame.html new file mode 100644 index 00000000..0c11bb81 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/pygame.html @@ -0,0 +1,708 @@ + + + + + + + + + pygame — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame
+
+
the top level pygame package
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+initialize all imported pygame modules
+uninitialize all pygame modules
+returns True if pygame is currently initialized
+standard pygame exception
+get the current error message
+set the current error message
+get the version number of SDL
+get the byte order of SDL
+register a function to be called when pygame quits
+Encode a Unicode or bytes object
+Encode a Unicode or bytes object as a file system path
+

The pygame package represents the top-level package for others to use. Pygame +itself is broken into many submodules, but this does not affect programs that +use pygame.

+

As a convenience, most of the top-level variables in pygame have been placed +inside a module named pygame.localspygame constants. This is meant to be used with +from pygame.locals import *, in addition to import pygame.

+

When you import pygame all available pygame submodules are automatically +imported. Be aware that some of the pygame modules are considered optional, +and may not be available. In that case, pygame will provide a placeholder +object instead of the module, which can be used to test for availability.

+
+
+pygame.init()
+
+
initialize all imported pygame modules
+
init() -> (numpass, numfail)
+
+

Initialize all imported pygame modules. No exceptions will be raised if a +module fails, but the total number if successful and failed inits will be +returned as a tuple. You can always initialize individual modules manually, +but pygame.init()initialize all imported pygame modules is a convenient way to get everything started. The +init() functions for individual modules will raise exceptions when they +fail.

+

You may want to initialize the different modules separately to speed up your +program or to not use modules your game does not require.

+

It is safe to call this init() more than once as repeated calls will have +no effect. This is true even if you have pygame.quit() all the modules.

+
+ +
+
+pygame.quit()
+
+
uninitialize all pygame modules
+
quit() -> None
+
+

Uninitialize all pygame modules that have previously been initialized. When +the Python interpreter shuts down, this method is called regardless, so your +program should not need it, except when it wants to terminate its pygame +resources and continue. It is safe to call this function more than once as +repeated calls have no effect.

+
+

Note

+

Calling pygame.quit()uninitialize all pygame modules will not exit your program. Consider letting +your program end in the same way a normal Python program will end.

+
+
+ +
+
+pygame.get_init()
+
+
returns True if pygame is currently initialized
+
get_init() -> bool
+
+

Returns True if pygame is currently initialized.

+
+

New in pygame 1.9.5.

+
+
+ +
+
+exception pygame.error
+
+
standard pygame exception
+
raise pygame.error(message)
+
+

This exception is raised whenever a pygame or SDL operation fails. You +can catch any anticipated problems and deal with the error. The exception is +always raised with a descriptive message about the problem.

+

Derived from the RuntimeError exception, which can also be used to catch +these raised errors.

+
+ +
+
+pygame.get_error()
+
+
get the current error message
+
get_error() -> errorstr
+
+

SDL maintains an internal error message. This message will usually be +given to you when pygame.error()standard pygame exception is raised, so this function will +rarely be needed.

+
+ +
+
+pygame.set_error()
+
+
set the current error message
+
set_error(error_msg) -> None
+
+

SDL maintains an internal error message. This message will usually be +given to you when pygame.error()standard pygame exception is raised, so this function will +rarely be needed.

+
+ +
+
+pygame.get_sdl_version()
+
+
get the version number of SDL
+
get_sdl_version(linked=True) -> major, minor, patch
+
+

Returns the three version numbers of the SDL library. linked=True +will cause the function to return the version of the library that pygame +is linked against while linked=False will cause the function to return +the version of the library that pygame is compiled against. +It can be used to detect which features may or may not be +available through pygame.

+
+

New in pygame 1.7.0.

+
+
+

Changed in pygame 2.2.0: linked keyword argument added

+
+
+ +
+
+pygame.get_sdl_byteorder()
+
+
get the byte order of SDL
+
get_sdl_byteorder() -> int
+
+

Returns the byte order of the SDL library. It returns 1234 for little +endian byte order and 4321 for big endian byte order.

+
+

New in pygame 1.8.

+
+
+ +
+
+pygame.register_quit()
+
+
register a function to be called when pygame quits
+
register_quit(callable) -> None
+
+

When pygame.quit()uninitialize all pygame modules is called, all registered quit functions are +called. Pygame modules do this automatically when they are initializing, so +this function will rarely be needed.

+
+ +
+
+pygame.encode_string()
+
+
Encode a Unicode or bytes object
+
encode_string([obj [, encoding [, errors [, etype]]]]) -> bytes or None
+
+

obj: If Unicode, encode; if bytes, return unaltered; if anything else, +return None; if not given, raise SyntaxError.

+

encoding (string): If present, encoding to use. The default is +'unicode_escape'.

+

errors (string): If given, how to handle unencodable characters. The default +is 'backslashreplace'.

+

etype (exception type): If given, the exception type to raise for an +encoding error. The default is UnicodeEncodeError, as returned by +PyUnicode_AsEncodedString(). For the default encoding and errors values +there should be no encoding errors.

+

This function is used in encoding file paths. Keyword arguments are +supported.

+
+

New in pygame 1.9.2: (primarily for use in unit tests)

+
+
+ +
+
+pygame.encode_file_path()
+
+
Encode a Unicode or bytes object as a file system path
+
encode_file_path([obj [, etype]]) -> bytes or None
+
+

obj: If Unicode, encode; if bytes, return unaltered; if anything else, +return None; if not given, raise SyntaxError.

+

etype (exception type): If given, the exception type to raise for an +encoding error. The default is UnicodeEncodeError, as returned by +PyUnicode_AsEncodedString().

+

This function is used to encode file paths in pygame. Encoding is to the +codec as returned by sys.getfilesystemencoding(). Keyword arguments are +supported.

+
+

New in pygame 1.9.2: (primarily for use in unit tests)

+
+
+ +
+ +
+
+
+
+pygame.version
+
+
small module containing version information
+
+ +++++ + + + + + + + + + + + + + + + + + + +
+version number as a string
+tupled integers of the version
+repository revision of the build
+tupled integers of the SDL library version
+

This module is automatically imported into the pygame package and can be used to +check which version of pygame has been imported.

+
+
+pygame.version.ver
+
+
version number as a string
+
ver = '1.2'
+
+

This is the version represented as a string. It can contain a micro release +number as well, e.g. '1.5.2'

+
+ +
+
+pygame.version.vernum
+
+
tupled integers of the version
+
vernum = (1, 5, 3)
+
+

This version information can easily be compared with other version +numbers of the same format. An example of checking pygame version numbers +would look like this:

+
if pygame.version.vernum < (1, 5):
+    print('Warning, older version of pygame (%s)' %  pygame.version.ver)
+    disable_advanced_features = True
+
+
+
+

New in pygame 1.9.6: Attributes major, minor, and patch.

+
+
vernum.major == vernum[0]
+vernum.minor == vernum[1]
+vernum.patch == vernum[2]
+
+
+
+

Changed in pygame 1.9.6: str(pygame.version.vernum) returns a string like "2.0.0" instead +of "(2, 0, 0)".

+
+
+

Changed in pygame 1.9.6: repr(pygame.version.vernum) returns a string like +"PygameVersion(major=2, minor=0, patch=0)" instead of "(2, 0, 0)".

+
+
+ +
+
+pygame.version.rev
+
+
repository revision of the build
+
rev = 'a6f89747b551+'
+
+

The Mercurial node identifier of the repository checkout from which this +package was built. If the identifier ends with a plus sign '+' then the +package contains uncommitted changes. Please include this revision number +in bug reports, especially for non-release pygame builds.

+

Important note: pygame development has moved to github, this variable is +obsolete now. As soon as development shifted to github, this variable started +returning an empty string "". +It has always been returning an empty string since v1.9.5.

+
+

Changed in pygame 1.9.5: Always returns an empty string "".

+
+
+ +
+
+pygame.version.SDL
+
+
tupled integers of the SDL library version
+
SDL = '(2, 0, 12)'
+
+

This is the SDL library version represented as an extended tuple. It also has +attributes 'major', 'minor' & 'patch' that can be accessed like this:

+
>>> pygame.version.SDL.major
+2
+
+
+

printing the whole thing returns a string like this:

+
>>> pygame.version.SDL
+SDLVersion(major=2, minor=0, patch=12)
+
+
+
+

New in pygame 2.0.0.

+
+
+ +

Setting Environment Variables

+

Some aspects of pygame's behaviour can be controlled by setting environment variables, they cover a wide +range of the library's functionality. Some of the variables are from pygame itself, while others come from +the underlying C SDL library that pygame uses.

+

In python, environment variables are usually set in code like this:

+
import os
+os.environ['NAME_OF_ENVIRONMENT_VARIABLE'] = 'value_to_set'
+
+
+

Or to preserve users ability to override the variable:

+
import os
+os.environ['ENV_VAR'] = os.environ.get('ENV_VAR', 'value')
+
+
+

If the variable is more useful for users of an app to set than the developer then they can set it like this:

+

Windows:

+
set NAME_OF_ENVIRONMENT_VARIABLE=value_to_set
+python my_application.py
+
+
+

Linux/Mac:

+
ENV_VAR=value python my_application.py
+
+
+

For some variables they need to be set before initialising pygame, some must be set before even importing pygame, +and others can simply be set right before the area of code they control is run.

+

Below is a list of environment variables, their settable values, and a brief description of what they do.

+
+

+
+

Pygame Environment Variables

+

These variables are defined by pygame itself.

+
+

+
+
PYGAME_DISPLAY - Experimental (subject to change)
+Set index of the display to use, "0" is the default.
+
+
+

This sets the display where pygame will open its window +or screen. The value set here will be used if set before +calling pygame.display.set_mode()Initialize a window or screen for display, and as long as no +'display' parameter is passed into pygame.display.set_mode()Initialize a window or screen for display.

+
+

+
+
PYGAME_FORCE_SCALE -
+Set to "photo" or "default".
+
+
+

This forces set_mode() to use the SCALED display mode and, +if "photo" is set, makes the scaling use the slowest, but +highest quality anisotropic scaling algorithm, if it is +available. Must be set before calling pygame.display.set_mode()Initialize a window or screen for display.

+
+

+
+
PYGAME_BLEND_ALPHA_SDL2 - New in pygame 2.0.0
+Set to "1" to enable the SDL2 blitter.
+
+
+

This makes pygame use the SDL2 blitter for all alpha +blending. The SDL2 blitter is sometimes faster than +the default blitter but uses a different formula so +the final colours may differ. Must be set before +pygame.init()initialize all imported pygame modules is called.

+
+

+
+
PYGAME_HIDE_SUPPORT_PROMPT -
+Set to "1" to hide the prompt.
+
+
+

This stops the welcome message popping up in the +console that tells you which version of python, +pygame & SDL you are using. Must be set before +importing pygame.

+
+

+
+
PYGAME_FREETYPE -
+Set to "1" to enable.
+
+
+

This switches the pygame.font module to a pure +freetype implementation that bypasses SDL_ttf. +See the font module for why you might want to +do this. Must be set before importing pygame.

+
+

+
+
PYGAME_CAMERA -
+Set to "opencv" or "vidcapture"
+
+
+

Forces the library backend used in the camera +module, overriding the platform defaults. Must +be set before calling pygame.camera.init()Module init.

+

In pygame 2.0.3, backends can be set programmatically instead, and the old +OpenCV backend has been replaced with one on top of "opencv-python," rather +than the old "highgui" OpenCV port. Also, there is a new native Windows +backend available.

+
+

+

+
+

SDL Environment Variables

+

These variables are defined by SDL.

+

For documentation on the environment variables available in +pygame 1 try here. +For Pygame 2, some selected environment variables are listed below.

+
+

+
+
SDL_VIDEO_CENTERED -
+Set to "1" to enable centering the window.
+
+
+

This will make the pygame window open in the centre of the display. +Must be set before calling pygame.display.set_mode()Initialize a window or screen for display.

+
+

+
+
SDL_VIDEO_WINDOW_POS -
+Set to "x,y" to position the top left corner of the window.
+
+
+

This allows control over the placement of the pygame window within +the display. Must be set before calling pygame.display.set_mode()Initialize a window or screen for display.

+
+

+
+
SDL_VIDEODRIVER -
+Set to "drivername" to change the video driver used.
+
+
+

On some platforms there are multiple video drivers available and +this allows users to pick between them. More information is available +here. Must be set before +calling pygame.init()initialize all imported pygame modules or pygame.display.init()Initialize the display module.

+
+

+
+
SDL_AUDIODRIVER -
+Set to "drivername" to change the audio driver used.
+
+
+

On some platforms there are multiple audio drivers available and +this allows users to pick between them. More information is available +here. Must be set before +calling pygame.init()initialize all imported pygame modules or pygame.mixer.init()initialize the mixer module.

+
+

+
+
SDL_VIDEO_ALLOW_SCREENSAVER
+Set to "1" to allow screensavers while pygame apps are running.
+
+
+

By default pygame apps disable screensavers while +they are running. Setting this environment variable allows users or +developers to change that and make screensavers run again.

+
+

+
+
SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
+Set to "0" to re-enable the compositor.
+
+
+

By default SDL tries to disable the X11 compositor for all pygame +apps. This is usually a good thing as it's faster, however if you +have an app which doesn't update every frame and are using linux +you may want to disable this bypass. The bypass has reported problems +on KDE linux. This variable is only used on x11/linux platforms.

+
+

+
+
SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS
+Set to "1" to allow joysticks to be updated even when the window is out of focus
+
+
+

By default, when the window is not in focus, input devices do not get +updated. However, using this environment variable it is possible to get +joystick updates even when the window is in the background. Must be set +before calling pygame.init()initialize all imported pygame modules or pygame.joystick.init()Initialize the joystick module..

+
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/rect.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/rect.html new file mode 100644 index 00000000..922e91cf --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/rect.html @@ -0,0 +1,908 @@ + + + + + + + + + pygame.Rect — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.Rect
+
+
pygame object for storing rectangular coordinates
+
Rect(left, top, width, height) -> Rect
+
Rect((left, top), (width, height)) -> Rect
+
Rect(object) -> Rect
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+copy the rectangle
+moves the rectangle
+moves the rectangle, in place
+grow or shrink the rectangle size
+grow or shrink the rectangle size, in place
+scale the rectangle by given a multiplier
+grow or shrink the rectangle size, in place
+sets the position and size of the rectangle
+moves the rectangle inside another
+moves the rectangle inside another, in place
+crops a rectangle inside another
+crops a line inside a rectangle
+joins two rectangles into one
+joins two rectangles into one, in place
+the union of many rectangles
+the union of many rectangles, in place
+resize and move a rectangle with aspect ratio
+correct negative sizes
+test if one rectangle is inside another
+test if a point is inside a rectangle
+test if two rectangles overlap
+test if one rectangle in a list intersects
+test if all rectangles in a list intersect
+test if any object in a list intersects
+test if all objects in a list intersect
+test if one rectangle in a dictionary intersects
+test if all rectangles in a dictionary intersect
+

Pygame uses Rect objects to store and manipulate rectangular areas. A Rect +can be created from a combination of left, top, width, and height values. +Rects can also be created from Python objects that are already a Rect or +have an attribute named "rect".

+

Any Pygame function that requires a Rect argument also accepts any of these +values to construct a Rect. This makes it easier to create Rects on the fly +as arguments for functions.

+

The Rect functions that change the position or size of a Rect return a new +copy of the Rect with the affected changes. The original Rect is not +modified. Some methods have an alternate "in-place" version that returns +None but affects the original Rect. These "in-place" methods are denoted +with the "ip" suffix.

+

The Rect object has several virtual attributes which can be used to move and +align the Rect:

+
x,y
+top, left, bottom, right
+topleft, bottomleft, topright, bottomright
+midtop, midleft, midbottom, midright
+center, centerx, centery
+size, width, height
+w,h
+
+
+

All of these attributes can be assigned to:

+
rect1.right = 10
+rect2.center = (20,30)
+
+
+

Assigning to size, width or height changes the dimensions of the rectangle; +all other assignments move the rectangle without resizing it. Notice that +some attributes are integers and others are pairs of integers.

+

If a Rect has a nonzero width or height, it will return True for a +nonzero test. Some methods return a Rect with 0 size to represent an invalid +rectangle. A Rect with a 0 size will not collide when using collision +detection methods (e.g. collidepoint(), colliderect(), etc.).

+

The coordinates for Rect objects are all integers. The size values can be +programmed to have negative values, but these are considered illegal Rects +for most operations.

+

There are several collision tests between other rectangles. Most python +containers can be searched for collisions against a single Rect.

+

The area covered by a Rect does not include the right- and bottom-most edge +of pixels. If one Rect's bottom border is another Rect's top border (i.e., +rect1.bottom=rect2.top), the two meet exactly on the screen but do not +overlap, and rect1.colliderect(rect2) returns false.

+

The Rect object is also iterable:

+
r = Rect(0, 1, 2, 3)
+x, y, w, h = r
+
+
+
+

New in pygame 1.9.2: The Rect class can be subclassed. Methods such as copy() and move() +will recognize this and return instances of the subclass. +However, the subclass's __init__() method is not called, +and __new__() is assumed to take no arguments. So these methods should be +overridden if any extra attributes need to be copied.

+
+
+
+copy()
+
+
copy the rectangle
+
copy() -> Rect
+
+

Returns a new rectangle having the same position and size as the original.

+

New in pygame 1.9

+
+ +
+
+move()
+
+
moves the rectangle
+
move(x, y) -> Rect
+
+

Returns a new rectangle that is moved by the given offset. The x and y +arguments can be any integer value, positive or negative.

+
+ +
+
+move_ip()
+
+
moves the rectangle, in place
+
move_ip(x, y) -> None
+
+

Same as the Rect.move() method, but operates in place.

+
+ +
+
+inflate()
+
+
grow or shrink the rectangle size
+
inflate(x, y) -> Rect
+
+

Returns a new rectangle with the size changed by the given offset. The +rectangle remains centered around its current center. Negative values +will shrink the rectangle. Note, uses integers, if the offset given is +too small(< 2 > -2), center will be off.

+
+ +
+
+inflate_ip()
+
+
grow or shrink the rectangle size, in place
+
inflate_ip(x, y) -> None
+
+

Same as the Rect.inflate() method, but operates in place.

+
+ +
+
+scale_by()
+
+
scale the rectangle by given a multiplier
+
scale_by(scalar) -> Rect
+
scale_by(scalex, scaley) -> Rect
+
+

Returns a new rectangle with the size scaled by the given multipliers. +The rectangle remains centered around its current center. A single +scalar or separate width and height scalars are allowed. Values above +one will increase the size of the rectangle, whereas values between +zero and one will decrease the size of the rectangle.

+
+

Changed in pygame 2.5.0: Added support for keyword arguments.

+
+
+ +
+
+scale_by_ip()
+
+
grow or shrink the rectangle size, in place
+
scale_by_ip(scalar) -> None
+
scale_by_ip(scalex, scaley) -> None
+
+

Same as the Rect.scale_by() method, but operates in place.

+
+

Changed in pygame 2.5.0: Added support for keyword arguments.

+
+
+ +
+
+update()
+
+
sets the position and size of the rectangle
+
update(left, top, width, height) -> None
+
update((left, top), (width, height)) -> None
+
update(object) -> None
+
+

Sets the position and size of the rectangle, in place. See +parameters for pygame.Rect()pygame object for storing rectangular coordinates for the parameters of this function.

+
+

New in pygame 2.0.1.

+
+
+ +
+
+clamp()
+
+
moves the rectangle inside another
+
clamp(Rect) -> Rect
+
+

Returns a new rectangle that is moved to be completely inside the +argument Rect. If the rectangle is too large to fit inside, it is +centered inside the argument Rect, but its size is not changed.

+
+ +
+
+clamp_ip()
+
+
moves the rectangle inside another, in place
+
clamp_ip(Rect) -> None
+
+

Same as the Rect.clamp() method, but operates in place.

+
+ +
+
+clip()
+
+
crops a rectangle inside another
+
clip(Rect) -> Rect
+
+

Returns a new rectangle that is cropped to be completely inside the +argument Rect. If the two rectangles do not overlap to begin with, a Rect +with 0 size is returned.

+
+ +
+
+clipline()
+
+
crops a line inside a rectangle
+
clipline(x1, y1, x2, y2) -> ((cx1, cy1), (cx2, cy2))
+
clipline(x1, y1, x2, y2) -> ()
+
clipline((x1, y1), (x2, y2)) -> ((cx1, cy1), (cx2, cy2))
+
clipline((x1, y1), (x2, y2)) -> ()
+
clipline((x1, y1, x2, y2)) -> ((cx1, cy1), (cx2, cy2))
+
clipline((x1, y1, x2, y2)) -> ()
+
clipline(((x1, y1), (x2, y2))) -> ((cx1, cy1), (cx2, cy2))
+
clipline(((x1, y1), (x2, y2))) -> ()
+
+

Returns the coordinates of a line that is cropped to be completely inside +the rectangle. If the line does not overlap the rectangle, then an empty +tuple is returned.

+

The line to crop can be any of the following formats (floats can be used +in place of ints, but they will be truncated):

+
+
    +
  • four ints

  • +
  • 2 lists/tuples/Vector2s of 2 ints

  • +
  • a list/tuple of four ints

  • +
  • a list/tuple of 2 lists/tuples/Vector2s of 2 ints

  • +
+
+
+
Returns
+

a tuple with the coordinates of the given line cropped to be +completely inside the rectangle is returned, if the given line does +not overlap the rectangle, an empty tuple is returned

+
+
Return type
+

tuple(tuple(int, int), tuple(int, int)) or ()

+
+
Raises
+

TypeError -- if the line coordinates are not given as one of the +above described line formats

+
+
+
+

Note

+

This method can be used for collision detection between a rect and a +line. See example code below.

+
+
+

Note

+

The rect.bottom and rect.right attributes of a +pygame.Rectpygame object for storing rectangular coordinates always lie one pixel outside of its actual border.

+
+
# Example using clipline().
+clipped_line = rect.clipline(line)
+
+if clipped_line:
+    # If clipped_line is not an empty tuple then the line
+    # collides/overlaps with the rect. The returned value contains
+    # the endpoints of the clipped line.
+    start, end = clipped_line
+    x1, y1 = start
+    x2, y2 = end
+else:
+    print("No clipping. The line is fully outside the rect.")
+
+
+
+

Changed in pygame 2.5.0: Added support for keyword arguments.

+
+
+

New in pygame 2.0.0.

+
+
+ +
+
+union()
+
+
joins two rectangles into one
+
union(Rect) -> Rect
+
+

Returns a new rectangle that completely covers the area of the two +provided rectangles. There may be area inside the new Rect that is not +covered by the originals.

+
+ +
+
+union_ip()
+
+
joins two rectangles into one, in place
+
union_ip(Rect) -> None
+
+

Same as the Rect.union() method, but operates in place.

+
+ +
+
+unionall()
+
+
the union of many rectangles
+
unionall(Rect_sequence) -> Rect
+
+

Returns the union of one rectangle with a sequence of many rectangles.

+
+

Changed in pygame 2.5.0: Added support for keyword arguments.

+
+
+ +
+
+unionall_ip()
+
+
the union of many rectangles, in place
+
unionall_ip(Rect_sequence) -> None
+
+

The same as the Rect.unionall() method, but operates in place.

+
+

Changed in pygame 2.5.0: Added support for keyword arguments.

+
+
+ +
+
+fit()
+
+
resize and move a rectangle with aspect ratio
+
fit(Rect) -> Rect
+
+

Returns a new rectangle that is moved and resized to fit another. The +aspect ratio of the original Rect is preserved, so the new rectangle may +be smaller than the target in either width or height.

+
+ +
+
+normalize()
+
+
correct negative sizes
+
normalize() -> None
+
+

This will flip the width or height of a rectangle if it has a negative +size. The rectangle will remain in the same place, with only the sides +swapped.

+
+ +
+
+contains()
+
+
test if one rectangle is inside another
+
contains(Rect) -> bool
+
+

Returns true when the argument is completely inside the Rect.

+
+ +
+
+collidepoint()
+
+
test if a point is inside a rectangle
+
collidepoint(x, y) -> bool
+
collidepoint((x,y)) -> bool
+
+

Returns true if the given point is inside the rectangle. A point along +the right or bottom edge is not considered to be inside the rectangle.

+
+

Note

+

For collision detection between a rect and a line the clipline() +method can be used.

+
+
+ +
+
+colliderect()
+
+
test if two rectangles overlap
+
colliderect(Rect) -> bool
+
+

Returns true if any portion of either rectangle overlap (except the +top+bottom or left+right edges).

+
+

Note

+

For collision detection between a rect and a line the clipline() +method can be used.

+
+
+ +
+
+collidelist()
+
+
test if one rectangle in a list intersects
+
collidelist(list) -> index
+
+

Test whether the rectangle collides with any in a sequence of rectangles. +The index of the first collision found is returned. If no collisions are +found an index of -1 is returned.

+
+

Changed in pygame 2.5.0: Added support for keyword arguments.

+
+
+ +
+
+collidelistall()
+
+
test if all rectangles in a list intersect
+
collidelistall(list) -> indices
+
+

Returns a list of all the indices that contain rectangles that collide +with the Rect. If no intersecting rectangles are found, an empty list is +returned.

+

Not only Rects are valid arguments, but these are all valid calls:

+
Rect = pygame.Rect
+r = Rect(0, 0, 10, 10)
+
+list_of_rects = [Rect(1, 1, 1, 1), Rect(2, 2, 2, 2)]
+indices0 = r.collidelistall(list_of_rects)
+
+list_of_lists = [[1, 1, 1, 1], [2, 2, 2, 2]]
+indices1 = r.collidelistall(list_of_lists)
+
+list_of_tuples = [(1, 1, 1, 1), (2, 2, 2, 2)]
+indices2 = r.collidelistall(list_of_tuples)
+
+list_of_double_tuples = [((1, 1), (1, 1)), ((2, 2), (2, 2))]
+indices3 = r.collidelistall(list_of_double_tuples)
+
+class ObjectWithRectAttribute(object):
+    def __init__(self, r):
+        self.rect = r
+
+list_of_object_with_rect_attribute = [
+    ObjectWithRectAttribute(Rect(1, 1, 1, 1)),
+    ObjectWithRectAttribute(Rect(2, 2, 2, 2)),
+]
+indices4 = r.collidelistall(list_of_object_with_rect_attribute)
+
+class ObjectWithCallableRectAttribute(object):
+    def __init__(self, r):
+        self._rect = r
+
+    def rect(self):
+        return self._rect
+
+list_of_object_with_callable_rect = [
+    ObjectWithCallableRectAttribute(Rect(1, 1, 1, 1)),
+    ObjectWithCallableRectAttribute(Rect(2, 2, 2, 2)),
+]
+indices5 = r.collidelistall(list_of_object_with_callable_rect)
+
+
+
+

Changed in pygame 2.5.0: Added support for keyword arguments.

+
+
+ +
+
+collideobjects()
+
+
test if any object in a list intersects
+
collideobjects(rect_list) -> object
+
collideobjects(obj_list, key=func) -> object
+
+

Experimental: feature still in development available for testing and feedback. It may change. +Please leave collideobjects feedback with authors

+

Test whether the rectangle collides with any object in the sequence. +The object of the first collision found is returned. If no collisions are +found then None is returned

+

If key is given, then it should be a method taking an object from the list +as input and returning a rect like object e.g. lambda obj: obj.rectangle. +If an object has multiple attributes of type Rect then key could return one +of them.

+
r = Rect(1, 1, 10, 10)
+
+rects = [
+    Rect(1, 1, 10, 10),
+    Rect(5, 5, 10, 10),
+    Rect(15, 15, 1, 1),
+    Rect(2, 2, 1, 1),
+]
+
+result = r.collideobjects(rects)  # -> <rect(1, 1, 10, 10)>
+print(result)
+
+class ObjectWithSomRectAttribute:
+    def __init__(self, name, collision_box, draw_rect):
+        self.name = name
+        self.draw_rect = draw_rect
+        self.collision_box = collision_box
+
+    def __repr__(self):
+        return f'<{self.__class__.__name__}("{self.name}", {list(self.collision_box)}, {list(self.draw_rect)})>'
+
+objects = [
+    ObjectWithSomRectAttribute("A", Rect(15, 15, 1, 1), Rect(150, 150, 50, 50)),
+    ObjectWithSomRectAttribute("B", Rect(1, 1, 10, 10), Rect(300, 300, 50, 50)),
+    ObjectWithSomRectAttribute("C", Rect(5, 5, 10, 10), Rect(200, 500, 50, 50)),
+]
+
+# collision = r.collideobjects(objects) # this does not work because the items in the list are no Rect like object
+collision = r.collideobjects(
+    objects, key=lambda o: o.collision_box
+)  # -> <ObjectWithSomRectAttribute("B", [1, 1, 10, 10], [300, 300, 50, 50])>
+print(collision)
+
+screen_rect = r.collideobjects(objects, key=lambda o: o.draw_rect)  # -> None
+print(screen_rect)
+
+
+
+

New in pygame 2.1.3.

+
+
+ +
+
+collideobjectsall()
+
+
test if all objects in a list intersect
+
collideobjectsall(rect_list) -> objects
+
collideobjectsall(obj_list, key=func) -> objects
+
+

Experimental: feature still in development available for testing and feedback. It may change. +Please leave collideobjectsall feedback with authors

+

Returns a list of all the objects that contain rectangles that collide +with the Rect. If no intersecting objects are found, an empty list is +returned.

+

If key is given, then it should be a method taking an object from the list +as input and returning a rect like object e.g. lambda obj: obj.rectangle. +If an object has multiple attributes of type Rect then key could return one +of them.

+
r = Rect(1, 1, 10, 10)
+
+rects = [
+    Rect(1, 1, 10, 10),
+    Rect(5, 5, 10, 10),
+    Rect(15, 15, 1, 1),
+    Rect(2, 2, 1, 1),
+]
+
+result = r.collideobjectsall(
+    rects
+)  # -> [<rect(1, 1, 10, 10)>, <rect(5, 5, 10, 10)>, <rect(2, 2, 1, 1)>]
+print(result)
+
+class ObjectWithSomRectAttribute:
+    def __init__(self, name, collision_box, draw_rect):
+        self.name = name
+        self.draw_rect = draw_rect
+        self.collision_box = collision_box
+
+    def __repr__(self):
+        return f'<{self.__class__.__name__}("{self.name}", {list(self.collision_box)}, {list(self.draw_rect)})>'
+
+objects = [
+    ObjectWithSomRectAttribute("A", Rect(1, 1, 10, 10), Rect(300, 300, 50, 50)),
+    ObjectWithSomRectAttribute("B", Rect(5, 5, 10, 10), Rect(200, 500, 50, 50)),
+    ObjectWithSomRectAttribute("C", Rect(15, 15, 1, 1), Rect(150, 150, 50, 50)),
+]
+
+# collisions = r.collideobjectsall(objects) # this does not work because ObjectWithSomRectAttribute is not a Rect like object
+collisions = r.collideobjectsall(
+    objects, key=lambda o: o.collision_box
+)  # -> [<ObjectWithSomRectAttribute("A", [1, 1, 10, 10], [300, 300, 50, 50])>, <ObjectWithSomRectAttribute("B", [5, 5, 10, 10], [200, 500, 50, 50])>]
+print(collisions)
+
+screen_rects = r.collideobjectsall(objects, key=lambda o: o.draw_rect)  # -> []
+print(screen_rects)
+
+
+
+

New in pygame 2.1.3.

+
+
+ +
+
+collidedict()
+
+
test if one rectangle in a dictionary intersects
+
collidedict(dict) -> (key, value)
+
collidedict(dict) -> None
+
collidedict(dict, use_values=0) -> (key, value)
+
collidedict(dict, use_values=0) -> None
+
+

Returns the first key and value pair that intersects with the calling +Rect object. If no collisions are found, None is returned. If +use_values is 0 (default) then the dict's keys will be used in the +collision detection, otherwise the dict's values will be used.

+
+

Note

+

Rect objects cannot be used as keys in a dictionary (they are not +hashable), so they must be converted to a tuple. +e.g. rect.collidedict({tuple(key_rect) : value})

+
+
+

Changed in pygame 2.5.0: Added support for keyword arguments.

+
+
+ +
+
+collidedictall()
+
+
test if all rectangles in a dictionary intersect
+
collidedictall(dict) -> [(key, value), ...]
+
collidedictall(dict, use_values=0) -> [(key, value), ...]
+
+

Returns a list of all the key and value pairs that intersect with the +calling Rect object. If no collisions are found an empty list is returned. +If use_values is 0 (default) then the dict's keys will be used in the +collision detection, otherwise the dict's values will be used.

+
+

Note

+

Rect objects cannot be used as keys in a dictionary (they are not +hashable), so they must be converted to a tuple. +e.g. rect.collidedictall({tuple(key_rect) : value})

+
+
+

Changed in pygame 2.5.0: Added support for keyword arguments.

+
+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/scrap.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/scrap.html new file mode 100644 index 00000000..9f9dc27c --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/scrap.html @@ -0,0 +1,456 @@ + + + + + + + + + pygame.scrap — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.scrap
+
+
pygame module for clipboard support.
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Initializes the scrap module.
+Returns True if the scrap module is currently initialized.
+Gets the data for the specified type from the clipboard.
+Gets a list of the available clipboard types.
+Places data into the clipboard.
+Checks whether data for a given type is available in the clipboard.
+Indicates if the clipboard ownership has been lost by the pygame application.
+Sets the clipboard access mode.
+

EXPERIMENTAL!: This API may change or disappear in later pygame releases. If +you use this, your code may break with the next pygame release.

+

The scrap module is for transferring data to/from the clipboard. This allows +for cutting and pasting data between pygame and other applications. Some basic +data (MIME) types are defined and registered:

+
 pygame         string
+constant        value        description
+--------------------------------------------------
+SCRAP_TEXT   "text/plain"    plain text
+SCRAP_BMP    "image/bmp"     BMP encoded image data
+SCRAP_PBM    "image/pbm"     PBM encoded image data
+SCRAP_PPM    "image/ppm"     PPM encoded image data
+
+
+

pygame.SCRAP_PPM, pygame.SCRAP_PBM and pygame.SCRAP_BMP are +suitable for surface buffers to be shared with other applications. +pygame.SCRAP_TEXT is an alias for the plain text clipboard type.

+

Depending on the platform, additional types are automatically registered when +data is placed into the clipboard to guarantee a consistent sharing behaviour +with other applications. The following listed types can be used as strings to +be passed to the respective pygame.scrappygame module for clipboard support. module functions.

+

For Windows platforms, these additional types are supported automatically +and resolve to their internal definitions:

+
"text/plain;charset=utf-8"   UTF-8 encoded text
+"audio/wav"                  WAV encoded audio
+"image/tiff"                 TIFF encoded image data
+
+
+

For X11 platforms, these additional types are supported automatically and +resolve to their internal definitions:

+
"text/plain;charset=utf-8"   UTF-8 encoded text
+"UTF8_STRING"                UTF-8 encoded text
+"COMPOUND_TEXT"              COMPOUND text
+
+
+

User defined types can be used, but the data might not be accessible by other +applications unless they know what data type to look for. +Example: Data placed into the clipboard by +pygame.scrap.put("my_data_type", byte_data) can only be accessed by +applications which query the clipboard for the "my_data_type" data type.

+

For an example of how the scrap module works refer to the examples page +(pygame.examples.scrap_clipboard.main()access the clipboard) or the code directly in GitHub +(pygame/examples/scrap_clipboard.py).

+
+

New in pygame 1.8.

+
+
+

Note

+

The scrap module is currently only supported for Windows, X11 and Mac OS X. +On Mac OS X only text works at the moment - other types may be supported in +future releases.

+
+
+
+pygame.scrap.init()
+
+
Initializes the scrap module.
+
init() -> None
+
+

Initialize the scrap module.

+
+
Raises
+

pygame.errorstandard pygame exception -- if unable to initialize scrap module

+
+
+
+

Note

+

The scrap module requires pygame.display.set_mode()Initialize a window or screen for display be +called before being initialized.

+
+
+ +
+
+pygame.scrap.get_init()
+
+
Returns True if the scrap module is currently initialized.
+
get_init() -> bool
+
+

Gets the scrap module's initialization state.

+
+
Returns
+

True if the pygame.scrappygame module for clipboard support. module is currently +initialized, False otherwise

+
+
Return type
+

bool

+
+
+
+

New in pygame 1.9.5.

+
+
+ +
+
+pygame.scrap.get()
+
+
Gets the data for the specified type from the clipboard.
+
get(type) -> bytes | None
+
+

Retrieves the data for the specified type from the clipboard. The data is +returned as a byte string and might need further processing (such as +decoding to Unicode).

+
+
Parameters
+

type (string) -- data type to retrieve from the clipboard

+
+
Returns
+

data (bytes object) for the given type identifier or None if +no data for the given type is available

+
+
Return type
+

bytes | None

+
+
+
text = pygame.scrap.get(pygame.SCRAP_TEXT)
+if text:
+    print("There is text in the clipboard.")
+else:
+    print("There does not seem to be text in the clipboard.")
+
+
+
+ +
+
+pygame.scrap.get_types()
+
+
Gets a list of the available clipboard types.
+
get_types() -> list
+
+

Gets a list of data type string identifiers for the data currently +available on the clipboard. Each identifier can be used in the +pygame.scrap.get()Gets the data for the specified type from the clipboard. method to get the clipboard content of the +specific type.

+
+
Returns
+

list of strings of the available clipboard data types, if there +is no data in the clipboard an empty list is returned

+
+
Return type
+

list

+
+
+
for t in pygame.scrap.get_types():
+    if "text" in t:
+        # There is some content with the word "text" in its type string.
+        print(pygame.scrap.get(t))
+
+
+
+ +
+
+pygame.scrap.put()
+
+
Places data into the clipboard.
+
put(type, data) -> None
+
+

Places data for a given clipboard type into the clipboard. The data must +be a string buffer. The type is a string identifying the type of data to be +placed into the clipboard. This can be one of the predefined +pygame.SCRAP_PBM, pygame.SCRAP_PPM, pygame.SCRAP_BMP or +pygame.SCRAP_TEXT values or a user defined string identifier.

+
+
Parameters
+
    +
  • type (string) -- type identifier of the data to be placed into the +clipboard

  • +
  • data (bytes) -- data to be place into the clipboard, a bytes object

  • +
+
+
Raises
+

pygame.errorstandard pygame exception -- if unable to put the data into the clipboard

+
+
+
with open("example.bmp", "rb") as fp:
+    pygame.scrap.put(pygame.SCRAP_BMP, fp.read())
+# The image data is now on the clipboard for other applications to access
+# it.
+pygame.scrap.put(pygame.SCRAP_TEXT, b"A text to copy")
+pygame.scrap.put("Plain text", b"Data for user defined type 'Plain text'")
+
+
+
+ +
+
+pygame.scrap.contains()
+
+
Checks whether data for a given type is available in the clipboard.
+
contains(type) -> bool
+
+

Checks whether data for the given type is currently available in the +clipboard.

+
+
Parameters
+

type (string) -- data type to check availability of

+
+
Returns
+

True if data for the passed type is available in the +clipboard, False otherwise

+
+
Return type
+

bool

+
+
+
if pygame.scrap.contains(pygame.SCRAP_TEXT):
+    print("There is text in the clipboard.")
+if pygame.scrap.contains("own_data_type"):
+    print("There is stuff in the clipboard.")
+
+
+
+ +
+
+pygame.scrap.lost()
+
+
Indicates if the clipboard ownership has been lost by the pygame application.
+
lost() -> bool
+
+

Indicates if the clipboard ownership has been lost by the pygame +application.

+
+
Returns
+

True, if the clipboard ownership has been lost by the pygame +application, False if the pygame application still owns the clipboard

+
+
Return type
+

bool

+
+
+
if pygame.scrap.lost():
+    print("The clipboard is in use by another application.")
+
+
+
+ +
+
+pygame.scrap.set_mode()
+
+
Sets the clipboard access mode.
+
set_mode(mode) -> None
+
+

Sets the access mode for the clipboard. This is only of interest for X11 +environments where clipboard modes pygame.SCRAP_SELECTION (for mouse +selections) and pygame.SCRAP_CLIPBOARD (for the clipboard) are +available. Setting the mode to pygame.SCRAP_SELECTION in other +environments will not change the mode from pygame.SCRAP_CLIPBOARD.

+
+
Parameters
+

mode -- access mode, supported values are pygame.SCRAP_CLIPBOARD +and pygame.SCRAP_SELECTION (pygame.SCRAP_SELECTION only has an +effect when used on X11 platforms)

+
+
Raises
+

ValueError -- if the mode parameter is not +pygame.SCRAP_CLIPBOARD or pygame.SCRAP_SELECTION

+
+
+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/sdl2_controller.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/sdl2_controller.html new file mode 100644 index 00000000..017b9236 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/sdl2_controller.html @@ -0,0 +1,571 @@ + + + + + + + + + pygame._sdl2.controller — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame._sdl2.controller
+
+
Pygame module to work with controllers.
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+initialize the controller module
+Uninitialize the controller module.
+Returns True if the controller module is initialized.
+Sets the current state of events related to controllers
+Gets the current state of events related to controllers
+Get the number of joysticks connected
+Check if the given joystick is supported by the game controller interface
+Get the name of the controller
+Create a new Controller object.
+
+

Note

+

Use import pygame._sdl2.controller before using this module.

+
+

This module offers control over common controller types like the dualshock 4 or +the xbox 360 controllers: They have two analog sticks, two triggers, two shoulder buttons, +a dpad, 4 buttons on the side, 2 (or 3) buttons in the middle.

+

Pygame uses xbox controllers naming conventions (like a, b, x, y for buttons) but +they always refer to the same buttons. For example CONTROLLER_BUTTON_X is +always the leftmost button of the 4 buttons on the right.

+

Controllers can generate the following events:

+
CONTROLLERAXISMOTION, CONTROLLERBUTTONDOWN, CONTROLLERBUTTONUP,
+CONTROLLERDEVICEREMAPPED, CONTROLLERDEVICEADDED, CONTROLLERDEVICEREMOVED
+
+
+

Additionally if pygame is built with SDL 2.0.14 or higher the following events can also be generated +(to get the version of sdl pygame is built with use pygame.version.SDL()tupled integers of the SDL library version):

+
CONTROLLERTOUCHPADDOWN, CONTROLLERTOUCHPADMOTION, CONTROLLERTOUCHPADUP
+
+
+

These events can be enabled/disabled by pygame._sdl2.controller.set_eventstate()Sets the current state of events related to controllers +Note that controllers can generate joystick events as well. This function only toggles +events related to controllers.

+
+

Note

+

See the pygame.joystickPygame module for interacting with joysticks, gamepads, and trackballs. for a more versatile but more advanced api.

+
+
+

New in pygame 2: This module requires SDL2.

+
+
+
+pygame._sdl2.controller.init()
+
+
initialize the controller module
+
init() -> None
+
+

Initialize the controller module.

+
+ +
+
+pygame._sdl2.controller.quit()
+
+
Uninitialize the controller module.
+
quit() -> None
+
+

Uninitialize the controller module.

+
+ +
+
+pygame._sdl2.controller.get_init()
+
+
Returns True if the controller module is initialized.
+
get_init() -> bool
+
+

Test if pygame._sdl2.controller.init() was called.

+
+
+
+ +
+
+pygame._sdl2.controller.set_eventstate()
+
+
Sets the current state of events related to controllers
+
set_eventstate(state) -> None
+
+

Enable or disable events connected to controllers.

+
+

Note

+

Controllers can still generate joystick events, which will not be toggled by this function.

+
+
+

Changed in pygame 2.0.2:: Changed return type from int to None

+
+
+ +
+
+pygame._sdl2.controller.get_eventstate()
+
+
Gets the current state of events related to controllers
+
get_eventstate() -> bool
+
+

Returns the current state of events related to controllers, True meaning +events will be posted.

+
+

New in pygame 2.0.2.

+
+
+ +
+
+pygame._sdl2.controller.get_count()
+
+
Get the number of joysticks connected
+
get_count() -> int
+
+

Get the number of joysticks connected.

+
+ +
+
+pygame._sdl2.controller.is_controller()
+
+
Check if the given joystick is supported by the game controller interface
+
is_controller(index) -> bool
+
+

Returns True if the index given can be used to create a controller object.

+
+ +
+
+pygame._sdl2.controller.name_forindex()
+
+
Get the name of the controller
+
name_forindex(index) -> name or None
+
+

Returns the name of controller, or None if there's no name or the +index is invalid.

+
+ +
+
+pygame._sdl2.controller.Controller
+
+
+
Create a new Controller object.
+
Controller(index) -> Controller
+
+

Create a new Controller object. Index should be integer between +0 and pygame._sdl2.controller.get_count(). Controllers also +can be created from a pygame.joystick.Joystick using +pygame._sdl2.controller.from_joystick. Controllers are +initialized on creation.

+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+uninitialize the Controller
+check if the Controller is initialized
+Create a Controller from a pygame.joystick.Joystick object
+Check if the Controller has been opened and is currently connected.
+Returns a pygame.joystick.Joystick() object
+Get the current state of a joystick axis
+Get the current state of a button
+Get the mapping assigned to the controller
+Assign a mapping to the controller
+Start a rumbling effect
+Stop any rumble effect playing
+
+
+quit()
+
+
uninitialize the Controller
+
quit() -> None
+
+

Close a Controller object. After this the pygame event queue will no longer +receive events from the device.

+

It is safe to call this more than once.

+
+ +
+
+get_init()
+
+
check if the Controller is initialized
+
get_init() -> bool
+
+

Returns True if the Controller object is currently initialised.

+
+ +
+
+static from_joystick()
+
+
Create a Controller from a pygame.joystick.Joystick object
+
from_joystick(joystick) -> Controller
+
+

Create a Controller object from a pygame.joystick.Joystick object

+
+ +
+
+attached()
+
+
Check if the Controller has been opened and is currently connected.
+
attached() -> bool
+
+

Returns True if the Controller object is opened and connected.

+
+ +
+
+as_joystick()
+
+
Returns a pygame.joystick.Joystick() object
+
as_joystick() -> Joystick object
+
+

Returns a pygame.joystick.Joystick() object created from this controller's index

+
+ +
+
+get_axis()
+
+
Get the current state of a joystick axis
+
get_axis(axis) -> int
+
+

Get the current state of a trigger or joystick axis. +The axis argument must be one of the following constants:

+
CONTROLLER_AXIS_LEFTX, CONTROLLER_AXIS_LEFTY,
+CONTROLLER_AXIS_RIGHTX, CONTROLLER_AXIS_RIGHTY,
+CONTROLLER_AXIS_TRIGGERLEFT, CONTROLLER_AXIS_TRIGGERRIGHT
+
+
+

Joysticks can return a value between -32768 and 32767. Triggers however +can only return a value between 0 and 32768.

+
+ +
+
+get_button()
+
+
Get the current state of a button
+
get_button(button) -> bool
+
+

Get the current state of a button, True meaning it is pressed down. +The button argument must be one of the following constants:

+
CONTROLLER_BUTTON_A, CONTROLLER_BUTTON_B,
+CONTROLLER_BUTTON_X, CONTROLLER_BUTTON_Y
+CONTROLLER_BUTTON_DPAD_UP, CONTROLLER_BUTTON_DPAD_DOWN,
+CONTROLLER_BUTTON_DPAD_LEFT, CONTROLLER_BUTTON_DPAD_RIGHT,
+CONTROLLER_BUTTON_LEFTSHOULDER, CONTROLLER_BUTTON_RIGHTSHOULDER,
+CONTROLLER_BUTTON_LEFTSTICK, CONTROLLER_BUTTON_RIGHTSTICK,
+CONTROLLER_BUTTON_BACK, CONTROLLER_BUTTON_GUIDE,
+CONTROLLER_BUTTON_START
+
+
+
+ +
+
+get_mapping()
+
+
Get the mapping assigned to the controller
+
get_mapping() -> mapping
+
+

Returns a dict containing the mapping of the Controller. For more +information see Controller.set_mapping()

+
+

Changed in pygame 2.0.2:: Return type changed from str to dict

+
+
+ +
+
+set_mapping()
+
+
Assign a mapping to the controller
+
set_mapping(mapping) -> int
+
+

Rebind buttons, axes, triggers and dpads. The mapping should be a +dict containing all buttons, hats and axes. The easiest way to get this +is to use the dict returned by Controller.get_mapping(). To edit +this mapping assign a value to the original button. The value of the +dictionary must be a button, hat or axis represented in the following way:

+
    +
  • For a button use: bX where X is the index of the button.

  • +
  • For a hat use: hX.Y where X is the index and the Y is the direction (up: 1, right: 2, down: 3, left: 4).

  • +
  • For an axis use: aX where x is the index of the axis.

  • +
+

An example of mapping:

+
mapping = controller.get_mapping() # Get current mapping
+mapping["a"] = "b3" # Remap button a to y
+mapping["y"] = "b0" # Remap button y to a
+controller.set_mapping(mapping) # Set the mapping
+
+
+

The function will return 1 if a new mapping is added or 0 if an existing one is updated.

+
+

Changed in pygame 2.0.2:: Renamed from add_mapping to set_mapping

+
+
+

Changed in pygame 2.0.2:: Argument type changed from str to dict

+
+
+ +
+
+rumble()
+
+
Start a rumbling effect
+
rumble(low_frequency, high_frequency, duration) -> bool
+
+

Start a rumble effect on the controller, with the specified strength ranging +from 0 to 1. Duration is length of the effect, in ms. Setting the duration +to 0 will play the effect until another one overwrites it or +Controller.stop_rumble() is called. If an effect is already +playing, then it will be overwritten.

+

Returns True if the rumble was played successfully or False if the +controller does not support it or pygame.version.SDL()tupled integers of the SDL library version is below 2.0.9.

+
+

New in pygame 2.0.2.

+
+
+ +
+
+stop_rumble()
+
+
Stop any rumble effect playing
+
stop_rumble() -> None
+
+

Stops any rumble effect playing on the controller. See +Controller.rumble() for more information.

+
+

New in pygame 2.0.2.

+
+
+ +
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/sdl2_video.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/sdl2_video.html new file mode 100644 index 00000000..22862be8 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/sdl2_video.html @@ -0,0 +1,1089 @@ + + + + + + + + + pygame.sdl2_video — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.sdl2_video
+
+

Warning

+

This module isn't ready for prime time yet, it's still in development. +These docs are primarily meant to help the pygame developers and super-early adopters +who are in communication with the developers. This API will change.

+
+ +++++ + + + + + + + + + + + + + + + + + + +
+pygame object that represents a window
+pygame object that representing a Texture.
+Easy way to use a portion of a Texture without worrying about srcrect all the time.
+Create a 2D rendering context for a window.
+
+
Experimental pygame module for porting new SDL video systems
+
+
+
+pygame._sdl2.video.Window
+
+
pygame object that represents a window
+
Window(title="pygame", size=(640, 480), position=None, fullscreen=False, fullscreen_desktop=False, keywords) -> Window
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Creates window using window created by pygame.display.set_mode().
+Create Window from another window. Could be from another UI toolkit.
+Gets or sets whether the mouse is confined to the window.
+Gets or sets the window's relative mouse motion state.
+Enable windowed mode (exit fullscreen).
+Enter fullscreen.
+Gets or sets whether the window title.
+Destroys the window.
+Hide the window.
+Show the window.
+Raise the window above other windows and set the input focus. The "input_only" argument is only supported on X11.
+Restore the size and position of a minimized or maximized window.
+Maximize the window.
+Minimize the window.
+Gets and sets whether the window is resizable.
+Add or remove the border from the window.
+Set the icon for the window.
+Get the unique window ID. *Read-only*
+Gets and sets the window size.
+Gets and sets the window position.
+Gets and sets the window opacity. Between 0.0 (fully transparent) and 1.0 (fully opaque).
+Get the index of the display that owns the window. *Read-only*
+Set the window as a modal for a parent window. This function is only supported on X11.
+
+
+classmethod from_display_module()
+
+
Creates window using window created by pygame.display.set_mode().
+
from_display_module() -> Window
+
+
+ +
+
+classmethod from_window()
+
+
Create Window from another window. Could be from another UI toolkit.
+
from_window(other) -> Window
+
+
+ +
+
+grab
+
+
Gets or sets whether the mouse is confined to the window.
+
grab -> bool
+
+
+ +
+
+relative_mouse
+
+
Gets or sets the window's relative mouse motion state.
+
relative_mouse -> bool
+
+
+ +
+
+set_windowed()
+
+
Enable windowed mode (exit fullscreen).
+
set_windowed() -> None
+
+
+ +
+
+set_fullscreen()
+
+
Enter fullscreen.
+
set_fullscreen(desktop=False) -> None
+
+
+ +
+
+title
+
+
Gets or sets whether the window title.
+
title -> string
+
+
+ +
+
+destroy()
+
+
Destroys the window.
+
destroy() -> None
+
+
+ +
+
+hide()
+
+
Hide the window.
+
hide() -> None
+
+
+ +
+
+show()
+
+
Show the window.
+
show() -> None
+
+
+ +
+
+focus()
+
+
Raise the window above other windows and set the input focus. The "input_only" argument is only supported on X11.
+
focus(input_only=False) -> None
+
+
+ +
+
+restore()
+
+
Restore the size and position of a minimized or maximized window.
+
restore() -> None
+
+
+ +
+
+maximize()
+
+
Maximize the window.
+
maximize() -> None
+
+
+ +
+
+minimize()
+
+
Minimize the window.
+
maximize() -> None
+
+
+ +
+
+resizable
+
+
Gets and sets whether the window is resizable.
+
resizable -> bool
+
+
+ +
+
+borderless
+
+
Add or remove the border from the window.
+
borderless -> bool
+
+
+ +
+
+set_icon()
+
+
Set the icon for the window.
+
set_icon(surface) -> None
+
+
+ +
+
+id
+
+
Get the unique window ID. *Read-only*
+
id -> int
+
+
+ +
+
+size
+
+
Gets and sets the window size.
+
size -> (int, int)
+
+
+ +
+
+position
+
+
Gets and sets the window position.
+
position -> (int, int) or WINDOWPOS_CENTERED or WINDOWPOS_UNDEFINED
+
+
+ +
+
+opacity
+
+
Gets and sets the window opacity. Between 0.0 (fully transparent) and 1.0 (fully opaque).
+
opacity -> float
+
+
+ +
+
+display_index
+
+
Get the index of the display that owns the window. *Read-only*
+
display_index -> int
+
+
+ +
+
+set_modal_for()
+
+
Set the window as a modal for a parent window. This function is only supported on X11.
+
set_modal_for(Window) -> None
+
+
+ +
+ +
+
+pygame._sdl2.video.Texture
+
+
pygame object that representing a Texture.
+
Texture(renderer, size, depth=0, static=False, streaming=False, target=False) -> Texture
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Create a texture from an existing surface.
+Gets the renderer associated with the Texture. *Read-only*
+Gets the width of the Texture. *Read-only*
+Gets the height of the Texture. *Read-only*
+Gets and sets an additional alpha value multiplied into render copy operations.
+Gets and sets the blend mode for the Texture.
+Gets and sets an additional color value multiplied into render copy operations.
+Get the rectangular area of the texture.
+Copy a portion of the texture to the rendering target.
+Update the texture with a Surface. WARNING: Slow operation, use sparingly.
+
+
+static from_surface()
+
+
Create a texture from an existing surface.
+
from_surface(renderer, surface) -> Texture
+
+
+ +
+
+renderer
+
+
Gets the renderer associated with the Texture. *Read-only*
+
renderer -> Renderer
+
+
+ +
+
+width
+
+
Gets the width of the Texture. *Read-only*
+
width -> int
+
+
+ +
+
+height
+
+
Gets the height of the Texture. *Read-only*
+
height -> int
+
+
+ +
+
+alpha
+
+
Gets and sets an additional alpha value multiplied into render copy operations.
+
alpha -> int
+
+
+ +
+
+blend_mode
+
+
Gets and sets the blend mode for the Texture.
+
blend_mode -> int
+
+
+ +
+
+color
+
+
Gets and sets an additional color value multiplied into render copy operations.
+
color -> color
+
+
+ +
+
+get_rect()
+
+
Get the rectangular area of the texture.
+
get_rect(**kwargs) -> Rect
+
+
+ +
+
+draw()
+
+
Copy a portion of the texture to the rendering target.
+
draw(srcrect=None, dstrect=None, angle=0, origin=None, flip_x=False, flip_y=False) -> None
+
+
+ +
+
+update()
+
+
Update the texture with a Surface. WARNING: Slow operation, use sparingly.
+
update(surface, area=None) -> None
+
+
+ +
+ +
+
+pygame._sdl2.video.Image
+
+
Easy way to use a portion of a Texture without worrying about srcrect all the time.
+
Image(textureOrImage, srcrect=None) -> Image
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Get the rectangular area of the Image.
+Copy a portion of the Image to the rendering target.
+Gets and sets the angle the Image draws itself with.
+Gets and sets the origin. Origin=None means the Image will be rotated around its center.
+Gets and sets whether the Image is flipped on the x axis.
+Gets and sets whether the Image is flipped on the y axis.
+Gets and sets the Image color modifier.
+Gets and sets the Image alpha modifier.
+Gets and sets the blend mode for the Image.
+Gets and sets the Texture the Image is based on.
+Gets and sets the Rect the Image is based on.
+
+
+get_rect()
+
+
Get the rectangular area of the Image.
+
get_rect() -> Rect
+
+
+ +
+
+draw()
+
+
Copy a portion of the Image to the rendering target.
+
draw(srcrect=None, dstrect=None) -> None
+
+
+ +
+
+angle
+
+
Gets and sets the angle the Image draws itself with.
+
angle -> float
+
+
+ +
+
+origin
+
+
Gets and sets the origin. Origin=None means the Image will be rotated around its center.
+
origin -> (float, float) or None.
+
+
+ +
+
+flip_x
+
+
Gets and sets whether the Image is flipped on the x axis.
+
flip_x -> bool
+
+
+ +
+
+flip_y
+
+
Gets and sets whether the Image is flipped on the y axis.
+
flip_y -> bool
+
+
+ +
+
+color
+
+
Gets and sets the Image color modifier.
+
color -> Color
+
+
+ +
+
+alpha
+
+
Gets and sets the Image alpha modifier.
+
alpha -> float
+
+
+ +
+
+blend_mode
+
+
Gets and sets the blend mode for the Image.
+
blend_mode -> int
+
+
+ +
+
+texture
+
+
Gets and sets the Texture the Image is based on.
+
texture -> Texture
+
+
+ +
+
+srcrect
+
+
Gets and sets the Rect the Image is based on.
+
srcrect -> Rect
+
+
+ +
+ +
+
+pygame._sdl2.video.Renderer
+
+
Create a 2D rendering context for a window.
+
Renderer(window, index=-1, accelerated=-1, vsync=False, target_texture=False) -> Renderer
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Easy way to create a Renderer.
+Gets and sets the blend mode used by the drawing functions.
+Gets and sets the color used by the drawing functions.
+Clear the current rendering target with the drawing color.
+Updates the screen with any new rendering since previous call.
+Returns the drawing area on the target.
+Set the drawing area on the target. If area is None, the entire target will be used.
+Gets and sets the logical size.
+Gets and sets the scale.
+Gets and sets the render target. None represents the default target (the renderer).
+For compatibility purposes. Textures created by different Renderers cannot be shared!
+Draws a line.
+Draws a point.
+Draws a rectangle.
+Fills a rectangle.
+Read pixels from current render target and create a pygame.Surface. WARNING: Slow operation, use sparingly.
+
+
+classmethod from_window()
+
+
Easy way to create a Renderer.
+
from_window(window) -> Renderer
+
+
+ +
+
+draw_blend_mode
+
+
Gets and sets the blend mode used by the drawing functions.
+
draw_blend_mode -> int
+
+
+ +
+
+draw_color
+
+
Gets and sets the color used by the drawing functions.
+
draw_color -> Color
+
+
+ +
+
+clear()
+
+
Clear the current rendering target with the drawing color.
+
clear() -> None
+
+
+ +
+
+present()
+
+
Updates the screen with any new rendering since previous call.
+
present() -> None
+
+
+ +
+
+get_viewport()
+
+
Returns the drawing area on the target.
+
get_viewport() -> Rect
+
+
+ +
+
+set_viewport()
+
+
Set the drawing area on the target. If area is None, the entire target will be used.
+
set_viewport(area) -> None
+
+
+ +
+
+logical_size
+
+
Gets and sets the logical size.
+
logical_size -> (int width, int height)
+
+
+ +
+
+scale
+
+
Gets and sets the scale.
+
scale -> (float x_scale, float y_scale)
+
+
+ +
+
+target
+
+
Gets and sets the render target. None represents the default target (the renderer).
+
target -> Texture or None
+
+
+ +
+
+blit()
+
+
For compatibility purposes. Textures created by different Renderers cannot be shared!
+
blit(source, dest, area=None, special_flags=0)-> Rect
+
+
+ +
+
+draw_line()
+
+
Draws a line.
+
draw_line(p1, p2) -> None
+
+
+ +
+
+draw_point()
+
+
Draws a point.
+
draw_point(point) -> None
+
+
+ +
+
+draw_rect()
+
+
Draws a rectangle.
+
draw_rect(rect)-> None
+
+
+ +
+
+fill_rect()
+
+
Fills a rectangle.
+
fill_rect(rect)-> None
+
+
+ +
+
+to_surface()
+
+
Read pixels from current render target and create a pygame.Surface. WARNING: Slow operation, use sparingly.
+
to_surface(surface=None, area=None)-> Surface
+
+
+ +
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/sndarray.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/sndarray.html new file mode 100644 index 00000000..d1ff4977 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/sndarray.html @@ -0,0 +1,272 @@ + + + + + + + + + pygame.sndarray — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.sndarray
+
+
pygame module for accessing sound sample data
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+copy Sound samples into an array
+reference Sound samples into an array
+convert an array into a Sound object
+Sets the array system to be used for sound arrays
+Gets the currently active array type.
+Gets the array system types currently supported.
+

Functions to convert between NumPy arrays and Sound objects. This +module will only be functional when pygame can use the external NumPy +package. If NumPy can't be imported, surfarray becomes a MissingModule +object.

+

Sound data is made of thousands of samples per second, and each sample is the +amplitude of the wave at a particular moment in time. For example, in 22-kHz +format, element number 5 of the array is the amplitude of the wave after +5/22000 seconds.

+

The arrays are indexed by the X axis first, followed by the Y axis. +Each sample is an 8-bit or 16-bit integer, depending on the data format. A +stereo sound file has two values per sample, while a mono sound file only has +one.

+
+
+pygame.sndarray.array()
+
+
copy Sound samples into an array
+
array(Sound) -> array
+
+

Creates a new array for the sound data and copies the samples. The array +will always be in the format returned from pygame.mixer.get_init().

+
+ +
+
+pygame.sndarray.samples()
+
+
reference Sound samples into an array
+
samples(Sound) -> array
+
+

Creates a new array that directly references the samples in a Sound object. +Modifying the array will change the Sound. The array will always be in the +format returned from pygame.mixer.get_init().

+
+ +
+
+pygame.sndarray.make_sound()
+
+
convert an array into a Sound object
+
make_sound(array) -> Sound
+
+

Create a new playable Sound object from an array. The mixer module must be +initialized and the array format must be similar to the mixer audio format.

+
+ +
+
+pygame.sndarray.use_arraytype()
+
+
Sets the array system to be used for sound arrays
+
use_arraytype (arraytype) -> None
+
+

DEPRECATED: Uses the requested array type for the module functions. The +only supported arraytype is 'numpy'. Other values will raise ValueError. +Using this function will raise a DeprecationWarning. +.. ## pygame.sndarray.use_arraytype ##

+
+ +
+
+pygame.sndarray.get_arraytype()
+
+
Gets the currently active array type.
+
get_arraytype () -> str
+
+

DEPRECATED: Returns the currently active array type. This will be a value of the +get_arraytypes() tuple and indicates which type of array module is used +for the array creation. Using this function will raise a DeprecationWarning.

+
+

New in pygame 1.8.

+
+
+ +
+
+pygame.sndarray.get_arraytypes()
+
+
Gets the array system types currently supported.
+
get_arraytypes () -> tuple
+
+

DEPRECATED: Checks, which array systems are available and returns them as a tuple of +strings. The values of the tuple can be used directly in the +pygame.sndarray.use_arraytype()Sets the array system to be used for sound arrays () method. If no supported array +system could be found, None will be returned. Using this function will raise a +DeprecationWarning.

+
+

New in pygame 1.8.

+
+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/sprite.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/sprite.html new file mode 100644 index 00000000..1f0332bb --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/sprite.html @@ -0,0 +1,1413 @@ + + + + + + + + + pygame.sprite — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.sprite
+
+
pygame module with basic game object classes
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Simple base class for visible game objects.
+A subclass of Sprite that references its Groups weakly. This means that any group this belongs to that is not referenced anywhere else is garbage collected automatically.
+A subclass of Sprite with more attributes and features.
+A container class to hold and manage multiple Sprite objects.
+A subclass of WeakSprite and DirtySprite that combines the benefits of both classes.
+Same as pygame.sprite.Group
+Same as pygame.sprite.Group
+Group sub-class that tracks dirty updates.
+RenderUpdates sub-class that draws Sprites in order of addition.
+LayeredUpdates is a sprite group that handles layers and draws like OrderedUpdates.
+LayeredDirty group is for DirtySprite objects. Subclasses LayeredUpdates.
+Group container that holds a single sprite.
+Find sprites in a group that intersect another sprite.
+Collision detection between two sprites, using rects.
+Collision detection between two sprites, using rects scaled to a ratio.
+Collision detection between two sprites, using circles.
+Collision detection between two sprites, using circles scaled to a ratio.
+Collision detection between two sprites, using masks.
+Find all sprites that collide between two groups.
+Simple test if a sprite intersects anything in a group.
+

This module contains several simple classes to be used within games. There is +the main Sprite class and several Group classes that contain Sprites. The use +of these classes is entirely optional when using pygame. The classes are fairly +lightweight and only provide a starting place for the code that is common to +most games.

+

The Sprite class is intended to be used as a base class for the different types +of objects in the game. There is also a base Group class that simply stores +sprites. A game could create new types of Group classes that operate on +specially customized Sprite instances they contain.

+

The basic Group class can draw the Sprites it contains to a Surface. The +Group.draw() method requires that each Sprite have a Surface.image +attribute and a Surface.rect. The Group.clear() method requires these +same attributes, and can be used to erase all the Sprites with background. +There are also more advanced Groups: pygame.sprite.RenderUpdates() and +pygame.sprite.OrderedUpdates().

+

Lastly, this module contains several collision functions. These help find +sprites inside multiple groups that have intersecting bounding rectangles. To +find the collisions, the Sprites are required to have a Surface.rect +attribute assigned.

+

The groups are designed for high efficiency in removing and adding Sprites to +them. They also allow cheap testing to see if a Sprite already exists in a +Group. A given Sprite can exist in any number of groups. A game could use some +groups to control object rendering, and a completely separate set of groups to +control interaction or player movement. Instead of adding type attributes or +bools to a derived Sprite class, consider keeping the Sprites inside organized +Groups. This will allow for easier lookup later in the game.

+

Sprites and Groups manage their relationships with the add() and +remove() methods. These methods can accept a single or multiple targets for +membership. The default initializers for these classes also takes a single or +list of targets for initial membership. It is safe to repeatedly add and remove +the same Sprite from a Group.

+

While it is possible to design sprite and group classes that don't derive from +the Sprite and AbstractGroup classes below, it is strongly recommended that you +extend those when you add a Sprite or Group class.

+

Sprites are not thread safe. So lock them yourself if using threads.

+
+
+pygame.sprite.Sprite
+
+
Simple base class for visible game objects.
+
Sprite(*groups) -> Sprite
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+method to control sprite behavior
+add the sprite to groups
+remove the sprite from groups
+remove the Sprite from all Groups
+does the sprite belong to any groups
+list of Groups that contain this Sprite
+

The base class for visible game objects. Derived classes will want to +override the Sprite.update() and assign a Sprite.image and +Sprite.rect attributes. The initializer can accept any number of Group +instances to be added to.

+

When subclassing the Sprite, be sure to call the base initializer before +adding the Sprite to Groups. For example:

+
class Block(pygame.sprite.Sprite):
+
+    # Constructor. Pass in the color of the block,
+    # and its x and y position
+    def __init__(self, color, width, height):
+       # Call the parent class (Sprite) constructor
+       pygame.sprite.Sprite.__init__(self)
+
+       # Create an image of the block, and fill it with a color.
+       # This could also be an image loaded from the disk.
+       self.image = pygame.Surface([width, height])
+       self.image.fill(color)
+
+       # Fetch the rectangle object that has the dimensions of the image
+       # Update the position of this object by setting the values of rect.x and rect.y
+       self.rect = self.image.get_rect()
+
+
+
+
+update()
+
+
method to control sprite behavior
+
update(*args, **kwargs) -> None
+
+

The default implementation of this method does nothing; it's just a +convenient "hook" that you can override. This method is called by +Group.update() with whatever arguments you give it.

+

There is no need to use this method if not using the convenience method +by the same name in the Group class.

+
+ +
+
+add()
+
+
add the sprite to groups
+
add(*groups) -> None
+
+

Any number of Group instances can be passed as arguments. The Sprite will +be added to the Groups it is not already a member of.

+
+ +
+
+remove()
+
+
remove the sprite from groups
+
remove(*groups) -> None
+
+

Any number of Group instances can be passed as arguments. The Sprite will +be removed from the Groups it is currently a member of.

+
+ +
+
+kill()
+
+
remove the Sprite from all Groups
+
kill() -> None
+
+

The Sprite is removed from all the Groups that contain it. This won't +change anything about the state of the Sprite. It is possible to continue +to use the Sprite after this method has been called, including adding it +to Groups.

+
+ +
+
+alive()
+
+
does the sprite belong to any groups
+
alive() -> bool
+
+

Returns True when the Sprite belongs to one or more Groups.

+
+ +
+
+groups()
+
+
list of Groups that contain this Sprite
+
groups() -> group_list
+
+

Return a list of all the Groups that contain this Sprite.

+
+ +
+ +
+
+pygame.sprite.WeakSprite
+
+
A subclass of Sprite that references its Groups weakly. This means that any group this belongs to that is not referenced anywhere else is garbage collected automatically.
+
WeakSprite(*groups) -> WeakSprite
+
+
+ +
+
+pygame.sprite.DirtySprite
+
+
A subclass of Sprite with more attributes and features.
+
DirtySprite(*groups) -> DirtySprite
+
+

Extra DirtySprite attributes with their default values:

+

dirty = 1

+
if set to 1, it is repainted and then set to 0 again
+if set to 2 then it is always dirty ( repainted each frame,
+flag is not reset)
+0 means that it is not dirty and therefore not repainted again
+
+
+

blendmode = 0

+
its the special_flags argument of blit, blendmodes
+
+
+

source_rect = None

+
source rect to use, remember that it is relative to
+topleft (0,0) of self.image
+
+
+

visible = 1

+
normally 1, if set to 0 it will not be repainted
+(you must set it dirty too to be erased from screen)
+
+
+

layer = 0

+
(READONLY value, it is read when adding it to the
+LayeredDirty, for details see doc of LayeredDirty)
+
+
+
+ +
+
+pygame.sprite.Group
+
+
A container class to hold and manage multiple Sprite objects.
+
Group(*sprites) -> Group
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+list of the Sprites this Group contains
+duplicate the Group
+add Sprites to this Group
+remove Sprites from the Group
+test if a Group contains Sprites
+call the update method on contained Sprites
+blit the Sprite images
+draw a background over the Sprites
+remove all Sprites
+

A simple container for Sprite objects. This class can be inherited to create +containers with more specific behaviors. The constructor takes any number of +Sprite arguments to add to the Group. The group supports the following +standard Python operations:

+
in      test if a Sprite is contained
+len     the number of Sprites contained
+bool    test if any Sprites are contained
+iter    iterate through all the Sprites
+
+
+

The Sprites in the Group are ordered only on python 3.6 and higher. +Below python 3.6 drawing and iterating over the Sprites is in no particular order.

+
+
+sprites()
+
+
list of the Sprites this Group contains
+
sprites() -> sprite_list
+
+

Return a list of all the Sprites this group contains. You can also get an +iterator from the group, but you cannot iterate over a Group while +modifying it.

+
+ +
+
+copy()
+
+
duplicate the Group
+
copy() -> Group
+
+

Creates a new Group with all the same Sprites as the original. If you +have subclassed Group, the new object will have the same (sub-)class as +the original. This only works if the derived class's constructor takes +the same arguments as the Group class's.

+
+ +
+
+add()
+
+
add Sprites to this Group
+
add(*sprites) -> None
+
+

Add any number of Sprites to this Group. This will only add Sprites that +are not already members of the Group.

+

Each sprite argument can also be a iterator containing Sprites.

+
+ +
+
+remove()
+
+
remove Sprites from the Group
+
remove(*sprites) -> None
+
+

Remove any number of Sprites from the Group. This will only remove +Sprites that are already members of the Group.

+

Each sprite argument can also be a iterator containing Sprites.

+
+ +
+
+has()
+
+
test if a Group contains Sprites
+
has(*sprites) -> bool
+
+

Return True if the Group contains all of the given sprites. This is +similar to using the "in" operator on the Group ("if sprite in group: +..."), which tests if a single Sprite belongs to a Group.

+

Each sprite argument can also be a iterator containing Sprites.

+
+ +
+
+update()
+
+
call the update method on contained Sprites
+
update(*args, **kwargs) -> None
+
+

Calls the update() method on all Sprites in the Group. The base +Sprite class has an update method that takes any number of arguments and +does nothing. The arguments passed to Group.update() will be passed +to each Sprite.

+

There is no way to get the return value from the Sprite.update() +methods.

+
+ +
+
+draw()
+
+
blit the Sprite images
+
draw(Surface, bgsurf=None, special_flags=0) -> List[Rect]
+
+

Draws the contained Sprites to the Surface argument. This uses the +Sprite.image attribute for the source surface, and Sprite.rect +for the position. special_flags is passed to Surface.blit(). +bgsurf is unused in this method but LayeredDirty.draw() uses +it.

+

The Group does not keep sprites in any order, so the draw order is +arbitrary.

+
+ +
+
+clear()
+
+
draw a background over the Sprites
+
clear(Surface_dest, background) -> None
+
+

Erases the Sprites used in the last Group.draw() call. The +destination Surface is cleared by filling the drawn Sprite positions with +the background.

+

The background is usually a Surface image the same dimensions as the +destination Surface. However, it can also be a callback function that +takes two arguments; the destination Surface and an area to clear. The +background callback function will be called several times each clear.

+

Here is an example callback that will clear the Sprites with solid red:

+
def clear_callback(surf, rect):
+    color = 255, 0, 0
+    surf.fill(color, rect)
+
+
+
+ +
+
+empty()
+
+
remove all Sprites
+
empty() -> None
+
+

Removes all Sprites from this Group.

+
+ +
+ +
+
+pygame.sprite.WeakDirtySprite
+
+
A subclass of WeakSprite and DirtySprite that combines the benefits of both classes.
+
WeakDirtySprite(*groups) -> WeakDirtySprite
+
+
+ +
+
+pygame.sprite.RenderPlain
+
+
Same as pygame.sprite.Group
+
+

This class is an alias to pygame.sprite.Group(). It has no additional functionality.

+
+ +
+
+pygame.sprite.RenderClear
+
+
Same as pygame.sprite.Group
+
+

This class is an alias to pygame.sprite.Group(). It has no additional functionality.

+
+ +
+
+pygame.sprite.RenderUpdates
+
+
Group sub-class that tracks dirty updates.
+
RenderUpdates(*sprites) -> RenderUpdates
+
+ +++++ + + + + + + +
+blit the Sprite images and track changed areas
+

This class is derived from pygame.sprite.Group(). It has an extended +draw() method that tracks the changed areas of the screen.

+
+
+draw()
+
+
blit the Sprite images and track changed areas
+
draw(surface, bgsurf=None, special_flags=0) -> Rect_list
+
+

Draws all the Sprites to the surface, the same as Group.draw(). This +method also returns a list of Rectangular areas on the screen that have +been changed. The returned changes include areas of the screen that have +been affected by previous Group.clear() calls. special_flags is +passed to Surface.blit().

+

The returned Rect list should be passed to pygame.display.update(). +This will help performance on software driven display modes. This type of +updating is usually only helpful on destinations with non-animating +backgrounds.

+
+ +
+ +
+
+pygame.sprite.OrderedUpdates()
+
+
RenderUpdates sub-class that draws Sprites in order of addition.
+
OrderedUpdates(*sprites) -> OrderedUpdates
+
+

This class derives from pygame.sprite.RenderUpdates(). It maintains the +order in which the Sprites were added to the Group for rendering. This makes +adding and removing Sprites from the Group a little slower than regular +Groups.

+
+ +
+
+pygame.sprite.LayeredUpdates
+
+
LayeredUpdates is a sprite group that handles layers and draws like OrderedUpdates.
+
LayeredUpdates(*sprites, **kwargs) -> LayeredUpdates
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+add a sprite or sequence of sprites to a group
+returns a ordered list of sprites (first back, last top).
+draw all sprites in the right order onto the passed surface.
+returns a list with all sprites at that position.
+returns the sprite at the index idx from the groups sprites
+removes all sprites from a layer and returns them as a list.
+returns a list of layers defined (unique), sorted from bottom up.
+changes the layer of the sprite
+returns the layer that sprite is currently in.
+returns the top layer
+returns the bottom layer
+brings the sprite to front layer
+moves the sprite to the bottom layer
+returns the topmost sprite
+returns all sprites from a layer, ordered by how they where added
+switches the sprites from layer1 to layer2
+

This group is fully compatible with pygame.sprite.SpriteSimple base class for visible game objects..

+

You can set the default layer through kwargs using 'default_layer' and an +integer for the layer. The default layer is 0.

+

If the sprite you add has an attribute _layer then that layer will be used. +If the **kwarg contains 'layer' then the sprites passed will be added to +that layer (overriding the sprite.layer attribute). If neither sprite +has attribute layer nor **kwarg then the default layer is used to add the +sprites.

+
+

New in pygame 1.8.

+
+
+
+add()
+
+
add a sprite or sequence of sprites to a group
+
add(*sprites, **kwargs) -> None
+
+

If the sprite(s) have an attribute layer then that is used for the +layer. If **kwargs contains 'layer' then the sprite(s) will be added +to that argument (overriding the sprite layer attribute). If neither is +passed then the sprite(s) will be added to the default layer.

+
+ +
+
+sprites()
+
+
returns a ordered list of sprites (first back, last top).
+
sprites() -> sprites
+
+
+ +
+
+draw()
+
+
draw all sprites in the right order onto the passed surface.
+
draw(surface, bgsurf=None, special_flags=0) -> Rect_list
+
+
+ +
+
+get_sprites_at()
+
+
returns a list with all sprites at that position.
+
get_sprites_at(pos) -> colliding_sprites
+
+

Bottom sprites first, top last.

+
+ +
+
+get_sprite()
+
+
returns the sprite at the index idx from the groups sprites
+
get_sprite(idx) -> sprite
+
+

Raises IndexOutOfBounds if the idx is not within range.

+
+ +
+
+remove_sprites_of_layer()
+
+
removes all sprites from a layer and returns them as a list.
+
remove_sprites_of_layer(layer_nr) -> sprites
+
+
+ +
+
+layers()
+
+
returns a list of layers defined (unique), sorted from bottom up.
+
layers() -> layers
+
+
+ +
+
+change_layer()
+
+
changes the layer of the sprite
+
change_layer(sprite, new_layer) -> None
+
+

sprite must have been added to the renderer. It is not checked.

+
+ +
+
+get_layer_of_sprite()
+
+
returns the layer that sprite is currently in.
+
get_layer_of_sprite(sprite) -> layer
+
+

If the sprite is not found then it will return the default layer.

+
+ +
+
+get_top_layer()
+
+
returns the top layer
+
get_top_layer() -> layer
+
+
+ +
+
+get_bottom_layer()
+
+
returns the bottom layer
+
get_bottom_layer() -> layer
+
+
+ +
+
+move_to_front()
+
+
brings the sprite to front layer
+
move_to_front(sprite) -> None
+
+

Brings the sprite to front, changing sprite layer to topmost layer (added +at the end of that layer).

+
+ +
+
+move_to_back()
+
+
moves the sprite to the bottom layer
+
move_to_back(sprite) -> None
+
+

Moves the sprite to the bottom layer, moving it behind all other layers +and adding one additional layer.

+
+ +
+
+get_top_sprite()
+
+
returns the topmost sprite
+
get_top_sprite() -> Sprite
+
+
+ +
+
+get_sprites_from_layer()
+
+
returns all sprites from a layer, ordered by how they where added
+
get_sprites_from_layer(layer) -> sprites
+
+

Returns all sprites from a layer, ordered by how they where added. It +uses linear search and the sprites are not removed from layer.

+
+ +
+
+switch_layer()
+
+
switches the sprites from layer1 to layer2
+
switch_layer(layer1_nr, layer2_nr) -> None
+
+

The layers number must exist, it is not checked.

+
+ +
+ +
+
+pygame.sprite.LayeredDirty
+
+
LayeredDirty group is for DirtySprite objects. Subclasses LayeredUpdates.
+
LayeredDirty(*sprites, **kwargs) -> LayeredDirty
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+draw all sprites in the right order onto the passed surface.
+used to set background
+repaints the given area
+clip the area where to draw. Just pass None (default) to reset the clip
+clip the area where to draw. Just pass None (default) to reset the clip
+changes the layer of the sprite
+sets the threshold in milliseconds
+sets the threshold in milliseconds
+

This group requires pygame.sprite.DirtySpriteA subclass of Sprite with more attributes and features. or any sprite that +has the following attributes:

+
image, rect, dirty, visible, blendmode (see doc of DirtySprite).
+
+
+

It uses the dirty flag technique and is therefore faster than the +pygame.sprite.RenderUpdatesGroup sub-class that tracks dirty updates. if you have many static sprites. It +also switches automatically between dirty rect update and full screen +drawing, so you do not have to worry what would be faster.

+

Same as for the pygame.sprite.GroupA container class to hold and manage multiple Sprite objects.. You can specify some +additional attributes through kwargs:

+
_use_update: True/False   default is False
+_default_layer: default layer where sprites without a layer are added.
+_time_threshold: threshold time for switching between dirty rect mode
+    and fullscreen mode, defaults to 1000./80  == 1000./fps
+
+
+
+

New in pygame 1.8.

+
+
+
+draw()
+
+
draw all sprites in the right order onto the passed surface.
+
draw(surface, bgsurf=None, special_flags=None) -> Rect_list
+
+

You can pass the background too. If a background is already set, then the +bgsurf argument has no effect. If present, the special_flags argument is +always passed to Surface.blit(), overriding DirtySprite.blendmode. +If special_flags is not present, DirtySprite.blendmode is passed +to the Surface.blit() instead.

+
+ +
+
+clear()
+
+
used to set background
+
clear(surface, bgd) -> None
+
+
+ +
+
+repaint_rect()
+
+
repaints the given area
+
repaint_rect(screen_rect) -> None
+
+

screen_rect is in screen coordinates.

+
+ +
+
+set_clip()
+
+
clip the area where to draw. Just pass None (default) to reset the clip
+
set_clip(screen_rect=None) -> None
+
+
+ +
+
+get_clip()
+
+
clip the area where to draw. Just pass None (default) to reset the clip
+
get_clip() -> Rect
+
+
+ +
+
+change_layer()
+
+
changes the layer of the sprite
+
change_layer(sprite, new_layer) -> None
+
+

sprite must have been added to the renderer. It is not checked.

+
+ +
+
+set_timing_treshold()
+
+
sets the threshold in milliseconds
+
set_timing_treshold(time_ms) -> None
+
+

DEPRECATED: Use set_timing_threshold() instead.

+
+

Deprecated since pygame 2.1.1.

+
+
+ +
+
+set_timing_threshold()
+
+
sets the threshold in milliseconds
+
set_timing_threshold(time_ms) -> None
+
+

Defaults to 1000.0 / 80.0. This means that the screen will be painted +using the flip method rather than the update method if the update +method is taking so long to update the screen that the frame rate falls +below 80 frames per second.

+
+

New in pygame 2.1.1.

+
+
+
Raises
+

TypeError -- if time_ms is not int or float

+
+
+
+ +
+ +
+
+pygame.sprite.GroupSingle()
+
+
Group container that holds a single sprite.
+
GroupSingle(sprite=None) -> GroupSingle
+
+

The GroupSingle container only holds a single Sprite. When a new Sprite is +added, the old one is removed.

+

There is a special property, GroupSingle.sprite, that accesses the +Sprite that this Group contains. It can be None when the Group is empty. The +property can also be assigned to add a Sprite into the GroupSingle +container.

+
+ +
+
+pygame.sprite.spritecollide()
+
+
Find sprites in a group that intersect another sprite.
+
spritecollide(sprite, group, dokill, collided = None) -> Sprite_list
+
+

Return a list containing all Sprites in a Group that intersect with another +Sprite. Intersection is determined by comparing the Sprite.rect +attribute of each Sprite.

+

The dokill argument is a bool. If set to True, all Sprites that collide will +be removed from the Group.

+

The collided argument is a callback function used to calculate if two +sprites are colliding. it should take two sprites as values, and return a +bool value indicating if they are colliding. If collided is not passed, all +sprites must have a "rect" value, which is a rectangle of the sprite area, +which will be used to calculate the collision.

+

collided callables:

+
collide_rect, collide_rect_ratio, collide_circle,
+collide_circle_ratio, collide_mask
+
+
+

Example:

+
# See if the Sprite block has collided with anything in the Group block_list
+# The True flag will remove the sprite in block_list
+blocks_hit_list = pygame.sprite.spritecollide(player, block_list, True)
+
+# Check the list of colliding sprites, and add one to the score for each one
+for block in blocks_hit_list:
+    score +=1
+
+
+
+ +
+
+pygame.sprite.collide_rect()
+
+
Collision detection between two sprites, using rects.
+
collide_rect(left, right) -> bool
+
+

Tests for collision between two sprites. Uses the pygame rect colliderect +function to calculate the collision. Intended to be passed as a collided +callback function to the *collide functions. Sprites must have a "rect" +attributes.

+
+

New in pygame 1.8.

+
+
+ +
+
+pygame.sprite.collide_rect_ratio()
+
+
Collision detection between two sprites, using rects scaled to a ratio.
+
collide_rect_ratio(ratio) -> collided_callable
+
+

A callable class that checks for collisions between two sprites, using a +scaled version of the sprites rects.

+

Is created with a ratio, the instance is then intended to be passed as a +collided callback function to the *collide functions.

+

A ratio is a floating point number - 1.0 is the same size, 2.0 is twice as +big, and 0.5 is half the size.

+
+

New in pygame 1.8.1.

+
+
+ +
+
+pygame.sprite.collide_circle()
+
+
Collision detection between two sprites, using circles.
+
collide_circle(left, right) -> bool
+
+

Tests for collision between two sprites, by testing to see if two circles +centered on the sprites overlap. If the sprites have a "radius" attribute, +that is used to create the circle, otherwise a circle is created that is big +enough to completely enclose the sprites rect as given by the "rect" +attribute. Intended to be passed as a collided callback function to the +*collide functions. Sprites must have a "rect" and an optional "radius" +attribute.

+
+

New in pygame 1.8.1.

+
+
+ +
+
+pygame.sprite.collide_circle_ratio()
+
+
Collision detection between two sprites, using circles scaled to a ratio.
+
collide_circle_ratio(ratio) -> collided_callable
+
+

A callable class that checks for collisions between two sprites, using a +scaled version of the sprites radius.

+

Is created with a floating point ratio, the instance is then intended to be +passed as a collided callback function to the *collide functions.

+

A ratio is a floating point number - 1.0 is the same size, 2.0 is twice as +big, and 0.5 is half the size.

+

The created callable tests for collision between two sprites, by testing to +see if two circles centered on the sprites overlap, after scaling the +circles radius by the stored ratio. If the sprites have a "radius" +attribute, that is used to create the circle, otherwise a circle is created +that is big enough to completely enclose the sprites rect as given by the +"rect" attribute. Intended to be passed as a collided callback function to +the *collide functions. Sprites must have a "rect" and an optional "radius" +attribute.

+
+

New in pygame 1.8.1.

+
+
+ +
+
+pygame.sprite.collide_mask()
+
+
Collision detection between two sprites, using masks.
+
collide_mask(sprite1, sprite2) -> (int, int)
+
collide_mask(sprite1, sprite2) -> None
+
+

Tests for collision between two sprites, by testing if their bitmasks +overlap (uses pygame.mask.Mask.overlap()Returns the point of intersection). If the sprites have a +mask attribute, it is used as the mask, otherwise a mask is created from +the sprite's image (uses pygame.mask.from_surface()Creates a Mask from the given surface). Sprites must +have a rect attribute; the mask attribute is optional.

+

The first point of collision between the masks is returned. The collision +point is offset from sprite1's mask's topleft corner (which is always +(0, 0)). The collision point is a position within the mask and is not +related to the actual screen position of sprite1.

+

This function is intended to be passed as a collided callback function +to the group collide functions (see spritecollide(), +groupcollide(), spritecollideany()).

+
+

Note

+

To increase performance, create and set a mask attribute for all +sprites that will use this function to check for collisions. Otherwise, +each time this function is called it will create new masks.

+
+
+

Note

+

A new mask needs to be recreated each time a sprite's image is changed +(e.g. if a new image is used or the existing image is rotated).

+
+
# Example of mask creation for a sprite.
+sprite.mask = pygame.mask.from_surface(sprite.image)
+
+
+
+
Returns
+

first point of collision between the masks or None if no +collision

+
+
Return type
+

tuple(int, int) or NoneType

+
+
+
+

New in pygame 1.8.0.

+
+
+ +
+
+pygame.sprite.groupcollide()
+
+
Find all sprites that collide between two groups.
+
groupcollide(group1, group2, dokill1, dokill2, collided = None) -> Sprite_dict
+
+

This will find collisions between all the Sprites in two groups. +Collision is determined by comparing the Sprite.rect attribute of +each Sprite or by using the collided function if it is not None.

+

Every Sprite inside group1 is added to the return dictionary. The value for +each item is the list of Sprites in group2 that intersect.

+

If either dokill argument is True, the colliding Sprites will be removed +from their respective Group.

+

The collided argument is a callback function used to calculate if two sprites are +colliding. It should take two sprites as values and return a bool value +indicating if they are colliding. If collided is not passed, then all +sprites must have a "rect" value, which is a rectangle of the sprite area, +which will be used to calculate the collision.

+
+ +
+
+pygame.sprite.spritecollideany()
+
+
Simple test if a sprite intersects anything in a group.
+
spritecollideany(sprite, group, collided = None) -> Sprite Collision with the returned sprite.
+
spritecollideany(sprite, group, collided = None) -> None No collision
+
+

If the sprite collides with any single sprite in the group, a single +sprite from the group is returned. On no collision None is returned.

+

If you don't need all the features of the pygame.sprite.spritecollide() function, this +function will be a bit quicker.

+

The collided argument is a callback function used to calculate if two sprites are +colliding. It should take two sprites as values and return a bool value +indicating if they are colliding. If collided is not passed, then all +sprites must have a "rect" value, which is a rectangle of the sprite area, +which will be used to calculate the collision.

+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/surface.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/surface.html new file mode 100644 index 00000000..b42395a6 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/surface.html @@ -0,0 +1,1339 @@ + + + + + + + + + pygame.Surface — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.Surface
+
+
pygame object for representing images
+
Surface((width, height), flags=0, depth=0, masks=None) -> Surface
+
Surface((width, height), flags=0, Surface) -> Surface
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+draw one image onto another
+draw many images onto another
+change the pixel format of an image
+change the pixel format of an image including per pixel alphas
+create a new copy of a Surface
+fill Surface with a solid color
+Shift the surface image in place
+Set the transparent colorkey
+Get the current transparent colorkey
+set the alpha value for the full Surface image
+get the current Surface transparency value
+lock the Surface memory for pixel access
+unlock the Surface memory from pixel access
+test if the Surface requires locking
+test if the Surface is current locked
+Gets the locks for the Surface
+get the color value at a single pixel
+set the color value for a single pixel
+get the mapped color value at a single pixel
+get the color index palette for an 8-bit Surface
+get the color for a single entry in a palette
+set the color palette for an 8-bit Surface
+set the color for a single index in an 8-bit Surface palette
+convert a color into a mapped color value
+convert a mapped integer color value into a Color
+set the current clipping area of the Surface
+get the current clipping area of the Surface
+create a new surface that references its parent
+find the parent of a subsurface
+find the top level parent of a subsurface
+find the position of a child subsurface inside a parent
+find the absolute position of a child subsurface inside its top level parent
+get the dimensions of the Surface
+get the width of the Surface
+get the height of the Surface
+get the rectangular area of the Surface
+get the bit depth of the Surface pixel format
+get the bytes used per Surface pixel
+get the additional flags used for the Surface
+get the number of bytes used per Surface row
+the bitmasks needed to convert between a color and a mapped integer
+set the bitmasks needed to convert between a color and a mapped integer
+the bit shifts needed to convert between a color and a mapped integer
+sets the bit shifts needed to convert between a color and a mapped integer
+the significant bits used to convert between a color and a mapped integer
+find the smallest rect containing data
+return a buffer view of the Surface's pixels.
+acquires a buffer object for the pixels of the Surface.
+pixel buffer address
+returns a copy of the surface with the RGB channels pre-multiplied by the alpha channel.
+

A pygame Surface is used to represent any image. The Surface has a fixed +resolution and pixel format. Surfaces with 8-bit pixels use a color palette +to map to 24-bit color.

+

Call pygame.Surface()pygame object for representing images to create a new image object. The Surface will +be cleared to all black. The only required arguments are the sizes. With no +additional arguments, the Surface will be created in a format that best +matches the display Surface.

+

The pixel format can be controlled by passing the bit depth or an existing +Surface. The flags argument is a bitmask of additional features for the +surface. You can pass any combination of these flags:

+
HWSURFACE    (obsolete in pygame 2) creates the image in video memory
+SRCALPHA     the pixel format will include a per-pixel alpha
+
+
+

Both flags are only a request, and may not be possible for all displays and +formats.

+

Advance users can combine a set of bitmasks with a depth value. The masks +are a set of 4 integers representing which bits in a pixel will represent +each color. Normal Surfaces should not require the masks argument.

+

Surfaces can have many extra attributes like alpha planes, colorkeys, source +rectangle clipping. These functions mainly effect how the Surface is blitted +to other Surfaces. The blit routines will attempt to use hardware +acceleration when possible, otherwise they will use highly optimized +software blitting methods.

+

There are three types of transparency supported in pygame: colorkeys, +surface alphas, and pixel alphas. Surface alphas can be mixed with +colorkeys, but an image with per pixel alphas cannot use the other modes. +Colorkey transparency makes a single color value transparent. Any pixels +matching the colorkey will not be drawn. The surface alpha value is a single +value that changes the transparency for the entire image. A surface alpha of +255 is opaque, and a value of 0 is completely transparent.

+

Per pixel alphas are different because they store a transparency value for +every pixel. This allows for the most precise transparency effects, but it +also the slowest. Per pixel alphas cannot be mixed with surface alpha and +colorkeys.

+

There is support for pixel access for the Surfaces. Pixel access on hardware +surfaces is slow and not recommended. Pixels can be accessed using the +get_at() and set_at() functions. These methods are fine for +simple access, but will be considerably slow when doing of pixel work with +them. If you plan on doing a lot of pixel level work, it is recommended to +use a pygame.PixelArraypygame object for direct pixel access of surfaces, which gives an array like view of the +surface. For involved mathematical manipulations try the +pygame.surfarraypygame module for accessing surface pixel data using array interfaces module (It's quite quick, but requires NumPy.)

+

Any functions that directly access a surface's pixel data will need that +surface to be lock()'ed. These functions can lock() and +unlock() the surfaces themselves without assistance. But, if a +function will be called many times, there will be a lot of overhead for +multiple locking and unlocking of the surface. It is best to lock the +surface manually before making the function call many times, and then +unlocking when you are finished. All functions that need a locked surface +will say so in their docs. Remember to leave the Surface locked only while +necessary.

+

Surface pixels are stored internally as a single number that has all the +colors encoded into it. Use the map_rgb() and +unmap_rgb() to convert between individual red, green, and blue +values into a packed integer for that Surface.

+

Surfaces can also reference sections of other Surfaces. These are created +with the subsurface() method. Any change to either Surface will +effect the other.

+

Each Surface contains a clipping area. By default the clip area covers the +entire Surface. If it is changed, all drawing operations will only effect +the smaller area.

+
+
+blit()
+
+
draw one image onto another
+
blit(source, dest, area=None, special_flags=0) -> Rect
+
+

Draws a source Surface onto this Surface. The draw can be positioned with +the dest argument. The dest argument can either be a pair of coordinates representing the position of +the upper left corner of the blit or a Rect, where the upper left corner of the rectangle will be used as the +position for the blit. The size of the destination rectangle does not +effect the blit.

+

An optional area rectangle can be passed as well. This represents a +smaller portion of the source Surface to draw.

+
+

New in pygame 1.8: Optional special_flags: BLEND_ADD, BLEND_SUB, +BLEND_MULT, BLEND_MIN, BLEND_MAX.

+
+
+

New in pygame 1.8.1: Optional special_flags: BLEND_RGBA_ADD, BLEND_RGBA_SUB, +BLEND_RGBA_MULT, BLEND_RGBA_MIN, BLEND_RGBA_MAX +BLEND_RGB_ADD, BLEND_RGB_SUB, BLEND_RGB_MULT, +BLEND_RGB_MIN, BLEND_RGB_MAX.

+
+
+

New in pygame 1.9.2: Optional special_flags: BLEND_PREMULTIPLIED

+
+
+

New in pygame 2.0.0: Optional special_flags: BLEND_ALPHA_SDL2 - Uses the SDL2 blitter for alpha blending, +this gives different results than the default blitter, which is modelled after SDL1, due to +different approximations used for the alpha blending formula. The SDL2 blitter also supports +RLE on alpha blended surfaces which the pygame one does not.

+
+

The return rectangle is the area of the affected pixels, excluding any +pixels outside the destination Surface, or outside the clipping area.

+

Pixel alphas will be ignored when blitting to an 8 bit Surface.

+

For a surface with colorkey or blanket alpha, a blit to self may give +slightly different colors than a non self-blit.

+
+ +
+
+blits()
+
+
draw many images onto another
+
blits(blit_sequence=((source, dest), ...), doreturn=1) -> [Rect, ...] or None
+
blits(((source, dest, area), ...)) -> [Rect, ...]
+
blits(((source, dest, area, special_flags), ...)) -> [Rect, ...]
+
+

Draws many surfaces onto this Surface. It takes a sequence as input, +with each of the elements corresponding to the ones of blit(). +It needs at minimum a sequence of (source, dest).

+
+
Parameters
+
    +
  • blit_sequence -- a sequence of surfaces and arguments to blit them, +they correspond to the blit() arguments

  • +
  • doreturn -- if True, return a list of rects of the areas changed, +otherwise return None

  • +
+
+
Returns
+

a list of rects of the areas changed if doreturn is +True, otherwise None

+
+
Return type
+

list or None

+
+
+

New in pygame 1.9.4.

+
+ +
+
+convert()
+
+
change the pixel format of an image
+
convert(Surface=None) -> Surface
+
convert(depth, flags=0) -> Surface
+
convert(masks, flags=0) -> Surface
+
+

Creates a new copy of the Surface with the pixel format changed. The new +pixel format can be determined from another existing Surface. Otherwise +depth, flags, and masks arguments can be used, similar to the +pygame.Surface()pygame object for representing images call.

+

If no arguments are passed the new Surface will have the same pixel +format as the display Surface. This is always the fastest format for +blitting. It is a good idea to convert all Surfaces before they are +blitted many times.

+

The converted Surface will have no pixel alphas. They will be stripped if +the original had them. See convert_alpha() for preserving or +creating per-pixel alphas.

+

The new copy will have the same class as the copied surface. This lets +as Surface subclass inherit this method without the need to override, +unless subclass specific instance attributes also need copying.

+
+ +
+
+convert_alpha()
+
+
change the pixel format of an image including per pixel alphas
+
convert_alpha(Surface) -> Surface
+
convert_alpha() -> Surface
+
+

Creates a new copy of the surface with the desired pixel format. The new +surface will be in a format suited for quick blitting to the given format +with per pixel alpha. If no surface is given, the new surface will be +optimized for blitting to the current display.

+

Unlike the convert() method, the pixel format for the new +image will not be exactly the same as the requested source, but it will +be optimized for fast alpha blitting to the destination.

+

As with convert() the returned surface has the same class as +the converted surface.

+
+ +
+
+copy()
+
+
create a new copy of a Surface
+
copy() -> Surface
+
+

Makes a duplicate copy of a Surface. The new surface will have the same +pixel formats, color palettes, transparency settings, and class as the +original. If a Surface subclass also needs to copy any instance specific +attributes then it should override copy().

+
+ +
+
+fill()
+
+
fill Surface with a solid color
+
fill(color, rect=None, special_flags=0) -> Rect
+
+

Fill the Surface with a solid color. If no rect argument is given the +entire Surface will be filled. The rect argument will limit the fill to a +specific area. The fill will also be contained by the Surface clip area.

+

The color argument can be either a RGB sequence, a RGBA sequence +or a mapped color index. If using RGBA, the Alpha (A part of +RGBA) is ignored unless the surface uses per pixel alpha (Surface has +the SRCALPHA flag).

+
+

New in pygame 1.8: Optional special_flags: BLEND_ADD, BLEND_SUB, +BLEND_MULT, BLEND_MIN, BLEND_MAX.

+
+
+

New in pygame 1.8.1: Optional special_flags: BLEND_RGBA_ADD, BLEND_RGBA_SUB, +BLEND_RGBA_MULT, BLEND_RGBA_MIN, BLEND_RGBA_MAX +BLEND_RGB_ADD, BLEND_RGB_SUB, BLEND_RGB_MULT, +BLEND_RGB_MIN, BLEND_RGB_MAX.

+
+

This will return the affected Surface area.

+
+ +
+
+scroll()
+
+
Shift the surface image in place
+
scroll(dx=0, dy=0) -> None
+
+

Move the image by dx pixels right and dy pixels down. dx and dy may be +negative for left and up scrolls respectively. Areas of the surface that +are not overwritten retain their original pixel values. Scrolling is +contained by the Surface clip area. It is safe to have dx and dy values +that exceed the surface size.

+
+

New in pygame 1.9.

+
+
+ +
+
+set_colorkey()
+
+
Set the transparent colorkey
+
set_colorkey(Color, flags=0) -> None
+
set_colorkey(None) -> None
+
+

Set the current color key for the Surface. When blitting this Surface +onto a destination, any pixels that have the same color as the colorkey +will be transparent. The color can be an RGB color or a mapped color +integer. If None is passed, the colorkey will be unset.

+

The colorkey will be ignored if the Surface is formatted to use per pixel +alpha values. The colorkey can be mixed with the full Surface alpha +value.

+

The optional flags argument can be set to pygame.RLEACCEL to provide +better performance on non accelerated displays. An RLEACCEL Surface +will be slower to modify, but quicker to blit as a source.

+
+ +
+
+get_colorkey()
+
+
Get the current transparent colorkey
+
get_colorkey() -> RGB or None
+
+

Return the current colorkey value for the Surface. If the colorkey is not +set then None is returned.

+
+ +
+
+set_alpha()
+
+
set the alpha value for the full Surface image
+
set_alpha(value, flags=0) -> None
+
set_alpha(None) -> None
+
+

Set the current alpha value for the Surface. When blitting this Surface +onto a destination, the pixels will be drawn slightly transparent. The +alpha value is an integer from 0 to 255, 0 is fully transparent and 255 +is fully opaque. If None is passed for the alpha value, then alpha +blending will be disabled, including per-pixel alpha.

+

This value is different than the per pixel Surface alpha. For a surface +with per pixel alpha, blanket alpha is ignored and None is returned.

+
+

Changed in pygame 2.0: per-surface alpha can be combined with per-pixel +alpha.

+
+

The optional flags argument can be set to pygame.RLEACCEL to provide +better performance on non accelerated displays. An RLEACCEL Surface +will be slower to modify, but quicker to blit as a source.

+
+ +
+
+get_alpha()
+
+
get the current Surface transparency value
+
get_alpha() -> int_value
+
+

Return the current alpha value for the Surface.

+
+ +
+
+lock()
+
+
lock the Surface memory for pixel access
+
lock() -> None
+
+

Lock the pixel data of a Surface for access. On accelerated Surfaces, the +pixel data may be stored in volatile video memory or nonlinear compressed +forms. When a Surface is locked the pixel memory becomes available to +access by regular software. Code that reads or writes pixel values will +need the Surface to be locked.

+

Surfaces should not remain locked for more than necessary. A locked +Surface can often not be displayed or managed by pygame.

+

Not all Surfaces require locking. The mustlock() method can +determine if it is actually required. There is no performance penalty for +locking and unlocking a Surface that does not need it.

+

All pygame functions will automatically lock and unlock the Surface data +as needed. If a section of code is going to make calls that will +repeatedly lock and unlock the Surface many times, it can be helpful to +wrap the block inside a lock and unlock pair.

+

It is safe to nest locking and unlocking calls. The surface will only be +unlocked after the final lock is released.

+
+ +
+
+unlock()
+
+
unlock the Surface memory from pixel access
+
unlock() -> None
+
+

Unlock the Surface pixel data after it has been locked. The unlocked +Surface can once again be drawn and managed by pygame. See the +lock() documentation for more details.

+

All pygame functions will automatically lock and unlock the Surface data +as needed. If a section of code is going to make calls that will +repeatedly lock and unlock the Surface many times, it can be helpful to +wrap the block inside a lock and unlock pair.

+

It is safe to nest locking and unlocking calls. The surface will only be +unlocked after the final lock is released.

+
+ +
+
+mustlock()
+
+
test if the Surface requires locking
+
mustlock() -> bool
+
+

Returns True if the Surface is required to be locked to access pixel +data. Usually pure software Surfaces do not require locking. This method +is rarely needed, since it is safe and quickest to just lock all Surfaces +as needed.

+

All pygame functions will automatically lock and unlock the Surface data +as needed. If a section of code is going to make calls that will +repeatedly lock and unlock the Surface many times, it can be helpful to +wrap the block inside a lock and unlock pair.

+
+ +
+
+get_locked()
+
+
test if the Surface is current locked
+
get_locked() -> bool
+
+

Returns True when the Surface is locked. It doesn't matter how many +times the Surface is locked.

+
+ +
+
+get_locks()
+
+
Gets the locks for the Surface
+
get_locks() -> tuple
+
+

Returns the currently existing locks for the Surface.

+
+ +
+
+get_at()
+
+
get the color value at a single pixel
+
get_at((x, y)) -> Color
+
+

Return a copy of the RGBA Color value at the given pixel. If the +Surface has no per pixel alpha, then the alpha value will always be 255 +(opaque). If the pixel position is outside the area of the Surface an +IndexError exception will be raised.

+

Getting and setting pixels one at a time is generally too slow to be used +in a game or realtime situation. It is better to use methods which +operate on many pixels at a time like with the blit, fill and draw +methods - or by using pygame.surfarraypygame module for accessing surface pixel data using array interfaces/pygame.PixelArraypygame object for direct pixel access of surfaces.

+

This function will temporarily lock and unlock the Surface as needed.

+
+

New in pygame 1.9: Returning a Color instead of tuple. Use tuple(surf.get_at((x,y))) +if you want a tuple, and not a Color. This should only matter if +you want to use the color as a key in a dict.

+
+
+ +
+
+set_at()
+
+
set the color value for a single pixel
+
set_at((x, y), Color) -> None
+
+

Set the RGBA or mapped integer color value for a single pixel. If the +Surface does not have per pixel alphas, the alpha value is ignored. +Setting pixels outside the Surface area or outside the Surface clipping +will have no effect.

+

Getting and setting pixels one at a time is generally too slow to be used +in a game or realtime situation.

+

This function will temporarily lock and unlock the Surface as needed.

+
+

Note

+

If the surface is palettized, the pixel color will be set to the +most similar color in the palette.

+
+
+ +
+
+get_at_mapped()
+
+
get the mapped color value at a single pixel
+
get_at_mapped((x, y)) -> Color
+
+

Return the integer value of the given pixel. If the pixel position is +outside the area of the Surface an IndexError exception will be +raised.

+

This method is intended for pygame unit testing. It unlikely has any use +in an application.

+

This function will temporarily lock and unlock the Surface as needed.

+
+

New in pygame 1.9.2.

+
+
+ +
+
+get_palette()
+
+
get the color index palette for an 8-bit Surface
+
get_palette() -> [RGB, RGB, RGB, ...]
+
+

Return a list of up to 256 color elements that represent the indexed +colors used in an 8-bit Surface. The returned list is a copy of the +palette, and changes will have no effect on the Surface.

+

Returning a list of Color(with length 3) instances instead of tuples.

+
+

New in pygame 1.9.

+
+
+ +
+
+get_palette_at()
+
+
get the color for a single entry in a palette
+
get_palette_at(index) -> RGB
+
+

Returns the red, green, and blue color values for a single index in a +Surface palette. The index should be a value from 0 to 255.

+
+

New in pygame 1.9: Returning Color(with length 3) instance instead of a tuple.

+
+
+ +
+
+set_palette()
+
+
set the color palette for an 8-bit Surface
+
set_palette([RGB, RGB, RGB, ...]) -> None
+
+

Set the full palette for an 8-bit Surface. This will replace the colors in +the existing palette. A partial palette can be passed and only the first +colors in the original palette will be changed.

+

This function has no effect on a Surface with more than 8-bits per pixel.

+
+ +
+
+set_palette_at()
+
+
set the color for a single index in an 8-bit Surface palette
+
set_palette_at(index, RGB) -> None
+
+

Set the palette value for a single entry in a Surface palette. The index +should be a value from 0 to 255.

+

This function has no effect on a Surface with more than 8-bits per pixel.

+
+ +
+
+map_rgb()
+
+
convert a color into a mapped color value
+
map_rgb(Color) -> mapped_int
+
+

Convert an RGBA color into the mapped integer value for this Surface. +The returned integer will contain no more bits than the bit depth of the +Surface. Mapped color values are not often used inside pygame, but can be +passed to most functions that require a Surface and a color.

+

See the Surface object documentation for more information about colors +and pixel formats.

+
+ +
+
+unmap_rgb()
+
+
convert a mapped integer color value into a Color
+
unmap_rgb(mapped_int) -> Color
+
+

Convert an mapped integer color into the RGB color components for +this Surface. Mapped color values are not often used inside pygame, but +can be passed to most functions that require a Surface and a color.

+

See the Surface object documentation for more information about colors +and pixel formats.

+
+ +
+
+set_clip()
+
+
set the current clipping area of the Surface
+
set_clip(rect) -> None
+
set_clip(None) -> None
+
+

Each Surface has an active clipping area. This is a rectangle that +represents the only pixels on the Surface that can be modified. If +None is passed for the rectangle the full Surface will be available +for changes.

+

The clipping area is always restricted to the area of the Surface itself. +If the clip rectangle is too large it will be shrunk to fit inside the +Surface.

+
+ +
+
+get_clip()
+
+
get the current clipping area of the Surface
+
get_clip() -> Rect
+
+

Return a rectangle of the current clipping area. The Surface will always +return a valid rectangle that will never be outside the bounds of the +image. If the Surface has had None set for the clipping area, the +Surface will return a rectangle with the full area of the Surface.

+
+ +
+
+subsurface()
+
+
create a new surface that references its parent
+
subsurface(Rect) -> Surface
+
+

Returns a new Surface that shares its pixels with its new parent. The new +Surface is considered a child of the original. Modifications to either +Surface pixels will effect each other. Surface information like clipping +area and color keys are unique to each Surface.

+

The new Surface will inherit the palette, color key, and alpha settings +from its parent.

+

It is possible to have any number of subsurfaces and subsubsurfaces on +the parent. It is also possible to subsurface the display Surface if the +display mode is not hardware accelerated.

+

See get_offset() and get_parent() to learn more +about the state of a subsurface.

+

A subsurface will have the same class as the parent surface.

+
+ +
+
+get_parent()
+
+
find the parent of a subsurface
+
get_parent() -> Surface
+
+

Returns the parent Surface of a subsurface. If this is not a subsurface +then None will be returned.

+
+ +
+
+get_abs_parent()
+
+
find the top level parent of a subsurface
+
get_abs_parent() -> Surface
+
+

Returns the parent Surface of a subsurface. If this is not a subsurface +then this surface will be returned.

+
+ +
+
+get_offset()
+
+
find the position of a child subsurface inside a parent
+
get_offset() -> (x, y)
+
+

Get the offset position of a child subsurface inside of a parent. If the +Surface is not a subsurface this will return (0, 0).

+
+ +
+
+get_abs_offset()
+
+
find the absolute position of a child subsurface inside its top level parent
+
get_abs_offset() -> (x, y)
+
+

Get the offset position of a child subsurface inside of its top level +parent Surface. If the Surface is not a subsurface this will return (0, +0).

+
+ +
+
+get_size()
+
+
get the dimensions of the Surface
+
get_size() -> (width, height)
+
+

Return the width and height of the Surface in pixels.

+
+ +
+
+get_width()
+
+
get the width of the Surface
+
get_width() -> width
+
+

Return the width of the Surface in pixels.

+
+ +
+
+get_height()
+
+
get the height of the Surface
+
get_height() -> height
+
+

Return the height of the Surface in pixels.

+
+ +
+
+get_rect()
+
+
get the rectangular area of the Surface
+
get_rect(**kwargs) -> Rect
+
+

Returns a new rectangle covering the entire surface. This rectangle will +always start at (0, 0) with a width and height the same size as the image.

+

You can pass keyword argument values to this function. These named values +will be applied to the attributes of the Rect before it is returned. An +example would be mysurf.get_rect(center=(100, 100)) to create a +rectangle for the Surface centered at a given position.

+
+ +
+
+get_bitsize()
+
+
get the bit depth of the Surface pixel format
+
get_bitsize() -> int
+
+

Returns the number of bits used to represent each pixel. This value may +not exactly fill the number of bytes used per pixel. For example a 15 bit +Surface still requires a full 2 bytes.

+
+ +
+
+get_bytesize()
+
+
get the bytes used per Surface pixel
+
get_bytesize() -> int
+
+

Return the number of bytes used per pixel.

+
+ +
+
+get_flags()
+
+
get the additional flags used for the Surface
+
get_flags() -> int
+
+

Returns a set of current Surface features. Each feature is a bit in the +flags bitmask. Typical flags are RLEACCEL, SRCALPHA, and +SRCCOLORKEY.

+

Here is a more complete list of flags. A full list can be found in +SDL_video.h

+
SWSURFACE      0x00000000    # Surface is in system memory
+HWSURFACE      0x00000001    # (obsolete in pygame 2) Surface is in video memory
+ASYNCBLIT      0x00000004    # (obsolete in pygame 2) Use asynchronous blits if possible
+
+
+

See pygame.display.set_mode()Initialize a window or screen for display for flags exclusive to the +display surface.

+

Used internally (read-only)

+
HWACCEL        0x00000100    # Blit uses hardware acceleration
+SRCCOLORKEY    0x00001000    # Blit uses a source color key
+RLEACCELOK     0x00002000    # Private flag
+RLEACCEL       0x00004000    # Surface is RLE encoded
+SRCALPHA       0x00010000    # Blit uses source alpha blending
+PREALLOC       0x01000000    # Surface uses preallocated memory
+
+
+
+ +
+
+get_pitch()
+
+
get the number of bytes used per Surface row
+
get_pitch() -> int
+
+

Return the number of bytes separating each row in the Surface. Surfaces +in video memory are not always linearly packed. Subsurfaces will also +have a larger pitch than their real width.

+

This value is not needed for normal pygame usage.

+
+ +
+
+get_masks()
+
+
the bitmasks needed to convert between a color and a mapped integer
+
get_masks() -> (R, G, B, A)
+
+

Returns the bitmasks used to isolate each color in a mapped integer.

+

This value is not needed for normal pygame usage.

+
+ +
+
+set_masks()
+
+
set the bitmasks needed to convert between a color and a mapped integer
+
set_masks((r,g,b,a)) -> None
+
+

This is not needed for normal pygame usage.

+
+

Note

+

Starting in pygame 2.0, the masks are read-only and +accordingly this method will raise a TypeError if called.

+
+
+

Deprecated since pygame 2.0.0.

+
+
+

New in pygame 1.8.1.

+
+
+ +
+
+get_shifts()
+
+
the bit shifts needed to convert between a color and a mapped integer
+
get_shifts() -> (R, G, B, A)
+
+

Returns the pixel shifts need to convert between each color and a mapped +integer.

+

This value is not needed for normal pygame usage.

+
+ +
+
+set_shifts()
+
+
sets the bit shifts needed to convert between a color and a mapped integer
+
set_shifts((r,g,b,a)) -> None
+
+

This is not needed for normal pygame usage.

+
+

Note

+

Starting in pygame 2.0, the shifts are read-only and +accordingly this method will raise a TypeError if called.

+
+
+

Deprecated since pygame 2.0.0.

+
+
+

New in pygame 1.8.1.

+
+
+ +
+
+get_losses()
+
+
the significant bits used to convert between a color and a mapped integer
+
get_losses() -> (R, G, B, A)
+
+

Return the least significant number of bits stripped from each color in a +mapped integer.

+

This value is not needed for normal pygame usage.

+
+ +
+
+get_bounding_rect()
+
+
find the smallest rect containing data
+
get_bounding_rect(min_alpha = 1) -> Rect
+
+

Returns the smallest rectangular region that contains all the pixels in +the surface that have an alpha value greater than or equal to the minimum +alpha value.

+

This function will temporarily lock and unlock the Surface as needed.

+
+

New in pygame 1.8.

+
+
+ +
+
+get_view()
+
+
return a buffer view of the Surface's pixels.
+
get_view(<kind>='2') -> BufferProxy
+
+

Return an object which exports a surface's internal pixel buffer as +a C level array struct, Python level array interface or a C level +buffer interface. The new buffer protocol is supported.

+

The kind argument is the length 1 string '0', '1', '2', '3', +'r', 'g', 'b', or 'a'. The letters are case insensitive; +'A' will work as well. The argument can be either a Unicode or byte (char) +string. The default is '2'.

+

'0' returns a contiguous unstructured bytes view. No surface shape +information is given. A ValueError is raised if the surface's pixels +are discontinuous.

+

'1' returns a (surface-width * surface-height) array of continuous +pixels. A ValueError is raised if the surface pixels are +discontinuous.

+

'2' returns a (surface-width, surface-height) array of raw pixels. +The pixels are surface-bytesize-d unsigned integers. The pixel format is +surface specific. The 3 byte unsigned integers of 24 bit surfaces are +unlikely accepted by anything other than other pygame functions.

+

'3' returns a (surface-width, surface-height, 3) array of RGB color +components. Each of the red, green, and blue components are unsigned +bytes. Only 24-bit and 32-bit surfaces are supported. The color +components must be in either RGB or BGR order within the pixel.

+

'r' for red, 'g' for green, 'b' for blue, and 'a' for alpha return a +(surface-width, surface-height) view of a single color component within a +surface: a color plane. Color components are unsigned bytes. Both 24-bit +and 32-bit surfaces support 'r', 'g', and 'b'. Only 32-bit surfaces with +SRCALPHA support 'a'.

+

The surface is locked only when an exposed interface is accessed. +For new buffer interface accesses, the surface is unlocked once the +last buffer view is released. For array interface and old buffer +interface accesses, the surface remains locked until the BufferProxy +object is released.

+
+

New in pygame 1.9.2.

+
+
+ +
+
+get_buffer()
+
+
acquires a buffer object for the pixels of the Surface.
+
get_buffer() -> BufferProxy
+
+

Return a buffer object for the pixels of the Surface. The buffer can be +used for direct pixel access and manipulation. Surface pixel data is +represented as an unstructured block of memory, with a start address +and length in bytes. The data need not be contiguous. Any gaps are +included in the length, but otherwise ignored.

+

This method implicitly locks the Surface. The lock will be released when +the returned pygame.BufferProxypygame object to export a surface buffer through an array protocol object is garbage collected.

+
+

New in pygame 1.8.

+
+
+ +
+
+_pixels_address
+
+
pixel buffer address
+
_pixels_address -> int
+
+

The starting address of the surface's raw pixel bytes.

+
+

New in pygame 1.9.2.

+
+
+ +
+
+premul_alpha()
+
+
returns a copy of the surface with the RGB channels pre-multiplied by the alpha channel.
+
premul_alpha() -> Surface
+
+

Experimental: feature still in development available for testing and feedback. It may change. +Please leave premul_alpha feedback with authors

+

Returns a copy of the initial surface with the red, green and blue color channels multiplied +by the alpha channel. This is intended to make it easier to work with the BLEND_PREMULTIPLED +blend mode flag of the blit() method. Surfaces which have called this method will only look +correct after blitting if the BLEND_PREMULTIPLED special flag is used.

+

It is worth noting that after calling this method, methods that return the colour of a pixel +such as get_at() will return the alpha multiplied colour values. It is not possible to fully +reverse an alpha multiplication of the colours in a surface as integer colour channel data +is generally reduced by the operation (e.g. 255 x 0 = 0, from there it is not possible to reconstruct +the original 255 from just the two remaining zeros in the colour and alpha channels).

+

If you call this method, and then call it again, it will multiply the colour channels by the alpha channel +twice. There are many possible ways to obtain a surface with the colour channels pre-multiplied by the +alpha channel in pygame, and it is not possible to tell the difference just from the information in the pixels. +It is completely possible to have two identical surfaces - one intended for pre-multiplied alpha blending and +one intended for normal blending. For this reason we do not store state on surfaces intended for pre-multiplied +alpha blending.

+

Surfaces without an alpha channel cannot use this method and will return an error if you use +it on them. It is best used on 32 bit surfaces (the default on most platforms) as the blitting +on these surfaces can be accelerated by SIMD versions of the pre-multiplied blitter.

+

In general pre-multiplied alpha blitting is faster then 'straight alpha' blitting and produces +superior results when blitting an alpha surface onto another surface with alpha - assuming both +surfaces contain pre-multiplied alpha colours.

+
+

New in pygame 2.2.0.

+
+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/surfarray.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/surfarray.html new file mode 100644 index 00000000..9a0d81f3 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/surfarray.html @@ -0,0 +1,569 @@ + + + + + + + + + pygame.surfarray — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.surfarray
+
+
pygame module for accessing surface pixel data using array interfaces
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Copy pixels into a 2d array
+Reference pixels into a 2d array
+Copy pixels into a 3d array
+Reference pixels into a 3d array
+Copy pixel alphas into a 2d array
+Reference pixel alphas into a 2d array
+Copy red pixels into a 2d array
+Reference pixel red into a 2d array.
+Copy green pixels into a 2d array
+Reference pixel green into a 2d array.
+Copy blue pixels into a 2d array
+Reference pixel blue into a 2d array.
+Copy the colorkey values into a 2d array
+Copy an array to a new surface
+Blit directly from a array values
+Map a 3d array into a 2d array
+Sets the array system to be used for surface arrays
+Gets the currently active array type.
+Gets the array system types currently supported.
+

Functions to convert between NumPy arrays and Surface objects. This module +will only be functional when pygame can use the external NumPy package. +If NumPy can't be imported, surfarray becomes a MissingModule object.

+

Every pixel is stored as a single integer value to represent the red, green, +and blue colors. The 8-bit images use a value that looks into a colormap. Pixels +with higher depth use a bit packing process to place three or four values into +a single number.

+

The arrays are indexed by the X axis first, followed by the Y axis. +Arrays that treat the pixels as a single integer are referred to as 2D arrays. +This module can also separate the red, green, and blue color values into +separate indices. These types of arrays are referred to as 3D arrays, and the +last index is 0 for red, 1 for green, and 2 for blue.

+

The pixels of a 2D array as returned by array2d() and pixels2d() +are mapped to the specific surface. Use pygame.Surface.unmap_rgb()convert a mapped integer color value into a Color +to convert to a color, and pygame.Surface.map_rgb()convert a color into a mapped color value to get the surface +specific pixel value of a color. Integer pixel values can only be used directly +between surfaces with matching pixel layouts (see pygame.Surfacepygame object for representing images).

+

All functions that refer to "array" will copy the surface information to a new +numpy array. All functions that refer to "pixels" will directly reference the +pixels from the surface and any changes performed to the array will make changes +in the surface. As this last functions share memory with the surface, this one +will be locked during the lifetime of the array.

+
+
+pygame.surfarray.array2d()
+
+
Copy pixels into a 2d array
+
array2d(Surface) -> array
+
+

Copy the mapped (raw) pixels from a Surface +into a 2D array. +The bit depth of the surface will control the size of the integer values, +and will work for any type of pixel format.

+

This function will temporarily lock the Surface as pixels are copied +(see the pygame.Surface.lock()lock the Surface memory for pixel access - lock the Surface memory for pixel +access method).

+
+ +
+
+pygame.surfarray.pixels2d()
+
+
Reference pixels into a 2d array
+
pixels2d(Surface) -> array
+
+

Create a new 2D array that directly references the pixel values in a +Surface. Any changes to the array will affect the pixels in the Surface. +This is a fast operation since no data is copied.

+

Pixels from a 24-bit Surface cannot be referenced, but all other Surface bit +depths can.

+

The Surface this references will remain locked for the lifetime of the array, +since the array generated by this function shares memory with the surface. +See the pygame.Surface.lock()lock the Surface memory for pixel access - lock the Surface memory for pixel +access method.

+
+ +
+
+pygame.surfarray.array3d()
+
+
Copy pixels into a 3d array
+
array3d(Surface) -> array
+
+

Copy the pixels from a Surface into a 3D array. The bit depth of the surface +will control the size of the integer values, and will work for any type of +pixel format.

+

This function will temporarily lock the Surface as pixels are copied (see +the pygame.Surface.lock()lock the Surface memory for pixel access - lock the Surface memory for pixel +access method).

+
+ +
+
+pygame.surfarray.pixels3d()
+
+
Reference pixels into a 3d array
+
pixels3d(Surface) -> array
+
+

Create a new 3D array that directly references the pixel values in a +Surface. Any changes to the array will affect the pixels in the Surface. +This is a fast operation since no data is copied.

+

This will only work on Surfaces that have 24-bit or 32-bit formats. Lower +pixel formats cannot be referenced.

+

The Surface this references will remain locked for the lifetime of the array, +since the array generated by this function shares memory with the surface. +See the pygame.Surface.lock()lock the Surface memory for pixel access - lock the Surface memory for pixel +access method.

+
+ +
+
+pygame.surfarray.array_alpha()
+
+
Copy pixel alphas into a 2d array
+
array_alpha(Surface) -> array
+
+

Copy the pixel alpha values (degree of transparency) from a Surface into a +2D array. This will work for any type of Surface format. Surfaces without a +pixel alpha will return an array with all opaque values.

+

This function will temporarily lock the Surface as pixels are copied (see +the pygame.Surface.lock()lock the Surface memory for pixel access - lock the Surface memory for pixel +access method).

+
+ +
+
+pygame.surfarray.pixels_alpha()
+
+
Reference pixel alphas into a 2d array
+
pixels_alpha(Surface) -> array
+
+

Create a new 2D array that directly references the alpha values (degree of +transparency) in a Surface. Any changes to the array will affect the pixels +in the Surface. This is a fast operation since no data is copied.

+

This can only work on 32-bit Surfaces with a per-pixel alpha value.

+

The Surface this references will remain locked for the lifetime of the array, +since the array generated by this function shares memory with the surface. +See the pygame.Surface.lock()lock the Surface memory for pixel access - lock the Surface memory for pixel +access method.

+
+ +
+
+pygame.surfarray.array_red()
+
+
Copy red pixels into a 2d array
+
array_red(Surface) -> array
+
+

Copy the pixel red values from a Surface into a 2D array. This will work +for any type of Surface format.

+

This function will temporarily lock the Surface as pixels are copied (see +the pygame.Surface.lock()lock the Surface memory for pixel access - lock the Surface memory for pixel +access method).

+
+

New in pygame 2.0.2.

+
+
+ +
+
+pygame.surfarray.pixels_red()
+
+
Reference pixel red into a 2d array.
+
pixels_red (Surface) -> array
+
+

Create a new 2D array that directly references the red values in a Surface. +Any changes to the array will affect the pixels in the Surface. This is a +fast operation since no data is copied.

+

This can only work on 24-bit or 32-bit Surfaces.

+

The Surface this references will remain locked for the lifetime of the array, +since the array generated by this function shares memory with the surface. +See the pygame.Surface.lock()lock the Surface memory for pixel access - lock the Surface memory for pixel +access method.

+
+ +
+
+pygame.surfarray.array_green()
+
+
Copy green pixels into a 2d array
+
array_green(Surface) -> array
+
+

Copy the pixel green values from a Surface into a 2D array. This will work +for any type of Surface format.

+

This function will temporarily lock the Surface as pixels are copied (see +the pygame.Surface.lock()lock the Surface memory for pixel access - lock the Surface memory for pixel +access method).

+
+

New in pygame 2.0.2.

+
+
+ +
+
+pygame.surfarray.pixels_green()
+
+
Reference pixel green into a 2d array.
+
pixels_green (Surface) -> array
+
+

Create a new 2D array that directly references the green values in a +Surface. Any changes to the array will affect the pixels in the Surface. +This is a fast operation since no data is copied.

+

This can only work on 24-bit or 32-bit Surfaces.

+

The Surface this references will remain locked for the lifetime of the array, +since the array generated by this function shares memory with the surface. +See the pygame.Surface.lock()lock the Surface memory for pixel access - lock the Surface memory for pixel +access method.

+
+ +
+
+pygame.surfarray.array_blue()
+
+
Copy blue pixels into a 2d array
+
array_blue(Surface) -> array
+
+

Copy the pixel blue values from a Surface into a 2D array. This will work +for any type of Surface format.

+

This function will temporarily lock the Surface as pixels are copied (see +the pygame.Surface.lock()lock the Surface memory for pixel access - lock the Surface memory for pixel +access method).

+
+

New in pygame 2.0.2.

+
+
+ +
+
+pygame.surfarray.pixels_blue()
+
+
Reference pixel blue into a 2d array.
+
pixels_blue (Surface) -> array
+
+

Create a new 2D array that directly references the blue values in a Surface. +Any changes to the array will affect the pixels in the Surface. This is a +fast operation since no data is copied.

+

This can only work on 24-bit or 32-bit Surfaces.

+

The Surface this references will remain locked for the lifetime of the array, +since the array generated by this function shares memory with the surface. +See the pygame.Surface.lock()lock the Surface memory for pixel access - lock the Surface memory for pixel +access method.

+
+ +
+
+pygame.surfarray.array_colorkey()
+
+
Copy the colorkey values into a 2d array
+
array_colorkey(Surface) -> array
+
+

Create a new array with the colorkey transparency value from each pixel. If +the pixel matches the colorkey it will be fully transparent; otherwise it +will be fully opaque.

+

This will work on any type of Surface format. If the image has no colorkey a +solid opaque array will be returned.

+

This function will temporarily lock the Surface as pixels are copied.

+
+ +
+
+pygame.surfarray.make_surface()
+
+
Copy an array to a new surface
+
make_surface(array) -> Surface
+
+

Create a new Surface that best resembles the data and format on the array. +The array can be 2D or 3D with any sized integer values. Function +make_surface uses the array struct interface to acquire array properties, +so is not limited to just NumPy arrays. See pygame.pixelcopypygame module for general pixel array copying.

+

New in pygame 1.9.2: array struct interface support.

+
+ +
+
+pygame.surfarray.blit_array()
+
+
Blit directly from a array values
+
blit_array(Surface, array) -> None
+
+

Directly copy values from an array into a Surface. This is faster than +converting the array into a Surface and blitting. The array must be the same +dimensions as the Surface and will completely replace all pixel values. Only +integer, ASCII character and record arrays are accepted.

+

This function will temporarily lock the Surface as the new values are +copied.

+
+ +
+
+pygame.surfarray.map_array()
+
+
Map a 3d array into a 2d array
+
map_array(Surface, array3d) -> array2d
+
+

Convert a 3D array into a 2D array. This will use the given Surface format +to control the conversion. Palette surface formats are supported for NumPy +arrays.

+
+ +
+
+pygame.surfarray.use_arraytype()
+
+
Sets the array system to be used for surface arrays
+
use_arraytype (arraytype) -> None
+
+

DEPRECATED: Uses the requested array type for the module functions. +The only supported arraytype is 'numpy'. Other values will raise +ValueError. Using this function will raise a DeprecationWarning.

+
+ +
+
+pygame.surfarray.get_arraytype()
+
+
Gets the currently active array type.
+
get_arraytype () -> str
+
+

DEPRECATED: Returns the currently active array type. This will be a value of the +get_arraytypes() tuple and indicates which type of array module is used +for the array creation. Using this function will raise a DeprecationWarning.

+
+

New in pygame 1.8.

+
+
+ +
+
+pygame.surfarray.get_arraytypes()
+
+
Gets the array system types currently supported.
+
get_arraytypes () -> tuple
+
+

DEPRECATED: Checks, which array systems are available and returns them as a tuple of +strings. The values of the tuple can be used directly in the +pygame.surfarray.use_arraytype()Sets the array system to be used for surface arrays () method. If no supported array +system could be found, None will be returned. Using this function will raise a +DeprecationWarning.

+
+

New in pygame 1.8.

+
+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/tests.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/tests.html new file mode 100644 index 00000000..418540e9 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/tests.html @@ -0,0 +1,241 @@ + + + + + + + + + pygame.tests — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.tests
+
+
Pygame unit test suite package
+
+ +++++ + + + + + + +
+Run the pygame unit test suite
+

A quick way to run the test suite package from the command line is to import +the go submodule with the Python -m option:

+
python -m pygame.tests [<test options>]
+
+
+

Command line option --help displays a usage message. Available options +correspond to the pygame.tests.run()Run the pygame unit test suite arguments.

+

The xxxx_test submodules of the tests package are unit test suites for +individual parts of pygame. Each can also be run as a main program. This is +useful if the test, such as cdrom_test, is interactive.

+

For pygame development the test suite can be run from a pygame distribution +root directory. Program run_tests.py is provided for convenience, though +test/go.py can be run directly.

+

Module level tags control which modules are included in a unit test run. Tags +are assigned to a unit test module with a corresponding <name>_tags.py module. +The tags module has the global __tags__, a list of tag names. For example, +cdrom_test.py has a tag file cdrom_tags.py containing a tags list that +has the 'interactive' string. The 'interactive' tag indicates cdrom_test.py +expects user input. It is excluded from a run_tests.py or +pygame.tests.go run.

+

Two other tags that are excluded are 'ignore' and 'subprocess_ignore'. These +two tags indicate unit tests that will not run on a particular platform, or +for which no corresponding pygame module is available.

+

The test runner will list each excluded module along with the tag responsible.

+
+
+pygame.tests.run()
+
+
Run the pygame unit test suite
+
run(*args, **kwds) -> tuple
+
+

Positional arguments (optional):

+
The names of tests to include. If omitted then all tests are run. Test names
+need not include the trailing '_test'.
+
+
+

Keyword arguments:

+
incomplete - fail incomplete tests (default False)
+nosubprocess - run all test suites in the current process
+               (default False, use separate subprocesses)
+dump - dump failures/errors as dict ready to eval (default False)
+file - if provided, the name of a file into which to dump failures/errors
+timings - if provided, the number of times to run each individual test to
+          get an average run time (default is run each test once)
+exclude - A list of TAG names to exclude from the run
+show_output - show silenced stderr/stdout on errors (default False)
+all - dump all results, not just errors (default False)
+randomize - randomize order of tests (default False)
+seed - if provided, a seed randomizer integer
+multi_thread - if provided, the number of THREADS in which to run
+               subprocessed tests
+time_out - if subprocess is True then the time limit in seconds before
+           killing a test (default 30)
+fake - if provided, the name of the fake tests package in the
+       run_tests__tests subpackage to run instead of the normal
+       pygame tests
+python - the path to a python executable to run subprocessed tests
+         (default sys.executable)
+
+
+

Return value:

+
A tuple of total number of tests run, dictionary of error information.
+The dictionary is empty if no errors were recorded.
+
+
+

By default individual test modules are run in separate subprocesses. This +recreates normal pygame usage where pygame.init() and pygame.quit() +are called only once per program execution, and avoids unfortunate +interactions between test modules.

+

A time limit is placed on test execution ensuring that any frozen tests +processes are killed when their time allotment is expired. Use the single +process option if threading is not working properly or if tests are taking +too long. It is not guaranteed that all tests will pass in single process +mode.

+

Tests are run in a randomized order if the randomize argument is True or a +seed argument is provided. If no seed integer is provided then the system +time is used for the randomization seed value.

+

Individual test modules may have a __tags__ attribute, a list of tag strings +used to selectively omit modules from a run. By default only 'interactive' +modules such as cdrom_test are ignored. An interactive module must be run +from the console as a Python program.

+

This function can only be called once per Python session. It is not +reentrant.

+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/time.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/time.html new file mode 100644 index 00000000..f5ab1567 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/time.html @@ -0,0 +1,368 @@ + + + + + + + + + pygame.time — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.time
+
+
pygame module for monitoring time
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + +
+get the time in milliseconds
+pause the program for an amount of time
+pause the program for an amount of time
+repeatedly create an event on the event queue
+create an object to help track time
+

Times in pygame are represented in milliseconds (1/1000 seconds). Most +platforms have a limited time resolution of around 10 milliseconds. This +resolution, in milliseconds, is given in the TIMER_RESOLUTION constant.

+
+
+pygame.time.get_ticks()
+
+
get the time in milliseconds
+
get_ticks() -> milliseconds
+
+

Return the number of milliseconds since pygame.init() was called. Before +pygame is initialized this will always be 0.

+
+ +
+
+pygame.time.wait()
+
+
pause the program for an amount of time
+
wait(milliseconds) -> time
+
+

Will pause for a given number of milliseconds. This function sleeps the +process to share the processor with other programs. A program that waits for +even a few milliseconds will consume very little processor time. It is +slightly less accurate than the pygame.time.delay() function.

+

This returns the actual number of milliseconds used.

+
+ +
+
+pygame.time.delay()
+
+
pause the program for an amount of time
+
delay(milliseconds) -> time
+
+

Will pause for a given number of milliseconds. This function will use the +processor (rather than sleeping) in order to make the delay more accurate +than pygame.time.wait().

+

This returns the actual number of milliseconds used.

+
+ +
+
+pygame.time.set_timer()
+
+
repeatedly create an event on the event queue
+
set_timer(event, millis) -> None
+
set_timer(event, millis, loops=0) -> None
+
+

Set an event to appear on the event queue every given number of milliseconds. +The first event will not appear until the amount of time has passed.

+

The event attribute can be a pygame.event.Event object or an integer +type that denotes an event.

+

loops is an integer that denotes the number of events posted. If 0 (default) +then the events will keep getting posted, unless explicitly stopped.

+

To disable the timer for such an event, call the function again with the same +event argument with millis argument set to 0.

+

It is also worth mentioning that a particular event type can only be put on a +timer once. In other words, there cannot be two timers for the same event type. +Setting an event timer for a particular event discards the old one for that +event type.

+

loops replaces the once argument, and this does not break backward +compatibility

+
+

New in pygame 2.0.0.dev3: once argument added.

+
+
+

Changed in pygame 2.0.1: event argument supports pygame.event.Event object

+
+
+

New in pygame 2.0.1: added loops argument to replace once argument

+
+
+ +
+
+pygame.time.Clock
+
+
create an object to help track time
+
Clock() -> Clock
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + +
+update the clock
+update the clock
+time used in the previous tick
+actual time used in the previous tick
+compute the clock framerate
+

Creates a new Clock object that can be used to track an amount of time. The +clock also provides several functions to help control a game's framerate.

+
+
+tick()
+
+
update the clock
+
tick(framerate=0) -> milliseconds
+
+

This method should be called once per frame. It will compute how many +milliseconds have passed since the previous call.

+

If you pass the optional framerate argument the function will delay to +keep the game running slower than the given ticks per second. This can be +used to help limit the runtime speed of a game. By calling +Clock.tick(40) once per frame, the program will never run at more +than 40 frames per second.

+

Note that this function uses SDL_Delay function which is not accurate on +every platform, but does not use much CPU. Use tick_busy_loop if you want +an accurate timer, and don't mind chewing CPU.

+
+ +
+
+tick_busy_loop()
+
+
update the clock
+
tick_busy_loop(framerate=0) -> milliseconds
+
+

This method should be called once per frame. It will compute how many +milliseconds have passed since the previous call.

+

If you pass the optional framerate argument the function will delay to +keep the game running slower than the given ticks per second. This can be +used to help limit the runtime speed of a game. By calling +Clock.tick_busy_loop(40) once per frame, the program will never run at +more than 40 frames per second.

+

Note that this function uses pygame.time.delay()pause the program for an amount of time, which uses lots +of CPU in a busy loop to make sure that timing is more accurate.

+
+

New in pygame 1.8.

+
+
+ +
+
+get_time()
+
+
time used in the previous tick
+
get_time() -> milliseconds
+
+

The number of milliseconds that passed between the previous two calls to +Clock.tick().

+
+ +
+
+get_rawtime()
+
+
actual time used in the previous tick
+
get_rawtime() -> milliseconds
+
+

Similar to Clock.get_time(), but does not include any time used +while Clock.tick() was delaying to limit the framerate.

+
+ +
+
+get_fps()
+
+
compute the clock framerate
+
get_fps() -> float
+
+

Compute your game's framerate (in frames per second). It is computed by +averaging the last ten calls to Clock.tick().

+
+ +
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/touch.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/touch.html new file mode 100644 index 00000000..6b1d53a4 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/touch.html @@ -0,0 +1,238 @@ + + + + + + + + + pygame._sdl2.touch — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame._sdl2.touch
+
+
pygame module to work with touch input
+
+ +++++ + + + + + + + + + + + + + + + + + + +
+get the number of touch devices
+get the a touch device id for a given index
+the number of active fingers for a given touch device
+get information about an active finger
+
+

New in pygame 2: This module requires SDL2.

+
+
+
+pygame._sdl2.touch.get_num_devices()
+
+
get the number of touch devices
+
get_num_devices() -> int
+
+

Return the number of available touch devices.

+
+ +
+
+pygame._sdl2.touch.get_device()
+
+
get the a touch device id for a given index
+
get_device(index) -> touchid
+
+
+
Parameters
+

index (int) -- This number is at least 0 and less than the +number of devices.

+
+
+

Return an integer id associated with the given index.

+
+ +
+
+pygame._sdl2.touch.get_num_fingers()
+
+
the number of active fingers for a given touch device
+
get_num_fingers(touchid) -> int
+
+

Return the number of fingers active for the touch device +whose id is touchid.

+
+ +
+
+pygame._sdl2.touch.get_finger()
+
+
get information about an active finger
+
get_finger(touchid, index) -> int
+
+
+
Parameters
+
    +
  • touchid (int) -- The touch device id.

  • +
  • index (int) -- The index of the finger to return +information about, between 0 and the +number of active fingers.

  • +
+
+
+

Return a dict for the finger index active on touchid. +The dict contains these keys:

+
id         the id of the finger (an integer).
+x          the normalized x position of the finger, between 0 and 1.
+y          the normalized y position of the finger, between 0 and 1.
+pressure   the amount of pressure applied by the finger, between 0 and 1.
+
+
+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/ref/transform.html b/.venv/Lib/site-packages/pygame/docs/generated/ref/transform.html new file mode 100644 index 00000000..5608b58a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/ref/transform.html @@ -0,0 +1,605 @@ + + + + + + + + + pygame.transform — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+
+pygame.transform
+
+
pygame module to transform surfaces
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+flip vertically and horizontally
+resize to new resolution
+resize to new resolution, using scalar(s)
+rotate an image
+filtered scale and rotation
+specialized image doubler
+scale a surface to an arbitrary size smoothly
+resize to new resolution, using scalar(s)
+return smoothscale filter version in use: 'GENERIC', 'MMX', or 'SSE'
+set smoothscale filter version to one of: 'GENERIC', 'MMX', or 'SSE'
+gets a copy of an image with an interior area removed
+find edges in a surface
+find the average surface from many surfaces.
+finds the average color of a surface
+grayscale a surface
+finds which, and how many pixels in a surface are within a threshold of a 'search_color' or a 'search_surf'.
+

A Surface transform is an operation that moves or resizes the pixels. All these +functions take a Surface to operate on and return a new Surface with the +results.

+

Some of the transforms are considered destructive. These means every time they +are performed they lose pixel data. Common examples of this are resizing and +rotating. For this reason, it is better to re-transform the original surface +than to keep transforming an image multiple times. (For example, suppose you +are animating a bouncing spring which expands and contracts. If you applied the +size changes incrementally to the previous images, you would lose detail. +Instead, always begin with the original image and scale to the desired size.)

+
+

Changed in pygame 2.0.2: transform functions now support keyword arguments.

+
+
+
+pygame.transform.flip()
+
+
flip vertically and horizontally
+
flip(surface, flip_x, flip_y) -> Surface
+
+

This can flip a Surface either vertically, horizontally, or both. +The arguments flip_x and flip_y are booleans that control whether +to flip each axis. Flipping a Surface is non-destructive and returns a new +Surface with the same dimensions.

+
+ +
+
+pygame.transform.scale()
+
+
resize to new resolution
+
scale(surface, size, dest_surface=None) -> Surface
+
+

Resizes the Surface to a new size, given as (width, height). +This is a fast scale operation that does not sample the results.

+

An optional destination surface can be used, rather than have it create a +new one. This is quicker if you want to repeatedly scale something. However +the destination must be the same size as the size (width, height) passed in. Also +the destination surface must be the same format.

+
+ +
+
+pygame.transform.scale_by()
+
+
resize to new resolution, using scalar(s)
+
scale_by(surface, factor, dest_surface=None) -> Surface
+
+

Experimental: feature still in development available for testing and feedback. It may change. +Please leave scale_by feedback with authors

+

Same as scale(), but scales by some factor, rather than taking +the new size explicitly. For example, transform.scale_by(surf, 3) +will triple the size of the surface in both dimensions. Optionally, the +scale factor can be a sequence of two numbers, controlling x and y scaling +separately. For example, transform.scale_by(surf, (2, 1)) doubles +the image width but keeps the height the same.

+
+

New in pygame 2.1.3.

+
+
+ +
+
+pygame.transform.rotate()
+
+
rotate an image
+
rotate(surface, angle) -> Surface
+
+

Unfiltered counterclockwise rotation. The angle argument represents degrees +and can be any floating point value. Negative angle amounts will rotate +clockwise.

+

Unless rotating by 90 degree increments, the image will be padded larger to +hold the new size. If the image has pixel alphas, the padded area will be +transparent. Otherwise pygame will pick a color that matches the Surface +colorkey or the topleft pixel value.

+
+ +
+
+pygame.transform.rotozoom()
+
+
filtered scale and rotation
+
rotozoom(surface, angle, scale) -> Surface
+
+

This is a combined scale and rotation transform. The resulting Surface will +be a filtered 32-bit Surface. The scale argument is a floating point value +that will be multiplied by the current resolution. The angle argument is a +floating point value that represents the counterclockwise degrees to rotate. +A negative rotation angle will rotate clockwise.

+
+ +
+
+pygame.transform.scale2x()
+
+
specialized image doubler
+
scale2x(surface, dest_surface=None) -> Surface
+
+

This will return a new image that is double the size of the original. It +uses the AdvanceMAME Scale2X algorithm which does a 'jaggie-less' scale of +bitmap graphics.

+

This really only has an effect on simple images with solid colors. On +photographic and antialiased images it will look like a regular unfiltered +scale.

+

An optional destination surface can be used, rather than have it create a +new one. This is quicker if you want to repeatedly scale something. However +the destination must be twice the size of the source surface passed in. Also +the destination surface must be the same format.

+
+ +
+
+pygame.transform.smoothscale()
+
+
scale a surface to an arbitrary size smoothly
+
smoothscale(surface, size, dest_surface=None) -> Surface
+
+

Uses one of two different algorithms for scaling each dimension of the input +surface as required. For shrinkage, the output pixels are area averages of +the colors they cover. For expansion, a bilinear filter is used. For the +x86-64 and i686 architectures, optimized MMX routines are included and +will run much faster than other machine types. The size is a 2 number +sequence for (width, height). This function only works for 24-bit or 32-bit +surfaces. An exception will be thrown if the input surface bit depth is less +than 24.

+
+

New in pygame 1.8.

+
+
+ +
+
+pygame.transform.smoothscale_by()
+
+
resize to new resolution, using scalar(s)
+
smoothscale_by(surface, factor, dest_surface=None) -> Surface
+
+

Experimental: feature still in development available for testing and feedback. It may change. +Please leave smoothscale_by feedback with authors

+

Same as smoothscale(), but scales by some factor, rather than +taking the new size explicitly. For example, +transform.smoothscale_by(surf, 3) will triple the size of the +surface in both dimensions. Optionally, the scale factor can be a sequence +of two numbers, controlling x and y scaling separately. For example, +transform.smoothscale_by(surf, (2, 1)) doubles the image width but +keeps the height the same.

+
+

New in pygame 2.1.3.

+
+
+ +
+
+pygame.transform.get_smoothscale_backend()
+
+
return smoothscale filter version in use: 'GENERIC', 'MMX', or 'SSE'
+
get_smoothscale_backend() -> string
+
+

Shows whether or not smoothscale is using MMX or SSE acceleration. +If no acceleration is available then "GENERIC" is returned. For a x86 +processor the level of acceleration to use is determined at runtime.

+

This function is provided for pygame testing and debugging.

+
+ +
+
+pygame.transform.set_smoothscale_backend()
+
+
set smoothscale filter version to one of: 'GENERIC', 'MMX', or 'SSE'
+
set_smoothscale_backend(backend) -> None
+
+

Sets smoothscale acceleration. Takes a string argument. A value of 'GENERIC' +turns off acceleration. 'MMX' uses MMX instructions only. 'SSE' allows +SSE extensions as well. A value error is raised if type is not +recognized or not supported by the current processor.

+

This function is provided for pygame testing and debugging. If smoothscale +causes an invalid instruction error then it is a pygame/SDL bug that should +be reported. Use this function as a temporary fix only.

+
+ +
+
+pygame.transform.chop()
+
+
gets a copy of an image with an interior area removed
+
chop(surface, rect) -> Surface
+
+

Extracts a portion of an image. All vertical and horizontal pixels +surrounding the given rectangle area are removed. The corner areas (diagonal +to the rect) are then brought together. (The original image is not altered +by this operation.)

+

NOTE: If you want a "crop" that returns the part of an image within a +rect, you can blit with a rect to a new surface or copy a subsurface.

+
+ +
+
+pygame.transform.laplacian()
+
+
find edges in a surface
+
laplacian(surface, dest_surface=None) -> Surface
+
+

Finds the edges in a surface using the laplacian algorithm.

+
+

New in pygame 1.8.

+
+
+ +
+
+pygame.transform.average_surfaces()
+
+
find the average surface from many surfaces.
+
average_surfaces(surfaces, dest_surface=None, palette_colors=1) -> Surface
+
+

Takes a sequence of surfaces and returns a surface with average colors from +each of the surfaces.

+

palette_colors - if true we average the colors in palette, otherwise we +average the pixel values. This is useful if the surface is actually +greyscale colors, and not palette colors.

+

Note, this function currently does not handle palette using surfaces +correctly.

+
+

New in pygame 1.8.

+
+
+

New in pygame 1.9: palette_colors argument

+
+
+ +
+
+pygame.transform.average_color()
+
+
finds the average color of a surface
+
average_color(surface, rect=None, consider_alpha=False) -> Color
+
+

Finds the average color of a Surface or a region of a surface specified by a +Rect, and returns it as a Color. If consider_alpha is set to True, then alpha is +taken into account (removing the black artifacts).

+
+

New in pygame 2.1.2: consider_alpha argument

+
+
+ +
+
+pygame.transform.grayscale()
+
+
grayscale a surface
+
grayscale(surface, dest_surface=None) -> Surface
+
+

Returns a grayscaled version of the original surface using the luminosity formula which weights red, green and blue according to their wavelengths.

+

An optional destination surface can be passed which is faster than creating a new Surface. +This destination surface must have the same dimensions (width, height) and depth as the source Surface.

+
+ +
+
+pygame.transform.threshold()
+
+
finds which, and how many pixels in a surface are within a threshold of a 'search_color' or a 'search_surf'.
+
threshold(dest_surface, surface, search_color, threshold=(0,0,0,0), set_color=(0,0,0,0), set_behavior=1, search_surf=None, inverse_set=False) -> num_threshold_pixels
+
+

This versatile function can be used for find colors in a 'surf' close to a 'search_color' +or close to colors in a separate 'search_surf'.

+

It can also be used to transfer pixels into a 'dest_surf' that match or don't match.

+

By default it sets pixels in the 'dest_surf' where all of the pixels NOT within the +threshold are changed to set_color. If inverse_set is optionally set to True, +the pixels that ARE within the threshold are changed to set_color.

+

If the optional 'search_surf' surface is given, it is used to threshold against +rather than the specified 'set_color'. That is, it will find each pixel in the +'surf' that is within the 'threshold' of the pixel at the same coordinates +of the 'search_surf'.

+
+
Parameters
+
+
+
Return type
+

int

+
+
Returns
+

The number of pixels that are within the 'threshold' in 'surf' +compared to either 'search_color' or search_surf.

+
+
Examples
+

+
+

See the threshold tests for a full of examples: https://github.com/pygame/pygame/blob/main/test/transform_test.py

+
    def test_threshold_dest_surf_not_change(self):
+        """the pixels within the threshold.
+
+        All pixels not within threshold are changed to set_color.
+        So there should be none changed in this test.
+        """
+        (w, h) = size = (32, 32)
+        threshold = (20, 20, 20, 20)
+        original_color = (25, 25, 25, 25)
+        original_dest_color = (65, 65, 65, 55)
+        threshold_color = (10, 10, 10, 10)
+        set_color = (255, 10, 10, 10)
+
+        surf = pygame.Surface(size, pygame.SRCALPHA, 32)
+        dest_surf = pygame.Surface(size, pygame.SRCALPHA, 32)
+        search_surf = pygame.Surface(size, pygame.SRCALPHA, 32)
+
+        surf.fill(original_color)
+        search_surf.fill(threshold_color)
+        dest_surf.fill(original_dest_color)
+
+        # set_behavior=1, set dest_surface from set_color.
+        # all within threshold of third_surface, so no color is set.
+
+        THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR = 1
+        pixels_within_threshold = pygame.transform.threshold(
+            dest_surface=dest_surf,
+            surface=surf,
+            search_color=None,
+            threshold=threshold,
+            set_color=set_color,
+            set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR,
+            search_surf=search_surf,
+        )
+
+        # # Return, of pixels within threshold is correct
+        self.assertEqual(w * h, pixels_within_threshold)
+
+        # # Size of dest surface is correct
+        dest_rect = dest_surf.get_rect()
+        dest_size = dest_rect.size
+        self.assertEqual(size, dest_size)
+
+        # The color is not the change_color specified for every pixel As all
+        # pixels are within threshold
+
+        for pt in test_utils.rect_area_pts(dest_rect):
+            self.assertNotEqual(dest_surf.get_at(pt), set_color)
+            self.assertEqual(dest_surf.get_at(pt), original_dest_color)
+
+
+
+

New in pygame 1.8.

+
+
+

Changed in pygame 1.9.4: Fixed a lot of bugs and added keyword arguments. Test your code.

+
+
+ +
+ +
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/search.html b/.venv/Lib/site-packages/pygame/docs/generated/search.html new file mode 100644 index 00000000..d55284be --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/search.html @@ -0,0 +1,96 @@ + + + + + + + + Search — pygame v2.5.2 documentation + + + + + + + + + + + + + + + + + + +
+
+
+ +

Search

+ + + + +

+ Searching for multiple words only shows matches that contain + all words. +

+ + +
+ + + +
+ + + +
+ +
+ + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/searchindex.js b/.venv/Lib/site-packages/pygame/docs/generated/searchindex.js new file mode 100644 index 00000000..89dcb279 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({docnames:["c_api","c_api/base","c_api/bufferproxy","c_api/color","c_api/display","c_api/event","c_api/freetype","c_api/mixer","c_api/rect","c_api/rwobject","c_api/slots","c_api/surface","c_api/surflock","c_api/version","filepaths","index","logos","ref/bufferproxy","ref/camera","ref/cdrom","ref/color","ref/color_list","ref/cursors","ref/display","ref/draw","ref/event","ref/examples","ref/fastevent","ref/font","ref/freetype","ref/gfxdraw","ref/image","ref/joystick","ref/key","ref/locals","ref/mask","ref/math","ref/midi","ref/mixer","ref/mouse","ref/music","ref/overlay","ref/pixelarray","ref/pixelcopy","ref/pygame","ref/rect","ref/scrap","ref/sdl2_controller","ref/sdl2_video","ref/sndarray","ref/sprite","ref/surface","ref/surfarray","ref/tests","ref/time","ref/touch","ref/transform","tut/CameraIntro","tut/ChimpLineByLine","tut/DisplayModes","tut/ImportInit","tut/MakeGames","tut/MoveIt","tut/PygameIntro","tut/SpriteIntro","tut/SurfarrayIntro","tut/chimp.py","tut/en/Red_or_Black/1.Prolog/introduction","tut/en/Red_or_Black/2.Print_text/Basic TEMPLATE and OUTPUT","tut/en/Red_or_Black/3.Move_text/Basic PROCESS","tut/en/Red_or_Black/4.Control_text/Basic INPUT","tut/en/Red_or_Black/5.HP_bar/Advanced OUTPUT with Advanced PROCESS","tut/en/Red_or_Black/6.Buttons/Advanced INPUT with Advanced OUTPUT","tut/en/Red_or_Black/7.Game_board/Advanced OUTPUT and plus alpha","tut/en/Red_or_Black/8.Epilog/Epilog","tut/ko/\ube68\uac04\ube14\ub85d \uac80\uc740\ube14\ub85d/1.\ud504\ub864\ub85c\uadf8/\uc18c\uac1c","tut/ko/\ube68\uac04\ube14\ub85d \uac80\uc740\ube14\ub85d/2.\ud14d\uc2a4\ud2b8 \ucd9c\ub825/\uae30\ucd08 \ud15c\ud50c\ub9bf\uacfc \ucd9c\ub825","tut/ko/\ube68\uac04\ube14\ub85d \uac80\uc740\ube14\ub85d/3.\ud14d\uc2a4\ud2b8 \uc774\ub3d9/\uae30\ucd08 \ucc98\ub9ac","tut/ko/\ube68\uac04\ube14\ub85d \uac80\uc740\ube14\ub85d/4.\ud14d\uc2a4\ud2b8 \uc870\uc885/\uae30\ucd08 \uc785\ub825","tut/ko/\ube68\uac04\ube14\ub85d \uac80\uc740\ube14\ub85d/5.HP\ubc14/\uc2ec\ud654 \ucd9c\ub825 \uadf8\ub9ac\uace0 \uc2ec\ud654 \ucc98\ub9ac","tut/ko/\ube68\uac04\ube14\ub85d \uac80\uc740\ube14\ub85d/6.\ubc84\ud2bc\ub4e4/\uc2ec\ud654 \uc785\ub825 \uadf8\ub9ac\uace0 \uc2ec\ud654 \ucd9c\ub825","tut/ko/\ube68\uac04\ube14\ub85d \uac80\uc740\ube14\ub85d/7.\uac8c\uc784\ud310/\uc2ec\ud654 \ucd9c\ub825 \uadf8\ub9ac\uace0 \uc870\uae08 \ub354","tut/ko/\ube68\uac04\ube14\ub85d \uac80\uc740\ube14\ub85d/8.\uc5d0\ud544\ub85c\uadf8/\uc5d0\ud544\ub85c\uadf8","tut/ko/\ube68\uac04\ube14\ub85d \uac80\uc740\ube14\ub85d/\uac1c\uc694","tut/newbieguide","tut/tom_games2","tut/tom_games3","tut/tom_games4","tut/tom_games5","tut/tom_games6"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":5,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":3,"sphinx.domains.rst":2,"sphinx.domains.std":2,sphinx:56},filenames:["c_api.rst","c_api\\base.rst","c_api\\bufferproxy.rst","c_api\\color.rst","c_api\\display.rst","c_api\\event.rst","c_api\\freetype.rst","c_api\\mixer.rst","c_api\\rect.rst","c_api\\rwobject.rst","c_api\\slots.rst","c_api\\surface.rst","c_api\\surflock.rst","c_api\\version.rst","filepaths.rst","index.rst","logos.rst","ref\\bufferproxy.rst","ref\\camera.rst","ref\\cdrom.rst","ref\\color.rst","ref\\color_list.rst","ref\\cursors.rst","ref\\display.rst","ref\\draw.rst","ref\\event.rst","ref\\examples.rst","ref\\fastevent.rst","ref\\font.rst","ref\\freetype.rst","ref\\gfxdraw.rst","ref\\image.rst","ref\\joystick.rst","ref\\key.rst","ref\\locals.rst","ref\\mask.rst","ref\\math.rst","ref\\midi.rst","ref\\mixer.rst","ref\\mouse.rst","ref\\music.rst","ref\\overlay.rst","ref\\pixelarray.rst","ref\\pixelcopy.rst","ref\\pygame.rst","ref\\rect.rst","ref\\scrap.rst","ref\\sdl2_controller.rst","ref\\sdl2_video.rst","ref\\sndarray.rst","ref\\sprite.rst","ref\\surface.rst","ref\\surfarray.rst","ref\\tests.rst","ref\\time.rst","ref\\touch.rst","ref\\transform.rst","tut\\CameraIntro.rst","tut\\ChimpLineByLine.rst","tut\\DisplayModes.rst","tut\\ImportInit.rst","tut\\MakeGames.rst","tut\\MoveIt.rst","tut\\PygameIntro.rst","tut\\SpriteIntro.rst","tut\\SurfarrayIntro.rst","tut\\chimp.py.rst","tut\\en\\Red_or_Black\\1.Prolog\\introduction.rst","tut\\en\\Red_or_Black\\2.Print_text\\Basic TEMPLATE and OUTPUT.rst","tut\\en\\Red_or_Black\\3.Move_text\\Basic PROCESS.rst","tut\\en\\Red_or_Black\\4.Control_text\\Basic INPUT.rst","tut\\en\\Red_or_Black\\5.HP_bar\\Advanced OUTPUT with Advanced PROCESS.rst","tut\\en\\Red_or_Black\\6.Buttons\\Advanced INPUT with Advanced OUTPUT.rst","tut\\en\\Red_or_Black\\7.Game_board\\Advanced OUTPUT and plus alpha.rst","tut\\en\\Red_or_Black\\8.Epilog\\Epilog.rst","tut\\ko\\\ube68\uac04\ube14\ub85d \uac80\uc740\ube14\ub85d\\1.\ud504\ub864\ub85c\uadf8\\\uc18c\uac1c.rst","tut\\ko\\\ube68\uac04\ube14\ub85d \uac80\uc740\ube14\ub85d\\2.\ud14d\uc2a4\ud2b8 \ucd9c\ub825\\\uae30\ucd08 \ud15c\ud50c\ub9bf\uacfc \ucd9c\ub825.rst","tut\\ko\\\ube68\uac04\ube14\ub85d \uac80\uc740\ube14\ub85d\\3.\ud14d\uc2a4\ud2b8 \uc774\ub3d9\\\uae30\ucd08 \ucc98\ub9ac.rst","tut\\ko\\\ube68\uac04\ube14\ub85d \uac80\uc740\ube14\ub85d\\4.\ud14d\uc2a4\ud2b8 \uc870\uc885\\\uae30\ucd08 \uc785\ub825.rst","tut\\ko\\\ube68\uac04\ube14\ub85d \uac80\uc740\ube14\ub85d\\5.HP\ubc14\\\uc2ec\ud654 \ucd9c\ub825 \uadf8\ub9ac\uace0 \uc2ec\ud654 \ucc98\ub9ac.rst","tut\\ko\\\ube68\uac04\ube14\ub85d \uac80\uc740\ube14\ub85d\\6.\ubc84\ud2bc\ub4e4\\\uc2ec\ud654 \uc785\ub825 \uadf8\ub9ac\uace0 \uc2ec\ud654 \ucd9c\ub825.rst","tut\\ko\\\ube68\uac04\ube14\ub85d \uac80\uc740\ube14\ub85d\\7.\uac8c\uc784\ud310\\\uc2ec\ud654 \ucd9c\ub825 \uadf8\ub9ac\uace0 \uc870\uae08 \ub354.rst","tut\\ko\\\ube68\uac04\ube14\ub85d \uac80\uc740\ube14\ub85d\\8.\uc5d0\ud544\ub85c\uadf8\\\uc5d0\ud544\ub85c\uadf8.rst","tut\\ko\\\ube68\uac04\ube14\ub85d \uac80\uc740\ube14\ub85d\\\uac1c\uc694.rst","tut\\newbieguide.rst","tut\\tom_games2.rst","tut\\tom_games3.rst","tut\\tom_games4.rst","tut\\tom_games5.rst","tut\\tom_games6.rst"],objects:{"":[[13,0,1,"c.PG_MAJOR_VERSION","PG_MAJOR_VERSION"],[13,0,1,"c.PG_MINOR_VERSION","PG_MINOR_VERSION"],[13,0,1,"c.PG_PATCH_VERSION","PG_PATCH_VERSION"],[13,0,1,"c.PG_VERSIONNUM","PG_VERSIONNUM"],[13,0,1,"c.PG_VERSION_ATLEAST","PG_VERSION_ATLEAST"],[1,1,1,"c.import_pygame_base","import_pygame_base"],[1,1,1,"c.pgBuffer_AsArrayInterface","pgBuffer_AsArrayInterface"],[1,1,1,"c.pgBuffer_AsArrayStruct","pgBuffer_AsArrayStruct"],[1,1,1,"c.pgBuffer_Release","pgBuffer_Release"],[2,1,1,"c.pgBufproxy_Check","pgBufproxy_Check"],[2,1,1,"c.pgBufproxy_GetParent","pgBufproxy_GetParent"],[2,1,1,"c.pgBufproxy_New","pgBufproxy_New"],[2,1,1,"c.pgBufproxy_Trip","pgBufproxy_Trip"],[2,3,1,"c.pgBufproxy_Type","pgBufproxy_Type"],[7,4,1,"c.pgChannelObject","pgChannelObject"],[7,1,1,"c.pgChannel_AsInt","pgChannel_AsInt"],[7,1,1,"c.pgChannel_Check","pgChannel_Check"],[7,1,1,"c.pgChannel_New","pgChannel_New"],[7,3,1,"c.pgChannel_Type","pgChannel_Type"],[3,1,1,"c.pgColor_Check","pgColor_Check"],[3,1,1,"c.pgColor_New","pgColor_New"],[3,1,1,"c.pgColor_NewLength","pgColor_NewLength"],[3,3,1,"c.pgColor_Type","pgColor_Type"],[1,1,1,"c.pgDict_AsBuffer","pgDict_AsBuffer"],[5,4,1,"c.pgEventObject","pgEventObject"],[5,1,1,"c.pgEvent_Check","pgEvent_Check"],[5,1,1,"c.pgEvent_FillUserEvent","pgEvent_FillUserEvent"],[5,1,1,"c.pgEvent_New","pgEvent_New"],[5,1,1,"c.pgEvent_New2","pgEvent_New2"],[5,4,1,"c.pgEvent_Type","pgEvent_Type"],[1,3,1,"c.pgExc_BufferError","pgExc_BufferError"],[1,3,1,"c.pgExc_SDLError","pgExc_SDLError"],[6,4,1,"c.pgFontObject","pgFontObject"],[6,1,1,"c.pgFont_Check","pgFont_Check"],[6,1,1,"c.pgFont_IS_ALIVE","pgFont_IS_ALIVE"],[6,1,1,"c.pgFont_New","pgFont_New"],[6,4,1,"c.pgFont_Type","pgFont_Type"],[12,4,1,"c.pgLifetimeLockObject","pgLifetimeLockObject"],[12,1,1,"c.pgLifetimeLock_Check","pgLifetimeLock_Check"],[12,3,1,"c.pgLifetimeLock_Type","pgLifetimeLock_Type"],[1,1,1,"c.pgObject_GetBuffer","pgObject_GetBuffer"],[9,1,1,"c.pgRWops_FromFileObject","pgRWops_FromFileObject"],[9,1,1,"c.pgRWops_FromObject","pgRWops_FromObject"],[9,1,1,"c.pgRWops_IsFileObject","pgRWops_IsFileObject"],[9,1,1,"c.pgRWops_ReleaseObject","pgRWops_ReleaseObject"],[8,4,1,"c.pgRectObject","pgRectObject"],[8,1,1,"c.pgRect_AsRect","pgRect_AsRect"],[8,1,1,"c.pgRect_Check","pgRect_Check"],[8,1,1,"c.pgRect_FromObject","pgRect_FromObject"],[8,1,1,"c.pgRect_New","pgRect_New"],[8,1,1,"c.pgRect_New4","pgRect_New4"],[8,1,1,"c.pgRect_Normalize","pgRect_Normalize"],[8,3,1,"c.pgRect_Type","pgRect_Type"],[7,4,1,"c.pgSoundObject","pgSoundObject"],[7,1,1,"c.pgSound_AsChunk","pgSound_AsChunk"],[7,1,1,"c.pgSound_Check","pgSound_Check"],[7,1,1,"c.pgSound_New","pgSound_New"],[7,3,1,"c.pgSound_Type","pgSound_Type"],[11,4,1,"c.pgSurfaceObject","pgSurfaceObject"],[11,1,1,"c.pgSurface_AsSurface","pgSurface_AsSurface"],[11,1,1,"c.pgSurface_Blit","pgSurface_Blit"],[11,1,1,"c.pgSurface_Check","pgSurface_Check"],[12,1,1,"c.pgSurface_Lock","pgSurface_Lock"],[12,1,1,"c.pgSurface_LockBy","pgSurface_LockBy"],[12,1,1,"c.pgSurface_LockLifetime","pgSurface_LockLifetime"],[11,1,1,"c.pgSurface_New","pgSurface_New"],[11,1,1,"c.pgSurface_New2","pgSurface_New2"],[12,1,1,"c.pgSurface_Prep","pgSurface_Prep"],[11,3,1,"c.pgSurface_Type","pgSurface_Type"],[12,1,1,"c.pgSurface_UnLock","pgSurface_UnLock"],[12,1,1,"c.pgSurface_UnLockBy","pgSurface_UnLockBy"],[12,1,1,"c.pgSurface_Unprep","pgSurface_Unprep"],[4,4,1,"c.pgVidInfoObject","pgVidInfoObject"],[4,1,1,"c.pgVidInfo_AsVidInfo","pgVidInfo_AsVidInfo"],[4,1,1,"c.pgVidInfo_Check","pgVidInfo_Check"],[4,1,1,"c.pgVidInfo_New","pgVidInfo_New"],[4,3,1,"c.pgVidInfo_Type","pgVidInfo_Type"],[9,1,1,"c.pg_EncodeFilePath","pg_EncodeFilePath"],[9,1,1,"c.pg_EncodeString","pg_EncodeString"],[1,1,1,"c.pg_FloatFromObj","pg_FloatFromObj"],[1,1,1,"c.pg_FloatFromObjIndex","pg_FloatFromObjIndex"],[1,1,1,"c.pg_GetDefaultWindow","pg_GetDefaultWindow"],[1,1,1,"c.pg_GetDefaultWindowSurface","pg_GetDefaultWindowSurface"],[1,1,1,"c.pg_IntFromObj","pg_IntFromObj"],[1,1,1,"c.pg_IntFromObjIndex","pg_IntFromObjIndex"],[1,1,1,"c.pg_RGBAFromObj","pg_RGBAFromObj"],[1,1,1,"c.pg_RegisterQuit","pg_RegisterQuit"],[1,1,1,"c.pg_SetDefaultWindow","pg_SetDefaultWindow"],[1,1,1,"c.pg_SetDefaultWindowSurface","pg_SetDefaultWindowSurface"],[1,1,1,"c.pg_TwoFloatsFromObj","pg_TwoFloatsFromObj"],[1,1,1,"c.pg_TwoIntsFromObj","pg_TwoIntsFromObj"],[1,1,1,"c.pg_UintFromObj","pg_UintFromObj"],[1,1,1,"c.pg_UintFromObjIndex","pg_UintFromObjIndex"],[1,4,1,"c.pg_buffer","pg_buffer"],[1,1,1,"c.pg_mod_autoinit","pg_mod_autoinit"],[1,1,1,"c.pg_mod_autoquit","pg_mod_autoquit"],[44,5,0,"-","pygame"]],"pygame.BufferProxy":[[17,7,1,"","length"],[17,7,1,"","parent"],[17,7,1,"","raw"],[17,8,1,"","write"]],"pygame.Color":[[20,7,1,"","a"],[20,7,1,"","b"],[20,7,1,"","cmy"],[20,8,1,"","correct_gamma"],[20,7,1,"","g"],[20,8,1,"","grayscale"],[20,7,1,"","hsla"],[20,7,1,"","hsva"],[20,7,1,"","i1i2i3"],[20,8,1,"","lerp"],[20,8,1,"","normalize"],[20,8,1,"","premul_alpha"],[20,7,1,"","r"],[20,8,1,"","set_length"],[20,8,1,"","update"]],"pygame.Overlay":[[41,8,1,"","display"],[41,8,1,"","get_hardware"],[41,8,1,"","set_location"]],"pygame.PixelArray":[[42,8,1,"","close"],[42,8,1,"","compare"],[42,8,1,"","extract"],[42,7,1,"","itemsize"],[42,8,1,"","make_surface"],[42,7,1,"","ndim"],[42,8,1,"","replace"],[42,7,1,"","shape"],[42,7,1,"","strides"],[42,7,1,"","surface"],[42,8,1,"","transpose"]],"pygame.Rect":[[45,8,1,"","clamp"],[45,8,1,"","clamp_ip"],[45,8,1,"","clip"],[45,8,1,"","clipline"],[45,8,1,"","collidedict"],[45,8,1,"","collidedictall"],[45,8,1,"","collidelist"],[45,8,1,"","collidelistall"],[45,8,1,"","collideobjects"],[45,8,1,"","collideobjectsall"],[45,8,1,"","collidepoint"],[45,8,1,"","colliderect"],[45,8,1,"","contains"],[45,8,1,"","copy"],[45,8,1,"","fit"],[45,8,1,"","inflate"],[45,8,1,"","inflate_ip"],[45,8,1,"","move"],[45,8,1,"","move_ip"],[45,8,1,"","normalize"],[45,8,1,"","scale_by"],[45,8,1,"","scale_by_ip"],[45,8,1,"","union"],[45,8,1,"","union_ip"],[45,8,1,"","unionall"],[45,8,1,"","unionall_ip"],[45,8,1,"","update"]],"pygame.Surface":[[51,7,1,"","_pixels_address"],[51,8,1,"","blit"],[51,8,1,"","blits"],[51,8,1,"","convert"],[51,8,1,"","convert_alpha"],[51,8,1,"","copy"],[51,8,1,"","fill"],[51,8,1,"","get_abs_offset"],[51,8,1,"","get_abs_parent"],[51,8,1,"","get_alpha"],[51,8,1,"","get_at"],[51,8,1,"","get_at_mapped"],[51,8,1,"","get_bitsize"],[51,8,1,"","get_bounding_rect"],[51,8,1,"","get_buffer"],[51,8,1,"","get_bytesize"],[51,8,1,"","get_clip"],[51,8,1,"","get_colorkey"],[51,8,1,"","get_flags"],[51,8,1,"","get_height"],[51,8,1,"","get_locked"],[51,8,1,"","get_locks"],[51,8,1,"","get_losses"],[51,8,1,"","get_masks"],[51,8,1,"","get_offset"],[51,8,1,"","get_palette"],[51,8,1,"","get_palette_at"],[51,8,1,"","get_parent"],[51,8,1,"","get_pitch"],[51,8,1,"","get_rect"],[51,8,1,"","get_shifts"],[51,8,1,"","get_size"],[51,8,1,"","get_view"],[51,8,1,"","get_width"],[51,8,1,"","lock"],[51,8,1,"","map_rgb"],[51,8,1,"","mustlock"],[51,8,1,"","premul_alpha"],[51,8,1,"","scroll"],[51,8,1,"","set_alpha"],[51,8,1,"","set_at"],[51,8,1,"","set_clip"],[51,8,1,"","set_colorkey"],[51,8,1,"","set_masks"],[51,8,1,"","set_palette"],[51,8,1,"","set_palette_at"],[51,8,1,"","set_shifts"],[51,8,1,"","subsurface"],[51,8,1,"","unlock"],[51,8,1,"","unmap_rgb"]],"pygame._sdl2":[[47,5,0,"-","controller"],[55,5,0,"-","touch"],[48,5,0,"-","video"]],"pygame._sdl2.controller":[[47,6,1,"","Controller"],[47,9,1,"","get_count"],[47,9,1,"","get_eventstate"],[47,9,1,"","get_init"],[47,9,1,"","init"],[47,9,1,"","is_controller"],[47,9,1,"","name_forindex"],[47,9,1,"","quit"],[47,9,1,"","set_eventstate"]],"pygame._sdl2.controller.Controller":[[47,8,1,"","as_joystick"],[47,8,1,"","attached"],[47,8,1,"","from_joystick"],[47,8,1,"","get_axis"],[47,8,1,"","get_button"],[47,8,1,"","get_init"],[47,8,1,"","get_mapping"],[47,8,1,"","quit"],[47,8,1,"","rumble"],[47,8,1,"","set_mapping"],[47,8,1,"","stop_rumble"]],"pygame._sdl2.touch":[[55,9,1,"","get_device"],[55,9,1,"","get_finger"],[55,9,1,"","get_num_devices"],[55,9,1,"","get_num_fingers"]],"pygame._sdl2.video":[[48,6,1,"","Image"],[48,6,1,"","Renderer"],[48,6,1,"","Texture"],[48,6,1,"","Window"]],"pygame._sdl2.video.Image":[[48,7,1,"","alpha"],[48,7,1,"","angle"],[48,7,1,"","blend_mode"],[48,7,1,"","color"],[48,8,1,"","draw"],[48,7,1,"","flip_x"],[48,7,1,"","flip_y"],[48,8,1,"","get_rect"],[48,7,1,"","origin"],[48,7,1,"","srcrect"],[48,7,1,"","texture"]],"pygame._sdl2.video.Renderer":[[48,8,1,"","blit"],[48,8,1,"","clear"],[48,7,1,"","draw_blend_mode"],[48,7,1,"","draw_color"],[48,8,1,"","draw_line"],[48,8,1,"","draw_point"],[48,8,1,"","draw_rect"],[48,8,1,"","fill_rect"],[48,8,1,"","from_window"],[48,8,1,"","get_viewport"],[48,7,1,"","logical_size"],[48,8,1,"","present"],[48,7,1,"","scale"],[48,8,1,"","set_viewport"],[48,7,1,"","target"],[48,8,1,"","to_surface"]],"pygame._sdl2.video.Texture":[[48,7,1,"","alpha"],[48,7,1,"","blend_mode"],[48,7,1,"","color"],[48,8,1,"","draw"],[48,8,1,"","from_surface"],[48,8,1,"","get_rect"],[48,7,1,"","height"],[48,7,1,"","renderer"],[48,8,1,"","update"],[48,7,1,"","width"]],"pygame._sdl2.video.Window":[[48,7,1,"","borderless"],[48,8,1,"","destroy"],[48,7,1,"","display_index"],[48,8,1,"","focus"],[48,8,1,"","from_display_module"],[48,8,1,"","from_window"],[48,7,1,"","grab"],[48,8,1,"","hide"],[48,7,1,"","id"],[48,8,1,"","maximize"],[48,8,1,"","minimize"],[48,7,1,"","opacity"],[48,7,1,"","position"],[48,7,1,"","relative_mouse"],[48,7,1,"","resizable"],[48,8,1,"","restore"],[48,8,1,"","set_fullscreen"],[48,8,1,"","set_icon"],[48,8,1,"","set_modal_for"],[48,8,1,"","set_windowed"],[48,8,1,"","show"],[48,7,1,"","size"],[48,7,1,"","title"]],"pygame.camera":[[18,6,1,"","Camera"],[18,9,1,"","colorspace"],[18,9,1,"","get_backends"],[18,9,1,"","init"],[18,9,1,"","list_cameras"]],"pygame.camera.Camera":[[18,8,1,"","get_controls"],[18,8,1,"","get_image"],[18,8,1,"","get_raw"],[18,8,1,"","get_size"],[18,8,1,"","query_image"],[18,8,1,"","set_controls"],[18,8,1,"","start"],[18,8,1,"","stop"]],"pygame.cdrom":[[19,6,1,"","CD"],[19,9,1,"","get_count"],[19,9,1,"","get_init"],[19,9,1,"","init"],[19,9,1,"","quit"]],"pygame.cdrom.CD":[[19,8,1,"","eject"],[19,8,1,"","get_all"],[19,8,1,"","get_busy"],[19,8,1,"","get_current"],[19,8,1,"","get_empty"],[19,8,1,"","get_id"],[19,8,1,"","get_init"],[19,8,1,"","get_name"],[19,8,1,"","get_numtracks"],[19,8,1,"","get_paused"],[19,8,1,"","get_track_audio"],[19,8,1,"","get_track_length"],[19,8,1,"","get_track_start"],[19,8,1,"","init"],[19,8,1,"","pause"],[19,8,1,"","play"],[19,8,1,"","quit"],[19,8,1,"","resume"],[19,8,1,"","stop"]],"pygame.cursors":[[22,6,1,"","Cursor"],[22,9,1,"","compile"],[22,9,1,"","load_xbm"]],"pygame.cursors.Cursor":[[22,8,1,"","copy"],[22,7,1,"","data"],[22,7,1,"","type"]],"pygame.display":[[23,9,1,"","Info"],[23,9,1,"","flip"],[23,9,1,"","get_active"],[23,9,1,"","get_allow_screensaver"],[23,9,1,"","get_caption"],[23,9,1,"","get_desktop_sizes"],[23,9,1,"","get_driver"],[23,9,1,"","get_init"],[23,9,1,"","get_num_displays"],[23,9,1,"","get_surface"],[23,9,1,"","get_window_size"],[23,9,1,"","get_wm_info"],[23,9,1,"","gl_get_attribute"],[23,9,1,"","gl_set_attribute"],[23,9,1,"","iconify"],[23,9,1,"","init"],[23,9,1,"","list_modes"],[23,9,1,"","mode_ok"],[23,9,1,"","quit"],[23,9,1,"","set_allow_screensaver"],[23,9,1,"","set_caption"],[23,9,1,"","set_gamma"],[23,9,1,"","set_gamma_ramp"],[23,9,1,"","set_icon"],[23,9,1,"","set_mode"],[23,9,1,"","set_palette"],[23,9,1,"","toggle_fullscreen"],[23,9,1,"","update"]],"pygame.draw":[[24,9,1,"","aaline"],[24,9,1,"","aalines"],[24,9,1,"","arc"],[24,9,1,"","circle"],[24,9,1,"","ellipse"],[24,9,1,"","line"],[24,9,1,"","lines"],[24,9,1,"","polygon"],[24,9,1,"","rect"]],"pygame.event":[[25,6,1,"","Event"],[25,9,1,"","clear"],[25,9,1,"","custom_type"],[25,9,1,"","event_name"],[25,9,1,"","get"],[25,9,1,"","get_blocked"],[25,9,1,"","get_grab"],[25,9,1,"","get_keyboard_grab"],[25,9,1,"","peek"],[25,9,1,"","poll"],[25,9,1,"","post"],[25,9,1,"","pump"],[25,9,1,"","set_allowed"],[25,9,1,"","set_blocked"],[25,9,1,"","set_grab"],[25,9,1,"","set_keyboard_grab"],[25,9,1,"","wait"]],"pygame.event.Event":[[25,7,1,"","__dict__"],[25,7,1,"","type"]],"pygame.examples.aliens":[[26,9,1,"","main"]],"pygame.examples.arraydemo":[[26,9,1,"","main"]],"pygame.examples.blend_fill":[[26,9,1,"","main"]],"pygame.examples.blit_blends":[[26,9,1,"","main"]],"pygame.examples.camera":[[26,9,1,"","main"]],"pygame.examples.chimp":[[26,9,1,"","main"]],"pygame.examples.cursors":[[26,9,1,"","main"]],"pygame.examples.eventlist":[[26,9,1,"","main"]],"pygame.examples.fonty":[[26,9,1,"","main"]],"pygame.examples.freetype_misc":[[26,9,1,"","main"]],"pygame.examples.glcube":[[26,9,1,"","main"]],"pygame.examples.headless_no_windows_needed":[[26,9,1,"","main"]],"pygame.examples.joystick":[[26,9,1,"","main"]],"pygame.examples.liquid":[[26,9,1,"","main"]],"pygame.examples.mask":[[26,9,1,"","main"]],"pygame.examples.midi":[[26,9,1,"","main"]],"pygame.examples.moveit":[[26,9,1,"","main"]],"pygame.examples.pixelarray":[[26,9,1,"","main"]],"pygame.examples.playmus":[[26,9,1,"","main"]],"pygame.examples.scaletest":[[26,9,1,"","main"]],"pygame.examples.scrap_clipboard":[[26,9,1,"","main"]],"pygame.examples.scroll":[[26,9,1,"","main"]],"pygame.examples.sound":[[26,9,1,"","main"]],"pygame.examples.sound_array_demos":[[26,9,1,"","main"]],"pygame.examples.stars":[[26,9,1,"","main"]],"pygame.examples.testsprite":[[26,9,1,"","main"]],"pygame.examples.vgrade":[[26,9,1,"","main"]],"pygame.fastevent":[[27,9,1,"","get"],[27,9,1,"","get_init"],[27,9,1,"","init"],[27,9,1,"","poll"],[27,9,1,"","post"],[27,9,1,"","pump"],[27,9,1,"","wait"]],"pygame.font":[[28,6,1,"","Font"],[28,9,1,"","SysFont"],[28,9,1,"","get_default_font"],[28,9,1,"","get_fonts"],[28,9,1,"","get_init"],[28,9,1,"","get_sdl_ttf_version"],[28,9,1,"","init"],[28,9,1,"","match_font"],[28,9,1,"","quit"]],"pygame.font.Font":[[28,7,1,"","bold"],[28,8,1,"","get_ascent"],[28,8,1,"","get_bold"],[28,8,1,"","get_descent"],[28,8,1,"","get_height"],[28,8,1,"","get_italic"],[28,8,1,"","get_linesize"],[28,8,1,"","get_strikethrough"],[28,8,1,"","get_underline"],[28,7,1,"","italic"],[28,8,1,"","metrics"],[28,8,1,"","render"],[28,8,1,"","set_bold"],[28,8,1,"","set_italic"],[28,8,1,"","set_script"],[28,8,1,"","set_strikethrough"],[28,8,1,"","set_underline"],[28,8,1,"","size"],[28,7,1,"","strikethrough"],[28,7,1,"","underline"]],"pygame.freetype":[[29,6,1,"","Font"],[29,9,1,"","SysFont"],[29,9,1,"","get_cache_size"],[29,9,1,"","get_default_font"],[29,9,1,"","get_default_resolution"],[29,9,1,"","get_error"],[29,9,1,"","get_init"],[29,9,1,"","get_version"],[29,9,1,"","init"],[29,9,1,"","quit"],[29,9,1,"","set_default_resolution"],[29,9,1,"","was_init"]],"pygame.freetype.Font":[[29,7,1,"","antialiased"],[29,7,1,"","ascender"],[29,7,1,"","bgcolor"],[29,7,1,"","descender"],[29,7,1,"","fgcolor"],[29,7,1,"","fixed_sizes"],[29,7,1,"","fixed_width"],[29,8,1,"","get_metrics"],[29,8,1,"","get_rect"],[29,8,1,"","get_sized_ascender"],[29,8,1,"","get_sized_descender"],[29,8,1,"","get_sized_glyph_height"],[29,8,1,"","get_sized_height"],[29,8,1,"","get_sizes"],[29,7,1,"","height"],[29,7,1,"","kerning"],[29,7,1,"","name"],[29,7,1,"","oblique"],[29,7,1,"","origin"],[29,7,1,"","pad"],[29,7,1,"","path"],[29,8,1,"","render"],[29,8,1,"","render_raw"],[29,8,1,"","render_raw_to"],[29,8,1,"","render_to"],[29,7,1,"","resolution"],[29,7,1,"","rotation"],[29,7,1,"","scalable"],[29,7,1,"","size"],[29,7,1,"","strength"],[29,7,1,"","strong"],[29,7,1,"","style"],[29,7,1,"","ucs4"],[29,7,1,"","underline"],[29,7,1,"","underline_adjustment"],[29,7,1,"","use_bitmap_strikes"],[29,7,1,"","vertical"],[29,7,1,"","wide"]],"pygame.gfxdraw":[[30,9,1,"","aacircle"],[30,9,1,"","aaellipse"],[30,9,1,"","aapolygon"],[30,9,1,"","aatrigon"],[30,9,1,"","arc"],[30,9,1,"","bezier"],[30,9,1,"","box"],[30,9,1,"","circle"],[30,9,1,"","ellipse"],[30,9,1,"","filled_circle"],[30,9,1,"","filled_ellipse"],[30,9,1,"","filled_polygon"],[30,9,1,"","filled_trigon"],[30,9,1,"","hline"],[30,9,1,"","line"],[30,9,1,"","pie"],[30,9,1,"","pixel"],[30,9,1,"","polygon"],[30,9,1,"","rectangle"],[30,9,1,"","textured_polygon"],[30,9,1,"","trigon"],[30,9,1,"","vline"]],"pygame.image":[[31,9,1,"","frombuffer"],[31,9,1,"","frombytes"],[31,9,1,"","fromstring"],[31,9,1,"","get_extended"],[31,9,1,"","get_sdl_image_version"],[31,9,1,"","load"],[31,9,1,"","load_basic"],[31,9,1,"","load_extended"],[31,9,1,"","save"],[31,9,1,"","save_extended"],[31,9,1,"","tobytes"],[31,9,1,"","tostring"]],"pygame.joystick":[[32,6,1,"","Joystick"],[32,9,1,"","get_count"],[32,9,1,"","get_init"],[32,9,1,"","init"],[32,9,1,"","quit"]],"pygame.joystick.Joystick":[[32,8,1,"","get_axis"],[32,8,1,"","get_ball"],[32,8,1,"","get_button"],[32,8,1,"","get_guid"],[32,8,1,"","get_hat"],[32,8,1,"","get_id"],[32,8,1,"","get_init"],[32,8,1,"","get_instance_id"],[32,8,1,"","get_name"],[32,8,1,"","get_numaxes"],[32,8,1,"","get_numballs"],[32,8,1,"","get_numbuttons"],[32,8,1,"","get_numhats"],[32,8,1,"","get_power_level"],[32,8,1,"","init"],[32,8,1,"","quit"],[32,8,1,"","rumble"],[32,8,1,"","stop_rumble"]],"pygame.key":[[33,9,1,"","get_focused"],[33,9,1,"","get_mods"],[33,9,1,"","get_pressed"],[33,9,1,"","get_repeat"],[33,9,1,"","key_code"],[33,9,1,"","name"],[33,9,1,"","set_mods"],[33,9,1,"","set_repeat"],[33,9,1,"","set_text_input_rect"],[33,9,1,"","start_text_input"],[33,9,1,"","stop_text_input"]],"pygame.mask":[[35,6,1,"","Mask"],[35,9,1,"","from_surface"],[35,9,1,"","from_threshold"]],"pygame.mask.Mask":[[35,8,1,"","angle"],[35,8,1,"","centroid"],[35,8,1,"","clear"],[35,8,1,"","connected_component"],[35,8,1,"","connected_components"],[35,8,1,"","convolve"],[35,8,1,"","copy"],[35,8,1,"","count"],[35,8,1,"","draw"],[35,8,1,"","erase"],[35,8,1,"","fill"],[35,8,1,"","get_at"],[35,8,1,"","get_bounding_rects"],[35,8,1,"","get_rect"],[35,8,1,"","get_size"],[35,8,1,"","invert"],[35,8,1,"","outline"],[35,8,1,"","overlap"],[35,8,1,"","overlap_area"],[35,8,1,"","overlap_mask"],[35,8,1,"","scale"],[35,8,1,"","set_at"],[35,8,1,"","to_surface"]],"pygame.math":[[36,6,1,"","Vector2"],[36,6,1,"","Vector3"],[36,9,1,"","clamp"],[36,9,1,"","lerp"]],"pygame.math.Vector2":[[36,8,1,"","angle_to"],[36,8,1,"","as_polar"],[36,8,1,"","clamp_magnitude"],[36,8,1,"","clamp_magnitude_ip"],[36,8,1,"","copy"],[36,8,1,"","cross"],[36,8,1,"","distance_squared_to"],[36,8,1,"","distance_to"],[36,8,1,"","dot"],[36,8,1,"","elementwise"],[36,7,1,"","epsilon"],[36,8,1,"","from_polar"],[36,8,1,"","is_normalized"],[36,8,1,"","length"],[36,8,1,"","length_squared"],[36,8,1,"","lerp"],[36,8,1,"","magnitude"],[36,8,1,"","magnitude_squared"],[36,8,1,"","move_towards"],[36,8,1,"","move_towards_ip"],[36,8,1,"","normalize"],[36,8,1,"","normalize_ip"],[36,8,1,"","project"],[36,8,1,"","reflect"],[36,8,1,"","reflect_ip"],[36,8,1,"","rotate"],[36,8,1,"","rotate_ip"],[36,8,1,"","rotate_ip_rad"],[36,8,1,"","rotate_rad"],[36,8,1,"","rotate_rad_ip"],[36,8,1,"","scale_to_length"],[36,8,1,"","slerp"],[36,8,1,"","update"]],"pygame.math.Vector3":[[36,8,1,"","angle_to"],[36,8,1,"","as_spherical"],[36,8,1,"","clamp_magnitude"],[36,8,1,"","clamp_magnitude_ip"],[36,8,1,"","copy"],[36,8,1,"","cross"],[36,8,1,"","distance_squared_to"],[36,8,1,"","distance_to"],[36,8,1,"","dot"],[36,8,1,"","elementwise"],[36,7,1,"","epsilon"],[36,8,1,"","from_spherical"],[36,8,1,"","is_normalized"],[36,8,1,"","length"],[36,8,1,"","length_squared"],[36,8,1,"","lerp"],[36,8,1,"","magnitude"],[36,8,1,"","magnitude_squared"],[36,8,1,"","move_towards"],[36,8,1,"","move_towards_ip"],[36,8,1,"","normalize"],[36,8,1,"","normalize_ip"],[36,8,1,"","project"],[36,8,1,"","reflect"],[36,8,1,"","reflect_ip"],[36,8,1,"","rotate"],[36,8,1,"","rotate_ip"],[36,8,1,"","rotate_ip_rad"],[36,8,1,"","rotate_rad"],[36,8,1,"","rotate_rad_ip"],[36,8,1,"","rotate_x"],[36,8,1,"","rotate_x_ip"],[36,8,1,"","rotate_x_ip_rad"],[36,8,1,"","rotate_x_rad"],[36,8,1,"","rotate_x_rad_ip"],[36,8,1,"","rotate_y"],[36,8,1,"","rotate_y_ip"],[36,8,1,"","rotate_y_ip_rad"],[36,8,1,"","rotate_y_rad"],[36,8,1,"","rotate_y_rad_ip"],[36,8,1,"","rotate_z"],[36,8,1,"","rotate_z_ip"],[36,8,1,"","rotate_z_ip_rad"],[36,8,1,"","rotate_z_rad"],[36,8,1,"","rotate_z_rad_ip"],[36,8,1,"","scale_to_length"],[36,8,1,"","slerp"],[36,8,1,"","update"]],"pygame.midi":[[37,6,1,"","Input"],[37,10,1,"","MidiException"],[37,6,1,"","Output"],[37,9,1,"","frequency_to_midi"],[37,9,1,"","get_count"],[37,9,1,"","get_default_input_id"],[37,9,1,"","get_default_output_id"],[37,9,1,"","get_device_info"],[37,9,1,"","get_init"],[37,9,1,"","init"],[37,9,1,"","midi_to_ansi_note"],[37,9,1,"","midi_to_frequency"],[37,9,1,"","midis2events"],[37,9,1,"","quit"],[37,9,1,"","time"]],"pygame.midi.Input":[[37,8,1,"","close"],[37,8,1,"","poll"],[37,8,1,"","read"]],"pygame.midi.Output":[[37,8,1,"","abort"],[37,8,1,"","close"],[37,8,1,"","note_off"],[37,8,1,"","note_on"],[37,8,1,"","pitch_bend"],[37,8,1,"","set_instrument"],[37,8,1,"","write"],[37,8,1,"","write_short"],[37,8,1,"","write_sys_ex"]],"pygame.mixer":[[38,6,1,"","Channel"],[38,6,1,"","Sound"],[38,9,1,"","fadeout"],[38,9,1,"","find_channel"],[38,9,1,"","get_busy"],[38,9,1,"","get_init"],[38,9,1,"","get_num_channels"],[38,9,1,"","get_sdl_mixer_version"],[38,9,1,"","init"],[40,5,0,"-","music"],[38,9,1,"","pause"],[38,9,1,"","pre_init"],[38,9,1,"","quit"],[38,9,1,"","set_num_channels"],[38,9,1,"","set_reserved"],[38,9,1,"","stop"],[38,9,1,"","unpause"]],"pygame.mixer.Channel":[[38,8,1,"","fadeout"],[38,8,1,"","get_busy"],[38,8,1,"","get_endevent"],[38,8,1,"","get_queue"],[38,8,1,"","get_sound"],[38,8,1,"","get_volume"],[38,8,1,"","pause"],[38,8,1,"","play"],[38,8,1,"","queue"],[38,8,1,"","set_endevent"],[38,8,1,"","set_volume"],[38,8,1,"","stop"],[38,8,1,"","unpause"]],"pygame.mixer.Sound":[[38,8,1,"","fadeout"],[38,8,1,"","get_length"],[38,8,1,"","get_num_channels"],[38,8,1,"","get_raw"],[38,8,1,"","get_volume"],[38,8,1,"","play"],[38,8,1,"","set_volume"],[38,8,1,"","stop"]],"pygame.mixer.music":[[40,9,1,"","fadeout"],[40,9,1,"","get_busy"],[40,9,1,"","get_endevent"],[40,9,1,"","get_pos"],[40,9,1,"","get_volume"],[40,9,1,"","load"],[40,9,1,"","pause"],[40,9,1,"","play"],[40,9,1,"","queue"],[40,9,1,"","rewind"],[40,9,1,"","set_endevent"],[40,9,1,"","set_pos"],[40,9,1,"","set_volume"],[40,9,1,"","stop"],[40,9,1,"","unload"],[40,9,1,"","unpause"]],"pygame.mouse":[[39,9,1,"","get_cursor"],[39,9,1,"","get_focused"],[39,9,1,"","get_pos"],[39,9,1,"","get_pressed"],[39,9,1,"","get_rel"],[39,9,1,"","get_visible"],[39,9,1,"","set_cursor"],[39,9,1,"","set_pos"],[39,9,1,"","set_visible"]],"pygame.pixelcopy":[[43,9,1,"","array_to_surface"],[43,9,1,"","make_surface"],[43,9,1,"","map_array"],[43,9,1,"","surface_to_array"]],"pygame.scrap":[[46,9,1,"","contains"],[46,9,1,"","get"],[46,9,1,"","get_init"],[46,9,1,"","get_types"],[46,9,1,"","init"],[46,9,1,"","lost"],[46,9,1,"","put"],[46,9,1,"","set_mode"]],"pygame.sndarray":[[49,9,1,"","array"],[49,9,1,"","get_arraytype"],[49,9,1,"","get_arraytypes"],[49,9,1,"","make_sound"],[49,9,1,"","samples"],[49,9,1,"","use_arraytype"]],"pygame.sprite":[[50,6,1,"","DirtySprite"],[50,6,1,"","Group"],[50,9,1,"","GroupSingle"],[50,6,1,"","LayeredDirty"],[50,6,1,"","LayeredUpdates"],[50,9,1,"","OrderedUpdates"],[50,6,1,"","RenderClear"],[50,6,1,"","RenderPlain"],[50,6,1,"","RenderUpdates"],[50,6,1,"","Sprite"],[50,6,1,"","WeakDirtySprite"],[50,6,1,"","WeakSprite"],[50,9,1,"","collide_circle"],[50,9,1,"","collide_circle_ratio"],[50,9,1,"","collide_mask"],[50,9,1,"","collide_rect"],[50,9,1,"","collide_rect_ratio"],[50,9,1,"","groupcollide"],[50,9,1,"","spritecollide"],[50,9,1,"","spritecollideany"]],"pygame.sprite.Group":[[50,8,1,"","add"],[50,8,1,"","clear"],[50,8,1,"","copy"],[50,8,1,"","draw"],[50,8,1,"","empty"],[50,8,1,"","has"],[50,8,1,"","remove"],[50,8,1,"","sprites"],[50,8,1,"","update"]],"pygame.sprite.LayeredDirty":[[50,8,1,"","change_layer"],[50,8,1,"","clear"],[50,8,1,"","draw"],[50,8,1,"","get_clip"],[50,8,1,"","repaint_rect"],[50,8,1,"","set_clip"],[50,8,1,"","set_timing_threshold"],[50,8,1,"","set_timing_treshold"]],"pygame.sprite.LayeredUpdates":[[50,8,1,"","add"],[50,8,1,"","change_layer"],[50,8,1,"","draw"],[50,8,1,"","get_bottom_layer"],[50,8,1,"","get_layer_of_sprite"],[50,8,1,"","get_sprite"],[50,8,1,"","get_sprites_at"],[50,8,1,"","get_sprites_from_layer"],[50,8,1,"","get_top_layer"],[50,8,1,"","get_top_sprite"],[50,8,1,"","layers"],[50,8,1,"","move_to_back"],[50,8,1,"","move_to_front"],[50,8,1,"","remove_sprites_of_layer"],[50,8,1,"","sprites"],[50,8,1,"","switch_layer"]],"pygame.sprite.RenderUpdates":[[50,8,1,"","draw"]],"pygame.sprite.Sprite":[[50,8,1,"","add"],[50,8,1,"","alive"],[50,8,1,"","groups"],[50,8,1,"","kill"],[50,8,1,"","remove"],[50,8,1,"","update"]],"pygame.surfarray":[[52,9,1,"","array2d"],[52,9,1,"","array3d"],[52,9,1,"","array_alpha"],[52,9,1,"","array_blue"],[52,9,1,"","array_colorkey"],[52,9,1,"","array_green"],[52,9,1,"","array_red"],[52,9,1,"","blit_array"],[52,9,1,"","get_arraytype"],[52,9,1,"","get_arraytypes"],[52,9,1,"","make_surface"],[52,9,1,"","map_array"],[52,9,1,"","pixels2d"],[52,9,1,"","pixels3d"],[52,9,1,"","pixels_alpha"],[52,9,1,"","pixels_blue"],[52,9,1,"","pixels_green"],[52,9,1,"","pixels_red"],[52,9,1,"","use_arraytype"]],"pygame.tests":[[53,9,1,"","run"]],"pygame.time":[[54,6,1,"","Clock"],[54,9,1,"","delay"],[54,9,1,"","get_ticks"],[54,9,1,"","set_timer"],[54,9,1,"","wait"]],"pygame.time.Clock":[[54,8,1,"","get_fps"],[54,8,1,"","get_rawtime"],[54,8,1,"","get_time"],[54,8,1,"","tick"],[54,8,1,"","tick_busy_loop"]],"pygame.transform":[[56,9,1,"","average_color"],[56,9,1,"","average_surfaces"],[56,9,1,"","chop"],[56,9,1,"","flip"],[56,9,1,"","get_smoothscale_backend"],[56,9,1,"","grayscale"],[56,9,1,"","laplacian"],[56,9,1,"","rotate"],[56,9,1,"","rotozoom"],[56,9,1,"","scale"],[56,9,1,"","scale2x"],[56,9,1,"","scale_by"],[56,9,1,"","set_smoothscale_backend"],[56,9,1,"","smoothscale"],[56,9,1,"","smoothscale_by"],[56,9,1,"","threshold"]],"pygame.version":[[44,11,1,"","SDL"],[44,11,1,"","rev"],[44,11,1,"","ver"],[44,11,1,"","vernum"]],pgBuffer_AsArrayInterface:[[1,2,1,"c.pgBuffer_AsArrayInterface","view_p"]],pgBuffer_AsArrayStruct:[[1,2,1,"c.pgBuffer_AsArrayStruct","view_p"]],pgBuffer_Release:[[1,2,1,"c.pgBuffer_Release","pg_view_p"]],pgBufproxy_Check:[[2,2,1,"c.pgBufproxy_Check","x"]],pgBufproxy_GetParent:[[2,2,1,"c.pgBufproxy_GetParent","obj"]],pgBufproxy_New:[[2,2,1,"c.pgBufproxy_New","get_buffer"],[2,2,1,"c.pgBufproxy_New","obj"]],pgBufproxy_Trip:[[2,2,1,"c.pgBufproxy_Trip","obj"]],pgChannel_AsInt:[[7,2,1,"c.pgChannel_AsInt","x"]],pgChannel_Check:[[7,2,1,"c.pgChannel_Check","obj"]],pgChannel_New:[[7,2,1,"c.pgChannel_New","channelnum"]],pgColor_Check:[[3,2,1,"c.pgColor_Check","obj"]],pgColor_New:[[3,2,1,"c.pgColor_New","rgba"]],pgColor_NewLength:[[3,2,1,"c.pgColor_NewLength","length"],[3,2,1,"c.pgColor_NewLength","rgba"]],pgDict_AsBuffer:[[1,2,1,"c.pgDict_AsBuffer","dict"],[1,2,1,"c.pgDict_AsBuffer","flags"],[1,2,1,"c.pgDict_AsBuffer","pg_view_p"]],pgEventObject:[[5,3,1,"c.pgEventObject.type","type"]],pgEvent_Check:[[5,2,1,"c.pgEvent_Check","x"]],pgEvent_FillUserEvent:[[5,2,1,"c.pgEvent_FillUserEvent","e"],[5,2,1,"c.pgEvent_FillUserEvent","event"]],pgEvent_New2:[[5,2,1,"c.pgEvent_New2","dict"],[5,2,1,"c.pgEvent_New2","type"]],pgEvent_New:[[5,2,1,"c.pgEvent_New","event"]],pgFont_Check:[[6,2,1,"c.pgFont_Check","x"]],pgFont_IS_ALIVE:[[6,2,1,"c.pgFont_IS_ALIVE","o"]],pgFont_New:[[6,2,1,"c.pgFont_New","filename"],[6,2,1,"c.pgFont_New","font_index"]],pgLifetimeLockObject:[[12,3,1,"c.pgLifetimeLockObject.lockobj","lockobj"],[12,3,1,"c.pgLifetimeLockObject.surface","surface"]],pgLifetimeLock_Check:[[12,2,1,"c.pgLifetimeLock_Check","x"]],pgObject_GetBuffer:[[1,2,1,"c.pgObject_GetBuffer","flags"],[1,2,1,"c.pgObject_GetBuffer","obj"],[1,2,1,"c.pgObject_GetBuffer","pg_view_p"]],pgRWops_FromFileObject:[[9,2,1,"c.pgRWops_FromFileObject","obj"]],pgRWops_FromObject:[[9,2,1,"c.pgRWops_FromObject","extptr"],[9,2,1,"c.pgRWops_FromObject","obj"]],pgRWops_IsFileObject:[[9,2,1,"c.pgRWops_IsFileObject","rw"]],pgRWops_ReleaseObject:[[9,2,1,"c.pgRWops_ReleaseObject","context"]],pgRectObject:[[8,3,1,"c.pgRectObject.r","r"]],pgRect_AsRect:[[8,2,1,"c.pgRect_AsRect","obj"]],pgRect_Check:[[8,2,1,"c.pgRect_Check","obj"]],pgRect_FromObject:[[8,2,1,"c.pgRect_FromObject","obj"],[8,2,1,"c.pgRect_FromObject","temp"]],pgRect_New4:[[8,2,1,"c.pgRect_New4","h"],[8,2,1,"c.pgRect_New4","w"],[8,2,1,"c.pgRect_New4","x"],[8,2,1,"c.pgRect_New4","y"]],pgRect_New:[[8,2,1,"c.pgRect_New","r"]],pgRect_Normalize:[[8,2,1,"c.pgRect_Normalize","rect"]],pgSound_AsChunk:[[7,2,1,"c.pgSound_AsChunk","x"]],pgSound_Check:[[7,2,1,"c.pgSound_Check","obj"]],pgSound_New:[[7,2,1,"c.pgSound_New","chunk"]],pgSurface_AsSurface:[[11,2,1,"c.pgSurface_AsSurface","x"]],pgSurface_Blit:[[11,2,1,"c.pgSurface_Blit","dstobj"],[11,2,1,"c.pgSurface_Blit","dstrect"],[11,2,1,"c.pgSurface_Blit","srcobj"],[11,2,1,"c.pgSurface_Blit","srcrect"],[11,2,1,"c.pgSurface_Blit","the_args"]],pgSurface_Check:[[11,2,1,"c.pgSurface_Check","x"]],pgSurface_Lock:[[12,2,1,"c.pgSurface_Lock","surfobj"]],pgSurface_LockBy:[[12,2,1,"c.pgSurface_LockBy","lockobj"],[12,2,1,"c.pgSurface_LockBy","surfobj"]],pgSurface_LockLifetime:[[12,2,1,"c.pgSurface_LockLifetime","lockobj"],[12,2,1,"c.pgSurface_LockLifetime","surfobj"]],pgSurface_New2:[[11,2,1,"c.pgSurface_New2","owner"],[11,2,1,"c.pgSurface_New2","s"]],pgSurface_New:[[11,2,1,"c.pgSurface_New","s"]],pgSurface_Prep:[[12,2,1,"c.pgSurface_Prep","surfobj"]],pgSurface_UnLock:[[12,2,1,"c.pgSurface_UnLock","surfobj"]],pgSurface_UnLockBy:[[12,2,1,"c.pgSurface_UnLockBy","lockobj"],[12,2,1,"c.pgSurface_UnLockBy","surfobj"]],pgSurface_Unprep:[[12,2,1,"c.pgSurface_Unprep","surfobj"]],pgVidInfo_AsVidInfo:[[4,2,1,"c.pgVidInfo_AsVidInfo","obj"]],pgVidInfo_Check:[[4,2,1,"c.pgVidInfo_Check","x"]],pgVidInfo_New:[[4,2,1,"c.pgVidInfo_New","i"]],pg_EncodeFilePath:[[9,2,1,"c.pg_EncodeFilePath","eclass"],[9,2,1,"c.pg_EncodeFilePath","obj"]],pg_EncodeString:[[9,2,1,"c.pg_EncodeString","eclass"],[9,2,1,"c.pg_EncodeString","encoding"],[9,2,1,"c.pg_EncodeString","errors"],[9,2,1,"c.pg_EncodeString","obj"]],pg_FloatFromObj:[[1,2,1,"c.pg_FloatFromObj","obj"],[1,2,1,"c.pg_FloatFromObj","val"]],pg_FloatFromObjIndex:[[1,2,1,"c.pg_FloatFromObjIndex","index"],[1,2,1,"c.pg_FloatFromObjIndex","obj"],[1,2,1,"c.pg_FloatFromObjIndex","val"]],pg_IntFromObj:[[1,2,1,"c.pg_IntFromObj","obj"],[1,2,1,"c.pg_IntFromObj","val"]],pg_IntFromObjIndex:[[1,2,1,"c.pg_IntFromObjIndex","index"],[1,2,1,"c.pg_IntFromObjIndex","obj"],[1,2,1,"c.pg_IntFromObjIndex","val"]],pg_RGBAFromObj:[[1,2,1,"c.pg_RGBAFromObj","RGBA"],[1,2,1,"c.pg_RGBAFromObj","obj"]],pg_RegisterQuit:[[1,2,1,"c.pg_RegisterQuit","f"]],pg_SetDefaultWindow:[[1,2,1,"c.pg_SetDefaultWindow","win"]],pg_SetDefaultWindowSurface:[[1,2,1,"c.pg_SetDefaultWindowSurface","screen"]],pg_TwoFloatsFromObj:[[1,2,1,"c.pg_TwoFloatsFromObj","obj"],[1,2,1,"c.pg_TwoFloatsFromObj","val1"],[1,2,1,"c.pg_TwoFloatsFromObj","val2"]],pg_TwoIntsFromObj:[[1,2,1,"c.pg_TwoIntsFromObj","obj"],[1,2,1,"c.pg_TwoIntsFromObj","v2"],[1,2,1,"c.pg_TwoIntsFromObj","val1"]],pg_UintFromObj:[[1,2,1,"c.pg_UintFromObj","obj"],[1,2,1,"c.pg_UintFromObj","val"]],pg_UintFromObjIndex:[[1,2,1,"c.pg_UintFromObjIndex","_index"],[1,2,1,"c.pg_UintFromObjIndex","obj"],[1,2,1,"c.pg_UintFromObjIndex","val"]],pg_buffer:[[1,3,1,"c.pg_buffer.consumer","consumer"],[1,3,1,"c.pg_buffer.release_buffer","release_buffer"],[1,3,1,"c.pg_buffer.view","view"]],pg_mod_autoinit:[[1,2,1,"c.pg_mod_autoinit","modname"]],pg_mod_autoquit:[[1,2,1,"c.pg_mod_autoquit","modname"]],pygame:[[17,6,1,"","BufferProxy"],[20,6,1,"","Color"],[41,6,1,"","Overlay"],[42,6,1,"","PixelArray"],[45,6,1,"","Rect"],[51,6,1,"","Surface"],[18,5,0,"-","camera"],[19,5,0,"-","cdrom"],[22,5,0,"-","cursors"],[23,5,0,"-","display"],[24,5,0,"-","draw"],[44,9,1,"","encode_file_path"],[44,9,1,"","encode_string"],[44,10,1,"","error"],[25,5,0,"-","event"],[26,5,0,"-","examples"],[27,5,0,"-","fastevent"],[28,5,0,"-","font"],[29,5,0,"-","freetype"],[44,9,1,"","get_error"],[44,9,1,"","get_init"],[44,9,1,"","get_sdl_byteorder"],[44,9,1,"","get_sdl_version"],[30,5,0,"-","gfxdraw"],[31,5,0,"-","image"],[44,9,1,"","init"],[32,5,0,"-","joystick"],[33,5,0,"-","key"],[34,5,0,"-","locals"],[35,5,0,"-","mask"],[36,5,0,"-","math"],[37,5,0,"-","midi"],[38,5,0,"-","mixer"],[39,5,0,"-","mouse"],[43,5,0,"-","pixelcopy"],[44,9,1,"","quit"],[44,9,1,"","register_quit"],[46,5,0,"-","scrap"],[44,9,1,"","set_error"],[49,5,0,"-","sndarray"],[50,5,0,"-","sprite"],[52,5,0,"-","surfarray"],[53,5,0,"-","tests"],[54,5,0,"-","time"],[56,5,0,"-","transform"],[44,5,0,"-","version"]]},objnames:{"0":["c","macro","C macro"],"1":["c","function","C function"],"10":["py","exception","Python exception"],"11":["py","data","Python data"],"2":["c","functionParam","C function parameter"],"3":["c","member","C member"],"4":["c","type","C type"],"5":["py","module","Python module"],"6":["py","class","Python class"],"7":["py","attribute","Python attribute"],"8":["py","method","Python method"],"9":["py","function","Python function"]},objtypes:{"0":"c:macro","1":"c:function","10":"py:exception","11":"py:data","2":"c:functionParam","3":"c:member","4":"c:type","5":"py:module","6":"py:class","7":"py:attribute","8":"py:method","9":"py:function"},terms:{"0":[1,2,5,6,9,11,15,17,18,19,20,22,23,24,25,26,28,29,30,31,32,33,35,36,37,38,39,40,41,42,43,44,45,47,48,50,51,52,54,55,56,57,58,59,62,63,64,65,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84,85,86,88,89],"02778":29,"08333":29,"0\uac1c":[78,79],"0\ucc28\uc6d0":81,"0d":73,"0dev11":32,"0x00":20,"0x00000000":51,"0x00000001":51,"0x00000004":51,"0x00000100":51,"0x00001000":51,"0x00002000":51,"0x00004000":51,"0x00010000":51,"0x01000000":51,"0x10":37,"0x10000":29,"0x10ffff":29,"0x11":37,"0x12":37,"0x13":37,"0x7d":37,"0x90":37,"0xaacce":42,"0xc0":37,"0xd800":29,"0xdfff":29,"0xf0":37,"0xf7":37,"0xff":20,"0xff00ff":42,"0xffff":23,"0xrrggbb":20,"0xrrggbbaa":20,"1":[1,2,3,5,8,11,13,15,16,17,18,20,22,23,24,25,26,28,29,30,31,33,35,36,37,38,39,40,42,43,44,45,46,47,48,49,50,51,52,54,55,56,57,58,59,60,62,63,64,65,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,83,84],"10":[23,24,26,32,35,36,45,54,56,58,62,63,65,66,68,69,70,71,72,73,76,77,78,79,80,81,84,85,88,89],"100":[20,24,36,37,51,57,62,65,84],"1000":[15,50,54],"101":16,"1024":[37,38],"105":57,"1080":[23,59],"1080p":23,"10\ub610\ub294":79,"10\uc758":76,"10\uc774":77,"10th":35,"11":[32,68,69,76,77],"113":24,"114":42,"115":24,"117":26,"11\uc744":77,"11\uc758":76,"11\uc774":77,"12":[10,28,29,32,44,58,66,68,76],"120":22,"1234":44,"125":24,"127":[35,37,71,72,73,79,80,81],"128":65,"1280":[15,58,66,84],"13":[32,63,65,68,76,89],"135":24,"14":[25,32,47,68,76],"140":[67,68,69,70,75,76,77,78],"145":57,"14\uc758":76,"15":[24,28,32,36,45,51,58,66,68,69,76,77],"150":[24,45,85],"1561":16,"15924":28,"15\uc758":76,"16":[14,22,23,25,28,29,32,38,49,59,65,68,76],"1617":16,"16711680":59,"16bit":38,"17":[63,68,76],"170":[42,57,58,66],"179":24,"17\uc5d0\uc11c\uc758":76,"18":[24,25,36,58,63,66,68,76],"187":[58,66],"19":[68,76],"192":84,"1920":[23,59],"19\ub294":76,"19\uc5d0\uc11c":76,"1\uac1c":78,"1\uac1c\uc758":76,"1\uacfc":77,"1\uc778":75,"1\uc778\uc9c0":81,"1\uc904\uc9dc\ub9ac":76,"1\ucc28\uc6d0":81,"1\ucd08\uc5d0":77,"1d":[42,73],"1e":36,"1s":62,"1x1":37,"2":[1,8,11,14,15,17,18,19,20,22,23,24,25,26,27,28,29,30,31,33,35,36,37,38,39,40,41,42,43,44,45,47,50,51,52,54,55,56,57,58,61,63,65,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,83,84],"20":[22,24,28,36,45,56,65,68,71,72,73,76,79,80,81,84],"200":[24,45,63],"2000":[15,63,84],"20000":37,"2001":[16,63],"2004":18,"2021":16,"2022":84,"204":42,"20500":37,"20\uc77c":79,"20\uc904\uc9dc\ub9ac":76,"21":[37,63],"210":24,"214":16,"22":49,"220":[24,67,68,69,70,75,76,77,78],"22000":49,"22050":38,"225":24,"23":63,"235":[58,66],"238":[42,58,66],"2380":23,"24":[17,18,22,24,28,31,42,51,52,56,63,65],"240":[63,68,69,70,71,72,73,76,77,78,79,80,81],"24x24":22,"25":[32,56,58,66],"250":[24,85],"255":[1,20,28,29,30,32,35,42,43,50,51,56,57,58,59,65,68,69,70,71,72,73,76,77,78,79,80,81,84,85],"256":[23,37,51],"260":24,"27":37,"270":[72,73,80,81],"29":16,"299":42,"2\uac1c\ub97c":80,"2\uac1c\uc758":78,"2\ucc28\uc6d0":81,"2d":[15,35,42,43,48,52,63,64,65,68,73,76,84],"2d\uc6a9":76,"2pre":36,"2s":62,"2x2":65,"3":[1,16,17,18,20,22,23,24,25,28,30,31,32,33,36,37,38,39,42,43,44,45,47,50,51,56,61,62,63,64,65,67,68,69,70,71,73,76,77,78,79,81,83,84],"30":[22,24,29,30,32,36,38,45,53,57,63,71,84],"300":[15,24,45],"3072":38,"30\uc73c\ub85c":79,"315":29,"32":[1,14,17,18,28,29,31,35,38,51,52,56,59,65,68,69,70,71,72,73,76,77,78,79,80,81],"320":[63,68,69,70,71,72,73,76,77,78,79,80,81],"325":[72,73,80,81],"32767":47,"32768":47,"32x32":23,"33":65,"35":74,"359":29,"35\ub144":82,"36":[29,85],"360":[20,29,36,47,58,66],"390":29,"3\uac1c\uc758":[76,79],"3d":[23,26,43,52,63,65,67],"3f":32,"3rd":63,"3x3":65,"4":[1,3,8,17,20,24,25,28,29,33,36,37,38,39,42,47,51,56,57,58,59,60,61,62,63,65,66,68,69,70,71,72,73,76,77,78,79,80,81,83,84,86,89],"40":[15,16,22,24,54,56,62,63,71,72,73,79,80,81,84],"400":[22,23,24,57],"4096":[37,38],"42":24,"425":[72,73,80,81],"4321":44,"438":16,"44100":38,"45":[29,72,73,80,81],"47":89,"480":[18,26,39,48,57,58,59,62,66,68,69,70,71,72,73,76,77,78,79,80,81,89],"480\uc73c\ub85c":76,"4\uac1c\uc758":[76,79],"4k":23,"4th":65,"5":[13,20,23,24,25,29,33,35,36,37,38,39,40,42,44,45,46,49,50,57,58,61,62,63,65,66,68,69,70,71,72,73,76,77,78,79,80,81,83,89],"50":[16,22,24,26,36,45,57,65,73,81,84,85],"500":[32,45],"500m":37,"512":38,"55":56,"56":24,"587":42,"5\uac00":77,"5\uac1c\uc758":76,"5\uc5d0":78,"5x5":[73,81],"6":[24,26,32,33,36,38,44,50,61,62,65,68,69,70,71,73,76,77,78,79,81,83],"60":[15,16,22,24,37,38,39,58,62,66,69,71,72,73,77,79,80,81,84,89],"600":[22,59,62],"60\uc774\ub77c\ub294":77,"63":20,"64":[20,29,56,58,66],"640":[16,18,26,39,48,57,59,62,68,69,70,71,72,73,76,77,78,79,80,81,89],"640x480":[23,62,65],"65":[37,56],"65280":59,"6\uc744":79,"6x":84,"7":[23,32,33,43,44,63,65,68,69,70,71,76,77,78,79,83],"70":24,"700":[23,32],"72":29,"720":[15,29,84],"75":[22,24,69,77],"7\uc5d0\uc11c":79,"7\uc758":76,"8":[14,17,18,20,22,23,24,26,28,29,30,31,32,33,35,38,42,43,44,46,49,50,51,52,54,56,59,62,63,65,68,69,70,76,77,78,83,89],"80":[24,50,58,66],"800":59,"8191":37,"8192":37,"8\uc758":76,"8bit":38,"9":[13,15,17,18,20,23,25,26,29,30,32,33,35,36,37,38,40,42,43,44,45,46,47,51,52,56,57,65,68,69,70,76,77,78],"90":[24,35,36,38,56,57,58,66,89],"97":33,"9\uc758":76,"\uac00":[76,77,81],"\uac00\ub2a5":80,"\uac00\ub3c5\uc131\uc744":76,"\uac00\ubcf4\uc790":78,"\uac00\uc18d":77,"\uac00\uc7a5":[76,79,82],"\uac00\uc815\ud558\uba74":76,"\uac00\uc815\ud574":75,"\uac00\uc9c0\uace0":[75,78,81],"\uac00\uc9c0\uae30":76,"\uac00\uc9c0\ub294":76,"\uac00\uc9c0\ubbc0\ub85c":[76,79],"\uac00\uc9c4\ub2e4":[76,80],"\uac00\uc9c4\ub2e4\ub294":75,"\uac00\uc9c8":82,"\uac01\uac01":79,"\uac01\uac01\uc758":79,"\uac04\ub2e8\ud558\ub2e4":81,"\uac10\uc18c\uc2dc\ud0a4\ub294":80,"\uac10\uc18c\ud55c\ub2e4":81,"\uac12":79,"\uac12\ub9cc\uc774":79,"\uac12\uc740":[76,79],"\uac12\uc744":[76,79,80],"\uac12\uc774":79,"\uac12\uc774\uace0":79,"\uac16\ub294":[81,82],"\uac19\ub2e4":[75,76,82],"\uac19\uc544":[77,81],"\uac19\uc740":[76,77,82],"\uac19\uc74c":81,"\uac19\uc774":[76,79],"\uac1c\ub150\uc740":82,"\uac1c\ub150\uc744":77,"\uac1c\ub150\uc774\ub2e4":82,"\uac1c\ubc1c\uc790\uac00":75,"\uac1c\uc120\uc758":81,"\uac1c\uc218\ub97c":81,"\uac1c\uc218\ub9cc\ud07c":79,"\uac1d\uccb4":[76,78],"\uac1d\uccb4\uc5d0":76,"\uac1d\uccb4\uc758":76,"\uac70\uc758":[75,76],"\uac71\uc815\ud558\uc9c0":82,"\uac78\ub9ac\ub294":76,"\uac80\uc740":[79,81],"\uac80\uc740\ube14\ub85d":15,"\uac83":[75,76,78],"\uac83\uacfc":[80,82],"\uac83\ub4e4\uc774":81,"\uac83\ub4e4\uc774\ub2e4":76,"\uac83\ubcf4\ub2e4":79,"\uac83\uc5d0":[75,77,78],"\uac83\uc5d0\ub9cc":75,"\uac83\uc740":[76,77,78,79,80,82],"\uac83\uc744":79,"\uac83\uc774":[75,76,79,80],"\uac83\uc774\uae30":78,"\uac83\uc774\ub2e4":[75,76,77,78,79,80,81,82],"\uac83\uc778\uac00":80,"\uac83\ucc98\ub7fc":[77,80],"\uac8c\uc784":[75,76,77,78,81,82],"\uac8c\uc784\ub3c4":[75,82],"\uac8c\uc784\ub9cc\uc758":82,"\uac8c\uc784\uc5d0\uc11c\uc758":79,"\uac8c\uc784\uc5d0\uc120":77,"\uac8c\uc784\uc5d4\uc9c4":75,"\uac8c\uc784\uc5d4\uc9c4\uc774\ub098":75,"\uac8c\uc784\uc740":[75,78,81],"\uac8c\uc784\uc744":[75,78,82],"\uac8c\uc784\uc758":[75,78],"\uac8c\uc784\uc774":[75,77,78,80,81,82],"\uac8c\uc784\uc774\ub098":82,"\uac8c\uc784\uc774\ub2e4":81,"\uac8c\uc784\uc774\ub77c\uace0":78,"\uac8c\uc784\uc774\ubbc0\ub85c":76,"\uac8c\uc784\ud310":83,"\uac8c\uc784\ud310\uc740":81,"\uac8c\uc784\ud310\uc744":81,"\uacaa\uc5b4":82,"\uacb0\uacfc":[76,77,78],"\uacb0\uacfc\uac00":77,"\uacb0\uacfc\ub294":[78,81],"\uacb0\uacfc\ub97c":77,"\uacb0\uacfc\ubb3c\ub4e4\uc744":76,"\uacb0\uacfc\ubb3c\uc740":76,"\uacb0\ub860\uc774":82,"\uacb0\uc815\ud560":76,"\uacbd\uc6b0":[76,78,79,80],"\uacbd\uc6b0\ub97c":78,"\uacbd\uc6b0\uc758":82,"\uacbd\ud5d8\uc774":78,"\uacc4\uc0b0\ub9cc":76,"\uacc4\uc0b0\uc744":80,"\uacc4\uc0b0\ud558\uae30":77,"\uacc4\uc0b0\ud574\uc57c":81,"\uacc4\uc18d":[75,76,78],"\uace0":81,"\uace0\uae09":75,"\uace0\ub824\ub418\uc5c8\uc744":82,"\uace0\ub824\ub418\uc9c0":77,"\uace0\ub824\ud558\uc5ec":76,"\uace0\ub974\ub294":81,"\uace0\uc815":77,"\uace0\uc815\ub418\uc5b4":[76,79],"\uace0\uc815\ub41c":76,"\uace0\uc815\ub420":77,"\uace0\uc815\uc2dc\ucf1c":77,"\uace0\uc815\uc2dc\ud0a4\ub294":77,"\uace0\uc815\uc2dc\ud0a8\ub2e4":76,"\uace0\uc815\ud558\ub294":77,"\uacf5\uac04":76,"\uacf5\uac04\uc0c1\uc5d0\uc11c\uc758":75,"\uacf5\uac04\uc744":[76,80],"\uacf5\uc774\ub098":77,"\uacfc":[76,80],"\uad00\ub828":75,"\uad6c\uc131":76,"\uad6c\uc131\ub41c\ub2e4":75,"\uad6c\uc131\ud560":75,"\uad6c\uc2dd\uc774\uace0":75,"\uad6c\uc5ed\uc744":76,"\uad6c\uc5ed\uc758":76,"\uad6c\uccb4\uc801\uc778":[78,80],"\uad6c\ud604\ud55c":[81,82],"\uad6c\ud604\ud55c\ub2e4\uba74":81,"\uad6c\ud604\ud560":82,"\uaddc\uce59":[78,82],"\uaddc\uce59\uc740":81,"\uaddc\uce59\uc744":[81,82],"\uaddc\uce59\uc774":82,"\uadf8":[75,76,77,79,80,81],"\uadf8\uac83\uc740":79,"\uadf8\uac83\uc774":[78,82],"\uadf8\uac83\uc774\ub2e4":76,"\uadf8\ub2e4\uc9c0":76,"\uadf8\ub798\uc11c":[75,76,77],"\uadf8\ub798\ud53d":75,"\uadf8\ub798\ud53d\uc744":75,"\uadf8\ub7ec\ub098":[77,78,82],"\uadf8\ub7ec\uba74":[75,77,81],"\uadf8\ub7ec\ubbc0\ub85c":[76,77,80,82],"\uadf8\ub7f0\uac00":78,"\uadf8\ub807\ub2e4\uba74":[76,77],"\uadf8\ub807\uc9c0":80,"\uadf8\ub824\uc9c0\uace0":76,"\uadf8\ub824\uc9c0\ub294":76,"\uadf8\ub824\uc9c4\ub2e4":81,"\uadf8\ub9ac\uace0":[75,76,77],"\uadf8\ub9ac\uae30":75,"\uadf8\ub9ac\ub294":[76,79],"\uadf8\ub9b0\ub2e4":[79,80],"\uadf9\uc18c\uc218\ub9cc\uc744":82,"\uae30\ub2a5":77,"\uae30\ub2a5\uc744":[76,77],"\uae30\ub85d\ud574\uc57c":80,"\uae30\ubc18":76,"\uae30\ubc18\uc73c\ub85c":76,"\uae30\ubc18\ud558\uc600\uae30":75,"\uae30\uc874\uc758":82,"\uae38\uc774\ub97c":76,"\uae4c\uba39\uc5b4\uc120":76,"\uae5c\ube61\uac70\ub9ac\ub294":75,"\uaf64":76,"\ub049\uaca8":75,"\ub05d\ub09c":76,"\ub05d\uc774":82,"\ub098":81,"\ub098\uac8c":82,"\ub098\ub220\uc9c8":76,"\ub098\ub294":78,"\ub098\uba74":76,"\ub098\uc544\uc84c\ub2e4":79,"\ub098\uc911\uc5d0":[76,77,78],"\ub098\ud0c0\ub0b8\ub2e4":80,"\ub09c\ub2e4":82,"\ub09c\uc218":82,"\ub09c\uc218\uac00":82,"\ub09c\uc218\uae4c\uc9c0":82,"\ub0ab\ub2e4":75,"\ub0b4\ubd80":[76,78],"\ub0b4\ubd80\ub97c":80,"\ub0b4\ubd80\uc5d0\uc11c":80,"\ub0b4\ubd80\uc600\ub2e4\uba74":80,"\ub0b4\uc5d0\uc11c":[77,81,82],"\ub0b4\uc6a9":76,"\ub0b4\uc6a9\uacfc":76,"\ub0b4\uc6a9\uc740":80,"\ub108\ubb34":77,"\ub118\uac8c":82,"\ub123\ub294\ub2e4\uba74":81,"\ub123\uc73c\uba74":81,"\ub124\ubc88\uc9f8":79,"\ub192\ub2e4\ub294":75,"\ub192\uc774\uae30":76,"\ub204\ub974\ub294":77,"\ub204\ub974\uba74":78,"\ub208\uc0ac\ud0dc":82,"\ub208\uc0ac\ud0dc\ub97c":82,"\ub20c\ub7ec":79,"\ub20c\ub824\uc788\ub294":78,"\ub20c\ub838\ub2e4":78,"\ub20c\ub9ac\uc9c0":78,"\ub20c\ub9b0":78,"\ub294":76,"\ub2a5\ub825\uc774\ub2e4":82,"\ub2e4\ub8e8\uaca0\ub2e4":78,"\ub2e4\ub8e8\uae30":78,"\ub2e4\ub918\ub2e4":82,"\ub2e4\ub974\uac8c":78,"\ub2e4\ub974\ub2e4":77,"\ub2e4\ub974\uc9c0":79,"\ub2e4\ub978":[76,78,82],"\ub2e4\uc2dc":79,"\ub2e4\uc591\uc131":78,"\ub2e4\uc591\ud788":81,"\ub2e4\uc74c":[76,79,80,81],"\ub2e4\uc911":77,"\ub2e4\ud589\ud788":75,"\ub2e8":77,"\ub2e8\uacc4\ub85c":78,"\ub2e8\uacc4\ub97c":76,"\ub2e8\uacc4\uc5d0\uc11c\uc758":78,"\ub2e8\uc0c9":76,"\ub2e8\uc21c\ud558\uae30":81,"\ub2e8\uc21c\ud558\ub2e4":[79,80],"\ub2e8\uc21c\ud558\uc9c0\ub9cc":81,"\ub2e8\uc21c\ud788":[78,81],"\ub2e8\uc5b4\ub294":78,"\ub2e8\uc810\uc740":77,"\ub2e8\uc810\uc774":75,"\ub2ec\ub77c\uc9c0\ubbc0\ub85c":77,"\ub2ec\ub77c\uc9c4\ub2e4":81,"\ub2ec\ub77c\uc9c8":78,"\ub2f4\uc558\ub294\ub370":79,"\ub2f9\uc5f0\ud55c":76,"\ub300\ub2e8\ud788":[81,82],"\ub300\ub85c":81,"\ub300\ubcf4\uac8c":75,"\ub300\uc0c1\uc5d0":82,"\ub300\uccb4\ud558\ub294\uac00":76,"\ub300\ud55c":76,"\ub354":[75,78,79,82],"\ub370":76,"\ub370\uc5d0\ub9cc":80,"\ub370\uc774\ud130":[78,81],"\ub370\uc774\ud130\uac00":76,"\ub370\uc774\ud130\ub4e4\uc744":79,"\ub370\uc774\ud130\ub97c":79,"\ub3c4":[76,78],"\ub3c4\uc6c0":78,"\ub3c4\uc6c0\uc774":78,"\ub3c4\uc911":76,"\ub3c4\ud615":75,"\ub3c4\ud615\uc744":79,"\ub3d9\uae30\ubd80\uc5ec":78,"\ub3d9\uc2dc\uc5d0":75,"\ub3d9\uc77c\ud55c":[76,79,80],"\ub3d9\uc77c\ud574\uc57c":80,"\ub3d9\uc791\ud558\ub3c4\ub85d":78,"\ub3d9\uce58\uad00\uacc4\ub77c\ub294":75,"\ub418\uae30":78,"\ub418\ub3cc\uc544\uac00\uc57c":76,"\ub418\uc5b4\uc57c\ub9cc":77,"\ub418\uc5c8\ub294\uc9c0":78,"\ub418\uc9c0":79,"\ub41c":[78,80],"\ub41c\ub2e4":[75,76,77,78,79,80,81,82],"\ub41c\ub2e4\uba74":82,"\ub420":[75,77,79,80],"\ub450":[79,80,81,82],"\ub450\uaed8":79,"\ub450\uaed8\ub9cc":80,"\ub450\ub294":[79,80],"\ub450\ub294\ub370":76,"\ub450\uba74":77,"\ub450\ubc88\uc9f8":[78,79],"\ub450\uc5c8\ub2e4":76,"\ub458":75,"\ub458\uc9f8":[75,80],"\ub4a4\uc5d0":78,"\ub4b7\ubd80\ubd84\uc5d0":77,"\ub4e4\uba74":79,"\ub4e4\uc5b4\uc11c":76,"\ub4e4\uc744":80,"\ub4f1":[75,78],"\ub4f1\uc740":82,"\ub4f1\uc758":75,"\ub514\ub809\ud1a0\ub9ac\uc5d0":76,"\ub514\uc2a4\ud50c\ub808\uc774":75,"\ub514\uc790\uc778\ud560":79,"\ub51c\ub808\ub9c8\uac00":75,"\ub51c\ub808\ub9c8\ub97c":75,"\ub51c\ub808\uc774":77,"\ub530\ub77c":[77,81],"\ub530\uc62c":79,"\ub530\uc838\uc11c":79,"\ub54c":[76,77,79,81,82],"\ub54c\ub9c8\ub2e4":79,"\ub54c\ub9cc":76,"\ub54c\ubb38\uc5d0":[75,76,77,78,81],"\ub54c\ubb38\uc774\ub2e4":[76,77,78,80,81],"\ub54c\uc758":79,"\ub610\ub294":[75,79,82],"\ub610\ud55c":[77,81],"\ub611\uac19\uc740":78,"\ub73b\uc774\ub2e4":78,"\ub77c\uace0":[78,82],"\ub77c\ub294":[76,82],"\ub77c\uc774\ube0c\ub7ec\ub9ac\uc774\uae30":75,"\ub77c\uc774\ube0c\ub7ec\ub9ac\uc774\ub2e4":75,"\ub80c\ub354\ub9c1":79,"\ub85c\uc9c1\uc740":79,"\ub85c\uc9c1\uc744":[76,78],"\ub85c\uc9c1\uc774":77,"\ub8e8\ud2b82":77,"\ub97c":[76,77,78,80],"\ub9c8\ub77c":82,"\ub9c8\uc6b0\uc2a4":[75,78,80],"\ub9c8\uc6b0\uc2a4\uac00":80,"\ub9c8\uc9c0\ub9c9\uc5d0":76,"\ub9c8\uc9c0\ub9c9\uc73c\ub85c":[78,79],"\ub9c8\ucc2c\uac00\uc9c0\uc774\ub2e4":82,"\ub9cc":75,"\ub9cc\ub4dc\ub294":[75,80,82],"\ub9cc\ub4e0":75,"\ub9cc\ub4e0\ub2e4\uace0":75,"\ub9cc\ub4e4":[75,79,82],"\ub9cc\ub4e4\uace0":81,"\ub9cc\ub4e4\uc5b4":[79,81],"\ub9cc\ub4e4\uc5b4\uc11c":82,"\ub9cc\ub4e4\uc5b4\uc57c":80,"\ub9cc\ub4e4\uc5b4\uc9c4":81,"\ub9cc\ub4e4\uc5b4\uc9c4\ub2e4\uba74":82,"\ub9cc\ub4e4\uc5c8\ub2e4":79,"\ub9cc\uc57d":[76,79,80,82],"\ub9cc\uc744":[76,79],"\ub9ce\ub2e4":81,"\ub9ce\uc73c\ubbc0\ub85c":77,"\ub9ce\uc740":81,"\ub9d0\uc774\ub2e4":76,"\ub9d0\ud558\ub294":78,"\ub9d0\ud55c":76,"\ub9d0\ud588\ub2e4":82,"\ub9d0\ud588\ub4ef":76,"\ub9de\ub294\uac00":78,"\ub9de\uc744":76,"\ub9e4\uac1c":79,"\ub9e4\uc6b0":[78,82],"\uba39\ud788\ub294":76,"\uba3c\uc800":[78,79],"\uba54\ubaa8\ub9ac":75,"\uba64\ubc84":76,"\uba85\ub839\uc5b4\uac00":78,"\uba87":77,"\uba87\uba87":[76,78],"\ubaa8\ub2c8\ud130":76,"\ubaa8\ub450":[75,76],"\ubaa8\ub4c8\ub4e4\uc744":76,"\ubaa8\ub4e0":[75,76,78,81,82],"\ubaa9\ub85d\uc740":78,"\ubaa9\ud45c\uac00":75,"\ubab8\uc758":78,"\ubb34\uc5b8\uac00\ub97c":[76,78],"\ubb34\uc5c7\uc744":77,"\ubb34\uc5c7\uc774\ub4e0\uc9c0":82,"\ubb34\uc5c7\uc778\uac00":[77,79,82],"\ubb34\uc5c7\uc778\uc9c0\ub294":79,"\ubb34\uc791\uc704\ub85c":81,"\ubb34\ud55c":76,"\ubb36\uc744":80,"\ubb38\uad6c\uc774\ub2e4":76,"\ubb38\uc790\uc5f4\uc740":76,"\ubb38\uc790\uc5f4\uc774\ub2e4":76,"\ubb38\uc7a5\ub4e4":76,"\ubb38\uc81c\uac00":[76,77],"\ubb38\uc81c\ub97c":81,"\ubb3c\ub860":[75,76,77,78,79],"\ubb54\uac00":80,"\ubbf8\uce58\uac8c":82,"\ubc0f":78,"\ubc14\uafb8\uace0":77,"\ubc14\uafb8\ub294":77,"\ubc14\uafb8\ub294\uac00":79,"\ubc14\uafb8\uba74":81,"\ubc14\uafbc":78,"\ubc14\uafc0":77,"\ubc14\uafd4":79,"\ubc14\ub00c\ub294\uac00":77,"\ubc14\ub00c\ub294\uc9c0\ub97c":77,"\ubc14\ub294":79,"\ubc14\ub85c":[77,82],"\ubc18\ub4dc\uc2dc":[77,78],"\ubc18\ubcf5\ubb38":76,"\ubc18\uc601":78,"\ubc18\ud544\uc218\uc801\uc73c\ub85c":76,"\ubc18\ud658":76,"\ubc18\ud658\ud55c\ub2e4":[76,81],"\ubc1c\uc0dd":76,"\ubc1c\uc0dd\ud558\uba74":76,"\ubc1c\uc0dd\ud55c":76,"\ubc1c\uc804\ub41c":75,"\ubc1c\ud718\ub41c":82,"\ubc29\ubc95":78,"\ubc29\ubc95\uc740":[78,79],"\ubc29\ubc95\uc744":[77,78],"\ubc29\ubc95\uc774\ub2e4":79,"\ubc29\uc2dd":75,"\ubc29\ud5a5\uc73c\ub85c":78,"\ubc29\ud5a5\ud0a4\ub97c":78,"\ubc30\uc5f4\uacfc":81,"\ubc30\uc5f4\uc5d0\uc11c":81,"\ubc30\uc5f4\uc740":81,"\ubc30\uc5f4\uc744":[76,81],"\ubc30\uc5f4\uc774":81,"\ubc30\uc5f4\ucc98\ub7fc":81,"\ubc30\uc6b0\uace0":[78,82],"\ubc30\uc6b0\ub294":[75,76],"\ubc30\uc6b4\ub2e4":78,"\ubc30\ud2c0\uc2ed":75,"\ubc84\ud2bc":81,"\ubc84\ud2bc\ub4e4":83,"\ubc84\ud2bc\ub4e4\uc740":80,"\ubc84\ud2bc\ub4e4\uc744":80,"\ubc84\ud2bc\uc744":[77,80,81],"\ubc84\ud2bc\uc758":80,"\ubc88":77,"\ubc94\uc704\ub294":80,"\ubc95\uc744":81,"\ubcc0\uacbd\ud558\uba74\uc11c":79,"\ubcc0\uc218":[76,79,80],"\ubcc0\uc218\uac00":[76,77,79,80],"\ubcc0\uc218\ub294":[76,79],"\ubcc0\uc218\ub3c4":80,"\ubcc0\uc218\ub4e4\uc744":80,"\ubcc0\uc218\ub4e4\uc774":76,"\ubcc0\uc218\ub85c":[79,80],"\ubcc0\uc218\ub97c":[76,77,79,80],"\ubcc0\uc218\uc640":78,"\ubcc0\uc218\uc758":79,"\ubcc0\uc218\uc774\uace0":79,"\ubcc0\uc218\uc774\ub2e4":79,"\ubcc0\uc704\uac00":77,"\ubcc0\uc704\ub294":77,"\ubcc0\uc704\ub9cc":77,"\ubcc0\ud55c\ub2e4\uba74":79,"\ubcc0\ud560":79,"\ubcf4\uace0":75,"\ubcf4\ub2e4":77,"\ubcf4\uba74":[77,80],"\ubcf4\uc544\ub77c":[78,79,82],"\ubcf4\uc774\ub294":80,"\ubcf4\uc774\uc9c4":81,"\ubcf4\uc778\ub2e4":77,"\ubcf4\uc778\ub2e4\ub294":[75,77],"\ubcf4\uc77c":77,"\ubcf4\uc790":[75,82],"\ubcf4\ud1b5":75,"\ubcf5\uc18c\uc218\uc88c\ud45c\ub97c":75,"\ubcf5\uc7a1\ub3c4\ub294":77,"\ubcf5\uc7a1\ub3c4\ub97c":77,"\ubcf5\uc7a1\ud558\ub2e4":76,"\ubcf5\uc7a1\ud55c":75,"\ubcf8\uaca9\uc801\uc73c\ub85c":79,"\ubcfc\ub9cc":75,"\ubd80":83,"\ubd80\ubd84\uc5d0\uc11c":78,"\ubd80\ubd84\uc73c\ub85c":76,"\ubd80\ubd84\uc758":81,"\ubd80\uc5ec\ud560":82,"\ubd84\uc11d\ud558\uc9c0":77,"\ubd88\uacfc\ud558\ub2e4":78,"\ubd88\uacfc\ud558\ubbc0\ub85c":77,"\ubd88\ub9b4":78,"\ubd88\uc5f0\uc18d\uc801":79,"\ube14\ub85d":81,"\ube14\ub85d\uc744":81,"\ube14\ub85d\uc758":81,"\ube44\uad50\ud558\uc5ec":78,"\ube44\ud45c\uc900":75,"\ube44\ud558\uba74":76,"\ube48\ub3c4\uc5d0":77,"\ube60\ub978":75,"\ube60\ub97c\uae4c":77,"\ube60\uc9c0\uba74":75,"\ube68\uac04":[76,79,81],"\ube68\uac04\ube14\ub85d":15,"\ube68\ub9ac":75,"\ubfcc\uc694\ubfcc\uc694":75,"\uc0ac\ub78c\ub4e4\uc774":82,"\uc0ac\ub78c\uc774":82,"\uc0ac\uc2e4":81,"\uc0ac\uc6a9\ub418\uc5c8\uae30":80,"\uc0ac\uc6a9\ub418\uc5c8\uc9c0\ub9cc":80,"\uc0ac\uc6a9\ub41c":79,"\uc0ac\uc6a9\uc790\uac00":[76,80],"\uc0ac\uc6a9\ud558\uae30":76,"\uc0ac\uc6a9\ud558\ub294":76,"\uc0ac\uc6a9\ud558\uc5ec":76,"\uc0ac\uc6a9\ud560":76,"\uc0ac\uc774\uc5d0\ub294":75,"\uc0ac\uc774\uc758":75,"\uc0ac\uc9c4":75,"\uc0b4\ud3b4\ubcf4\uc790":76,"\uc0bd\uc785\ud558\uba74":76,"\uc0c1\uc138\ud558\uac8c":79,"\uc0c1\uc218":80,"\uc0c1\uc218\ub4e4\uc744":76,"\uc0c1\ud0dc":78,"\uc0c1\ud0dc\ub97c":75,"\uc0c1\ud638\uc791\uc6a9\uc774":78,"\uc0c8\ub85c\uc6b4":[79,81,82],"\uc0c9":[75,76,79],"\uc0c9\uc0c1":[76,79],"\uc0c9\uc0c1\uacfc":76,"\uc0c9\uc0c1\uc740":[76,81],"\uc0c9\uc0c1\uc744":[76,81],"\uc0c9\uc0c1\uc758":81,"\uc0c9\uc774":76,"\uc0dd\uac01\uc77c":80,"\uc0dd\uac01\ud574":82,"\uc0dd\uac01\ud574\ubcf4\uba74":78,"\uc0dd\uac01\ud574\ubd10\ub77c":78,"\uc0dd\uacbc\ub2e4":78,"\uc0dd\uae38":76,"\uc0dd\uc131\ud558\uace0":76,"\uc11c\ub85c":76,"\uc120\uc5b8":76,"\uc120\uc5b8\ub418\uc5b4\uc57c":76,"\uc120\ud0dd\uc801\uc73c\ub85c":75,"\uc120\ud0dd\uc801\uc778":77,"\uc120\ud0dd\uc9c0\ub294":81,"\uc120\ud589\ub418\uc5b4\uc57c":79,"\uc124\uba85\uc774":76,"\uc124\uba85\uc774\uc5c8\ub2e4":76,"\uc124\uba85\ud558\ub294":79,"\uc124\uba85\ud560":81,"\uc124\uc815":75,"\uc131\ubd84":76,"\uc131\ubd84\uc774":76,"\uc138\uace0":81,"\uc138\ud305":78,"\uc148\uc774\ub2e4":76,"\uc18c\uac1c":83,"\uc18c\ub9ac":[75,78],"\uc18c\uc124":82,"\uc18c\uc2a4":[75,76,77,78],"\uc18c\uc2a4\ucf54\ub4dc":76,"\uc18c\uc2a4\ucf54\ub4dc\uac00":76,"\uc18c\uc2a4\ucf54\ub4dc\ub294":76,"\uc18c\uc2a4\ucf54\ub4dc\ub97c":[75,76],"\uc18c\uc2a4\ucf54\ub4dc\uc5d0":76,"\uc18c\uc2a4\ucf54\ub4dc\uc640":75,"\uc18c\uc2a4\ucf54\ub4dc\uc758":76,"\uc18c\uc2a4\ud30c\uc77c\uc5d0":75,"\uc18c\uc2a4\ud30c\uc77c\uc740":75,"\uc18c\uc9c0\uac00":80,"\uc18d":76,"\uc18d\ub3c4\ub77c\ub294":77,"\uc18d\ub3c4\ub97c":77,"\uc18d\ub3c4\uc5d0":77,"\uc18d\uc5d0\ub294":77,"\uc190\uac00\ub77d":78,"\uc190\uc744":75,"\uc218":[75,76,77,78,79,80,81,82],"\uc218\uac00":82,"\uc218\ub294":[77,82],"\uc218\ub3c4":82,"\uc218\ub97c":81,"\uc218\ub9ce\uc740":75,"\uc218\uc815\ud558\ub294":76,"\uc218\uc815\ud55c\ub2e4\uba74":76,"\uc218\uc900":75,"\uc218\ud589\ub418\uc5b4\uc57c":76,"\uc218\ud589\ud55c\ub2e4":76,"\uc21c\ucc28\uc801\uc73c\ub85c":76,"\uc26c\uc6b4":79,"\uc27d\uac8c":[77,79],"\uc27d\ub2e4":[77,78,80],"\uc2a4\ud06c\ub9b0":76,"\uc2b5\ub4dd\ud560":82,"\uc2dc\uac01\uc801":81,"\uc2dc\uac01\ud654":[78,79],"\uc2dc\uac04":[77,78,81],"\uc2dc\uac04\ubcf4\ub2e4":82,"\uc2dc\uac04\uc21c\uc73c\ub85c":76,"\uc2dc\uac04\uc740":77,"\uc2dc\uac04\uc744":[77,82],"\uc2dc\uac04\uc774":[76,82],"\uc2dc\uac04\uc774\ub2e4":82,"\uc2dc\ub3c4\ud574":75,"\uc2dc\uc2a4\ud15c\uc744":78,"\uc2dc\uc2a4\ud15c\uc774":76,"\uc2dc\uc791\ub418\uae30":77,"\uc2dc\uc791\ub420":77,"\uc2dc\uc791\ud558\ub294":77,"\uc2dc\ud589":82,"\uc2dd\uc758":75,"\uc2e0\uacbd":80,"\uc2e4\uc81c\ub85c":76,"\uc2e4\ud589":[76,77,78,81],"\uc2e4\ud589\ub418\uac70\ub098":75,"\uc2e4\ud589\ub418\ub294":[75,77],"\uc2e4\ud589\ub418\uba74":76,"\uc2e4\ud589\ub418\uc57c":77,"\uc2e4\ud589\ub418\uc5b4\uc57c":76,"\uc2e4\ud589\ub418\uc9c0":76,"\uc2e4\ud589\ub41c\ub2e4":76,"\uc2e4\ud589\ub41c\ub2e4\ub294":75,"\uc2e4\ud589\ub428":75,"\uc2e4\ud589\ud558\ub294":75,"\uc2eb\ub2e4\uba74":80,"\uc2ec\ud654":78,"\uc2ec\ud654\ub41c":78,"\uc2f6\ub2e4\uba74":75,"\uc2f6\uc744":76,"\uc368\uc57c":80,"\uc4f0\uba74":76,"\uc4f8":75,"\uc544\ub2c8\ub2e4":78,"\uc544\ub2c8\ub77c":77,"\uc544\ub2c8\ubbc0\ub85c":77,"\uc544\ub2c8\uc9c0\ub9cc":78,"\uc544\ub2cc":[75,76,77,79,80,81],"\uc544\ub2cc\uc9c0\ub97c":78,"\uc544\ub798\ub97c":79,"\uc544\ub798\uc640":79,"\uc544\ub798\ucabd\uc774":76,"\uc544\ub9c8\ub3c4":78,"\uc544\ubb34":82,"\uc544\ubb34\ub798\ub3c4":75,"\uc544\uc2a4\ud0a4\uc544\ud2b8\ub97c":75,"\uc544\uc774\ub514\uc5b4\ub294":79,"\uc544\uc774\ub514\uc5b4\ub97c":79,"\uc544\uc774\ub514\uc5b4\uc640":79,"\uc544\uc774\ub514\uc5b4\ucc98\ub7fc":80,"\uc544\uc9c1":[79,80],"\uc544\uc9c1\ub3c4":[77,80],"\uc548\ub418\ub294":77,"\uc548\ub41c\ub2e4":76,"\uc54a\ub294":[76,77],"\uc54a\ub294\ub2e4":[77,78,79,81],"\uc54a\ub2e4":79,"\uc54a\ub2e4\uba74":80,"\uc54a\uc558\uc9c0\ub9cc":78,"\uc54a\uc73c\uba74":76,"\uc54a\uc744":[76,77],"\uc54c":[76,80,81],"\uc54c\uace0":[77,78],"\uc54c\uace0\ub9ac\uc998\uc73c\ub85c":75,"\uc54c\uace0\ub9ac\uc998\uc758":78,"\uc54c\uace0\ub9ac\uc998\uc774":78,"\uc54c\ub809\uc138\uc774":82,"\uc54c\uc544\ub0b4\uae30":80,"\uc54c\uc544\ub0bc":[76,77],"\uc54c\uc544\uc57c":78,"\uc54c\uc558\ub2e4":77,"\uc54c\uce74\ub178\uc774\ub4dc\uc758":77,"\uc55e\ubd80\ubd84\uc5d0":77,"\uc55e\uc11c":76,"\uc55e\uc11c\uc11c":76,"\uc55e\uc5d0":78,"\uc560\ub2c8\uba54\uc774\uc158":[75,77],"\uc57d\uac04":78,"\uc57d\uac04\uc758":76,"\uc5b4\ub5a4":[76,82],"\uc5b4\ub5a4\uac00":79,"\uc5b4\ub5a8\uae4c":[76,80],"\uc5b4\ub5bb\uac8c":[76,77,79,80,81],"\uc5b4\ub835\uc9c0":76,"\uc5b4\uca0c\ub4e0":78,"\uc5b4\ucc0c\ub410\ub4e0":76,"\uc5b8\uae09\ud558\uaca0\ub2e4":76,"\uc5b8\uae09\ud55c":81,"\uc5b8\uc5b4\uc758":76,"\uc5bc\ub9c8\ub098":77,"\uc5bc\ub9cc\ud07c\uc758":82,"\uc5c5\ub370\uc774\ud2b8":77,"\uc5c5\ub370\uc774\ud2b8\uac00":76,"\uc5c5\ub370\uc774\ud2b8\ub418\uac70\ub098":76,"\uc5c5\ub370\uc774\ud2b8\ub418\uac8c":77,"\uc5c5\ub370\uc774\ud2b8\ub418\uc5c8\ub294\uc9c0\ub97c":77,"\uc5c5\ub370\uc774\ud2b8\ub41c\ub2e4":78,"\uc5c5\ub370\uc774\ud2b8\ud558\ub294":[77,78,80],"\uc5c6\uace0":[78,80],"\uc5c6\uae30":[76,77,78],"\uc5c6\ub294":[75,76,82],"\uc5c6\ub2e4":[77,78,81],"\uc5c6\uc73c\ubbc0\ub85c":[77,80],"\uc5c6\uc774":[76,78],"\uc5d0":76,"\uc5d0\uc11c":[78,82],"\uc5d0\uc120":76,"\uc5d0\ud544\ub85c\uadf8":83,"\uc5d4\uc9c4":75,"\uc5d4\uc9c4\uc5d0\ub3c4":75,"\uc5d4\uc9c4\uc740":75,"\uc5d4\uc9c4\uc744":75,"\uc5d4\uc9c4\uc758":75,"\uc5d4\ud130":75,"\uc5ec\uae30\uc11c":77,"\uc5ec\uae30\uc5d0":76,"\uc5ec\uae30\uc5d0\uc11c":82,"\uc5ec\ub7ec":75,"\uc5ec\ub7ec\uac00\uc9c0":76,"\uc5ec\uc804\ud788":79,"\uc5ec\uc9c0\uac00":81,"\uc5f0\uacb0\uc2dc\ud0a4\uba74\uc11c":82,"\uc5f0\uacb0\uc810\uc774":75,"\uc601\uc5ed":80,"\uc601\uc5ed\uacfc":80,"\uc601\uc5ed\uc744":80,"\uc601\uc5ed\uc774":80,"\uc601\ud5a5\uc744":82,"\uc601\ud654":82,"\uc608\ub97c":[76,79],"\uc608\uc2dc":75,"\uc608\uc2dc\uc774\ub2e4":82,"\uc608\uc678\uc801\uc73c\ub85c":76,"\uc608\uc81c\uc778":76,"\uc608\uce21\ub418\uae30":77,"\uc624\uac8c":76,"\uc624\uac8c\ub054":76,"\uc624\ub2f5\uc774\ub77c\uba74":81,"\uc624\ub2f5\uc77c":81,"\uc624\ub798":76,"\uc624\ub978\ucabd\uc774":76,"\uc624\ube0c\uc81d\ud2b8\uac04":78,"\uc624\ube0c\uc81d\ud2b8\uc758":77,"\uc624\uc9c1":[79,80],"\uc624\ud574\uc758":80,"\uc640":[76,77,78,80,82],"\uc640\uc57c":77,"\uc644\ubcbd\ud788":82,"\uc644\uc131\ub418\uc5c8\ub2e4":78,"\uc644\uc804\ud55c":80,"\uc644\uc804\ud788":78,"\uc65c":78,"\uc65c\ub098\ud558\uba74":76,"\uc65c\ub0d0\uba74":78,"\uc65c\ub0d0\ud558\uba74":[77,78],"\uc678\ubd80":75,"\uc678\uc758":76,"\uc694\uc18c":[78,81],"\uc694\uc18c\ub97c":76,"\uc694\uc57d\ud558\uc790\uba74":75,"\uc6a9":80,"\uc6a9\ub3c4\uac00":79,"\uc6a9\uc774\ub2e4":81,"\uc6b0\ub9ac\uac00":77,"\uc6b0\ub9ac\ub294":[77,78,81,82],"\uc6b0\ub9ac\uc758":82,"\uc6b0\uc120":[76,77,78,79,81],"\uc6c0\uc9c1\uc774\uac8c":77,"\uc6c0\uc9c1\uc774\ub294":77,"\uc6c0\uc9c1\uc778\ub2e4":[77,78],"\uc6c0\uc9c1\uc778\ub2e4\ub294":78,"\uc6c0\uc9c1\uc77c\uae4c":77,"\uc6d0\ub9ac\ub97c":76,"\uc704":79,"\uc704\uce58":[76,79],"\uc704\uce58\uac00":77,"\uc704\uce58\ub97c":76,"\uc704\uce58\uc5d0\uc11c\uc758":80,"\uc704\ud55c":[75,76,80,81],"\uc704\ud574":[76,78,82],"\uc704\ud574\uc11c\ub294":78,"\uc704\ud574\uc120":76,"\uc708\ub3c4\uc6b0":76,"\uc708\ub3c4\uc6b0\uc758":77,"\uc720\ub2c8\ud2f0":75,"\uc720\uc6a9\ud55c":76,"\uc73c\ub85c":[75,76],"\uc740":77,"\uc744":[77,78,81],"\uc74c\uc545":82,"\uc758":75,"\uc758\ubbf8\ub97c":80,"\uc758\ubbf8\ud558\uac8c":[77,80],"\uc758\ubbf8\ud558\uace0":78,"\uc758\ubbf8\ud558\uc9c0\ub294":78,"\uc758\ubbf8\ud55c\ub2e4":[76,77,78],"\uc774":[75,76,77,78,79,80,81,82],"\uc774\uac83\ub4e4\uc740":79,"\uc774\uac83\uc740":[76,77,81],"\uc774\uac83\uc774":[75,76,82],"\uc774\ub294":[77,78],"\uc774\ub2e4":76,"\uc774\ub3d9":83,"\uc774\ub780":75,"\uc774\ub7f0":[75,78],"\uc774\ub807\uac8c":75,"\uc774\ub8e8\uc5b4\uc9c4\ub2e4":[76,79],"\uc774\ub8e8\uc5b4\uc9d0\uc5d0":78,"\uc774\ub97c":77,"\uc774\ub984":78,"\uc774\ub984\uc73c\ub85c\ub294":78,"\uc774\ub984\uc744":78,"\uc774\ub984\uc758":76,"\uc774\ubbf8":82,"\uc774\ubbf8\uc9c0":[77,78,81],"\uc774\ubca4\ud2b8\uac00":[76,78],"\uc774\ubca4\ud2b8\ub4e4\uc740":76,"\uc774\ubca4\ud2b8\ub4e4\uc744":76,"\uc774\ubca4\ud2b8\ub4e4\uc758":76,"\uc774\ubca4\ud2b8\ub97c":[76,78,80],"\uc774\ubca4\ud2b8\uc801":75,"\uc774\ubcc4\uc744":76,"\uc774\uc0c1":[75,77],"\uc774\uc0c1\uc758":78,"\uc774\uc5d0":76,"\uc774\uc6a9\ud558\uba74":77,"\uc774\uc6a9\ud55c":75,"\uc774\uc6a9\ud560":78,"\uc774\uc720\ub294":80,"\uc774\uc720\uc774\ub2e4":[75,78,82],"\uc774\uc804":[77,78],"\uc774\uc804\uacfc":79,"\uc774\uc804\ubd80\ud130":78,"\uc774\uc804\uc5d0\ub294":78,"\uc774\uc804\uc758":[76,78,80],"\uc774\uc81c":[77,78,80,81,82],"\uc774\uc81c\ub294":79,"\uc774\uc820":77,"\uc774\ud574\ud558\ub294":76,"\uc774\ud574\ud560":[78,79],"\uc774\ud574\ud588\ub2e4\uba74":76,"\uc774\ud6c4":76,"\uc774\ud6c4\uc5d0":76,"\uc778\uc790\ub97c":78,"\uc778\ud130\ud398\uc774\uc2a4":78,"\uc778\ud130\ud398\uc774\uc2a4\ub97c":81,"\uc77c\ubc18\uc801\uc73c\ub85c":76,"\uc77c\ubd80":[76,77,78],"\uc77c\ubd80\ubd84":78,"\uc77c\ubd80\uc774\uae30":75,"\uc77c\uc5b4\ub0ac\ub294\uc9c0":80,"\uc77c\uc73c\ud0a4\ub294":82,"\uc77c\uc885\uc758":77,"\uc77c\uce58\ud558\uc9c0":76,"\uc784\ub9c8\ub204\uc5d8":82,"\uc784\uc740":77,"\uc785\ub825":[75,76,77,80],"\uc785\ub825\ubcf4\ub2e4":82,"\uc785\ub825\uc2dc\ud0a4\ub294":82,"\uc785\ub825\uc740":[75,80],"\uc785\ub825\uc744":[78,80],"\uc785\ub825\uc774":[77,78],"\uc785\ub825\uc774\ub098":77,"\uc785\ub825\uc774\ub780":80,"\uc785\ub825\ud558\ub294":78,"\uc785\ubb38\uc7a5\ubcbd\uc774":75,"\uc788\uac8c":[75,76],"\uc788\uace0":79,"\uc788\uae30":78,"\uc788\ub290\ub0d0":78,"\uc788\ub294":[75,76,80,81,82],"\uc788\ub294\ub370":79,"\uc788\ub2e4":[75,76,77,78,79,80,81,82],"\uc788\ub2e4\uace0":76,"\uc788\ub3c4\ub85d":79,"\uc788\uc5b4\uc57c":77,"\uc788\uc73c\uba74":75,"\uc788\uc744\uae4c":[75,76],"\uc788\uc74c":80,"\uc788\uc74c\uc744":[77,78,80],"\uc790\ub3d9\uc801\uc73c\ub85c":[76,77],"\uc790\uc8fc":77,"\uc791\ub3d9":76,"\uc791\ub3d9\ud558\uc9c0":77,"\uc791\uc131\ub418\uc5b4\uc57c":76,"\uc791\uc131\uc740":81,"\uc791\uc131\ud558\ub294":75,"\uc791\uc131\ud55c":75,"\uc791\uc5c5\uc774":76,"\uc791\uc740":[79,80],"\uc7a5":77,"\uc7a5\uc774":77,"\uc7a5\uc810\ub3c4":75,"\uc7a5\uc810\uc740":75,"\uc7a5\uc810\uc744":75,"\uc800\uae09":75,"\uc800\uc7a5\ud574":76,"\uc801\uc6a9\ub418\uc5b4":80,"\uc801\uc808\ud55c":[76,77,78,80],"\uc801\uc808\ud788":76,"\uc804\uc138\uacc4":82,"\uc804\uc5ed":76,"\uc804\uccb4":[76,81],"\uc804\uccb4\ub97c":79,"\uc804\ud600":78,"\uc808\ucc28\uc801\uc73c\ub85c":75,"\uc808\ucc28\uc801\uc774":75,"\uc810\uc218\ub3c4":78,"\uc810\uc740":80,"\uc811\uadfc\uc131\uc774":75,"\uc811\uadfc\ud560":75,"\uc815\ub2f5\uc774\uac70\ub098":81,"\uc815\ub2f5\uc774\ub77c\uba74":81,"\uc815\ub82c\ub41c\ub2e4":76,"\uc815\ub9ac\ub97c":77,"\uc815\ubcf4\ub4e4\uc740":76,"\uc815\ubcf4\ub97c":76,"\uc815\uc0ac\uac01\ud615":80,"\uc815\uc0ac\uac01\ud615\uc744":80,"\uc815\uc218":[79,81],"\uc815\uc2e0\uc5c6\uc774":77,"\uc815\uc758":78,"\uc815\uc911\uc559\uc5d0":76,"\uc815\uc911\uc559\uc73c\ub85c":76,"\uc815\uc911\uc559\uc740":76,"\uc815\uc911\uc559\uc744":76,"\uc815\uc9c0\ub41c":77,"\uc815\ud558\uace0":76,"\uc815\ud558\ub294":79,"\uc815\ud55c\ub2e4":76,"\uc815\ud560":[76,77],"\uc815\ud574\uc57c":76,"\uc815\ud574\uc838\uc57c":76,"\uc815\ud655\ud788\ub294":76,"\uc81c\uacf5\ud558\uae30":75,"\uc81c\uc57d\uc870\uac74":78,"\uc81c\uc678\ud654\uba74":75,"\uc81c\uc791":75,"\uc81c\ud55c\uc744":81,"\uc870\uac74\ubb38\uc774":76,"\uc870\uac74\uc774":76,"\uc870\uc808\ud558\uac8c":79,"\uc870\uc885":83,"\uc874\uc7ac\ud558\uace0":80,"\uc874\uc7ac\ud558\uae30":77,"\uc874\uc7ac\ud568\uc744":80,"\uc885\ub8cc":77,"\uc885\ub8cc\ub418\uac8c":76,"\uc885\ub8cc\ub418\uace0":78,"\uc885\ub8cc\ub418\uc5b4\uc57c":76,"\uc885\ub8cc\ub41c":76,"\uc885\ub8cc\ud558\uace0":76,"\uc885\ub8cc\ud558\ub294":77,"\uc885\ub958\uc758":76,"\uc88b\ub2e4\ub294":75,"\uc88b\uc740":[75,80],"\uc88b\uc744":79,"\uc88c\ud45c":76,"\uc88c\ud45c\ub97c":[78,79],"\uc88c\ud45c\uc5d0":77,"\uc8fc\ub294":75,"\uc8fc\ub85c":76,"\uc8fc\ubaa9\ud574\ub77c":78,"\uc8fc\uc5b4\uc9c4":76,"\uc904\ub4e4\uc740":78,"\uc911":[75,77],"\uc911\uc694\ud55c":82,"\uc990\uae38":78,"\uc99d\uac00\uc2dc\ud0a4\uac70\ub098":80,"\uc99d\uac00\ud558\uace0":81,"\uc9c0\uae08\uae4c\uc9c0":78,"\uc9c0\uae08\uc740":78,"\uc9c0\ub294":80,"\uc9c0\uc2dd\ub9cc\uc73c\ub85c":82,"\uc9c0\uc2dd\ubcf4\ub2e4":82,"\uc9c0\uc2dd\uc5d0":82,"\uc9c0\uc2dd\uc744":82,"\uc9c0\uc5d0":81,"\uc9c0\uc6b0\ub294":75,"\uc9c0\uc810\uc744":80,"\uc9c1\uad00\uc801\uc73c\ub85c":78,"\uc9c1\uad00\uc801\uc778":78,"\uc9c1\uc0ac\uac01\ud615":76,"\uc9c1\uc0ac\uac01\ud615\ub4e4\uc5d0":79,"\uc9c1\uc0ac\uac01\ud615\ub4e4\uc744":79,"\uc9c1\uc0ac\uac01\ud615\uc744":79,"\uc9c1\uc811":79,"\uc9c4\uc9dc":78,"\uc9c4\ud589\ub420":76,"\uc9c8":81,"\uc9c8\ub9b0\ub2e4\uba74":75,"\uc9d1\uc911\ud558\uba74":75,"\uc9dc\uc99d\ub0a0":81,"\ucc28\uc774\uc810\uc774":78,"\ucc28\uc774\uc810\uc774\ub2e4":78,"\ucc29\uc624\ub97c":82,"\ucc38\uace0":[76,77,78,79,80,81],"\ucc3d\uc758\uc801\uc778":82,"\ucc3e\uc544\ub0b4\uba74":77,"\ucc3e\uc744":79,"\ucc44\ub85c":78,"\ucc44\uc6b0\ub294":76,"\ucc98\ub9ac":[75,76,78],"\ucc98\ub9ac\uac00":[76,77],"\ucc98\ub9ac\ub294":75,"\ucc98\ub9ac\ub97c":79,"\ucc98\ub9ac\ub9cc\uc774":80,"\ucc98\ub9ac\uc758":76,"\ucc98\ub9ac\ud558\uace0":76,"\ucc98\ub9ac\ud558\ub294":80,"\ucc98\ub9ac\ud558\ub294\uc9c0\ub294":77,"\ucc98\ub9ac\ud558\ub824\uba74":81,"\ucc98\ub9ac\ud560":76,"\ucc9c\uc7ac\uc131\uc774\ub780":82,"\uccab\ubc88\uc9f8":79,"\uccab\uc9f8":[75,80],"\uccab\uc9f8\ub294":78,"\uccab\uc9f8\ub85c":76,"\uccb4\ud06c\ud558\ub294":76,"\ucd08\uae30\ud654\ub418\uac70\ub098":76,"\ucd08\uae30\ud654\ub41c\ub2e4":76,"\ucd08\uae30\ud654\ub428\uc744":77,"\ucd08\ub85d":76,"\ucd1d":79,"\ucd5c\ub300":79,"\ucd5c\ub300\uac12\uc774":79,"\ucd5c\ub300\ud55c":75,"\ucd5c\uc18c":76,"\ucd94\uac00":77,"\ucd94\uac00\ub418\uba74":81,"\ucd94\uac00\ub418\uc5c8\uace0":77,"\ucd94\uac00\ub418\uc5c8\ub2e4":[77,80],"\ucd94\uac00\ub41c":76,"\ucd94\uac00\ub41c\ub2e4":78,"\ucd94\uac00\uc801\uc73c\ub85c":78,"\ucd94\uac00\uc801\uc778":[76,77,78,79],"\ucd94\uac00\ud558\uac70\ub098":76,"\ucd94\uac00\ud558\ub294":[76,78],"\ucd94\uac00\ud558\ub824":81,"\ucd94\uac00\ud574\ubcf4\uc790":78,"\ucd94\uac00\ud574\uc57c":77,"\ucd9c\ub825":[75,78,79,81,83],"\ucd9c\ub825\ub418\uac8c":76,"\ucd9c\ub825\ub418\ub294":76,"\ucd9c\ub825\ub420":79,"\ucd9c\ub825\uc6a9\uc774\ub2e4":80,"\ucd9c\ub825\uc73c\ub85c":75,"\ucd9c\ub825\uc740":[75,78],"\ucd9c\ub825\uc744":[76,77,80],"\ucd9c\ub825\uc758":78,"\ucd9c\ub825\uc774":82,"\ucd9c\ub825\ud558\uae30":76,"\ucd9c\ub825\ud558\ub294":[76,78,79,81],"\ucd9c\ub825\ud55c\ub2e4":81,"\ucd9c\ub825\ud574\uc57c":81,"\ucda9\ub3cc":75,"\ucda9\ubd84\ud788":79,"\uce58\uace4":76,"\uce5c\uc219\ud55c":76,"\uce60\ud558\uae30":75,"\uce78\ud2b8\ub294":82,"\uce94\ubc84\uc2a4":[76,79],"\uce94\ubc84\uc2a4\ub97c":76,"\uce94\ubc84\uc2a4\uc5d0":76,"\uce94\ubc84\uc2a4\uc758":76,"\ucea1\uc158\uc5d0":76,"\ucee4\uc9c0\uac8c":82,"\ucee8\ud150\uce20":78,"\ucef4\ud4e8\ud130\ub294":82,"\ucef4\ud4e8\ud130\ub9c8\ub2e4":77,"\ucef4\ud4e8\ud130\uc5d0\uac8c":82,"\ucf54\ub4dc":[76,77,78,79,80,81],"\ucf54\ub4dc\uac00":76,"\ucf54\ub4dc\ub97c":75,"\ucf54\ub4dc\uc640":76,"\ucf54\ub529\ud558\uac8c":75,"\ucf54\ub529\ud574":75,"\ucf58\uc194":75,"\ucf58\uc194\uc5d0\uc11c":75,"\ud06c\uac8c":[79,81],"\ud06c\uace0":76,"\ud06c\uae30":76,"\ud06c\uae30\uac00":76,"\ud06c\uae30\ub97c":[76,81],"\ud06c\uae30\uc640":76,"\ud06c\ub2e4\ub294":82,"\ud06c\uba74":77,"\ud070":[75,78,79,80],"\ud074\uae4c":82,"\ud074\ub9ad":80,"\ud074\ub9ad\uc774":80,"\ud074\ub9ad\ud588\ub2e4":80,"\ud07c\uc744":76,"\ud0a4":78,"\ud0a4\ub294":78,"\ud0a4\ub4e4\ub3c4":78,"\ud0a4\ub97c":78,"\ud0a4\ubcf4\ub4dc":[75,78,79],"\ud0a4\ubcf4\ub4dc\uac00":80,"\ud0a4\ubcf4\ub4dc\uc5d0":78,"\ud0a4\uc758":78,"\ud0c4\ucc3d\uc5d0\uc11c":79,"\ud14c\ub450\ub9ac\ub97c":[79,81],"\ud14c\ud2b8\ub9ac\uc2a4\ub97c":82,"\ud14d\uc2a4\ud2b8":[76,83],"\ud14d\uc2a4\ud2b8\uac00":[76,79],"\ud14d\uc2a4\ud2b8\ub294":76,"\ud14d\uc2a4\ud2b8\ub97c":[76,77,79],"\ud14d\uc2a4\ud2b8\uc758":[76,77],"\ud14d\uc2a4\ud2b8\uc774\ub2e4":79,"\ud1b5\ud574":80,"\ud22c\uc790\ud588\uc744\uae4c":82,"\ud234\uc774":75,"\ud29c\ud1a0\ub9ac\uc5bc":[15,81],"\ud29c\ud1a0\ub9ac\uc5bc\uc740":82,"\ud2b8\ub9ac\uac70":78,"\ud2b8\ub9ac\uac70\ub418\uba74":76,"\ud2b9\uc131":[76,81],"\ud2b9\uc131\uc774":82,"\ud2b9\uc131\uc774\ub2e4":82,"\ud2b9\uc774\ud55c":80,"\ud2b9\uc815":[76,77,80],"\ud2b9\uc815\ud55c":80,"\ud30c\uc774\uac8c\uc784":[76,81],"\ud30c\uc774\uac8c\uc784\uc740":[75,76],"\ud30c\uc774\uac8c\uc784\uc744":75,"\ud30c\uc774\uac8c\uc784\uc758":[75,76,77,82],"\ud30c\uc774\uac8c\uc784\uc774":[75,76],"\ud30c\uc774\uc36c\uc5d0":75,"\ud30c\uc774\uc36c\uc758":[75,76],"\ud30c\uc77c":75,"\ud30c\uc77c\ub85c":81,"\ud30c\uc77c\uc744":[75,76],"\ud30c\uc77c\uc774\ub098":75,"\ud30c\uc9c0\ud2b8\ub178\ud504\uac00":82,"\ud310\ub2e8\ud558\ub294":78,"\ud310\uc815":80,"\ud3ec\ud568":82,"\ud3ed\ub113\uc740":82,"\ud3f0\ud2b8":76,"\ud3f0\ud2b8\ub97c":76,"\ud3f0\ud2b8\uc640":76,"\ud479":75,"\ud48d\uc131\ud558\uac8c":78,"\ud504\ub85c\uadf8\ub798\uba38\uac00":75,"\ud504\ub85c\uadf8\ub798\uba38\ub294":75,"\ud504\ub85c\uadf8\ub798\ubc0d":76,"\ud504\ub85c\uadf8\ub798\ubc0d\uacfc":82,"\ud504\ub85c\uadf8\ub798\ubc0d\uc740":82,"\ud504\ub85c\uadf8\ub798\ubc0d\uc758":82,"\ud504\ub85c\uadf8\ub7a8":[76,77],"\ud504\ub85c\uadf8\ub7a8\uacfc":75,"\ud504\ub85c\uadf8\ub7a8\uc5d0":81,"\ud504\ub85c\uadf8\ub7a8\uc5d0\uc120":78,"\ud504\ub85c\uadf8\ub7a8\uc740":[81,82],"\ud504\ub85c\uadf8\ub7a8\uc744":[76,77,79,82],"\ud504\ub85c\uadf8\ub7a8\uc758":[75,76,78],"\ud504\ub85c\uadf8\ub7a8\uc774":79,"\ud504\ub85c\uc81d\ud2b8":77,"\ud504\ub85c\uc81d\ud2b8\uac00":[76,78],"\ud504\ub85c\uc81d\ud2b8\ub294":[76,77,78],"\ud504\ub85c\uc81d\ud2b8\ub85c":76,"\ud504\ub85c\uc81d\ud2b8\uc5d0":77,"\ud504\ub85c\uc81d\ud2b8\uc5d0\uc11c":76,"\ud504\ub85c\uc81d\ud2b8\uc640":78,"\ud504\ub85c\uc81d\ud2b8\uc740":75,"\ud504\ub85c\uc81d\ud2b8\uc758":[76,77,78],"\ud504\ub85c\uc81d\ud2b8\uc774\uba70":76,"\ud504\ub864\ub85c\uadf8":83,"\ud504\ub864\ub85c\uadf8\uc5d0\uc11c":81,"\ud504\ub9b0\ud2b8":80,"\ud50c\ub808\uc774":[78,82],"\ud53c\ud0c0\uace0\ub77c\uc2a4":77,"\ud544\uc218\uc870\uac74\uc774":78,"\ud544\uc218\uc870\uac74\uc774\uae30":78,"\ud544\uc694":[75,76],"\ud544\uc694\ub3c4":81,"\ud544\uc694\ub85c":76,"\ud544\uc694\ud558\uac8c":80,"\ud544\uc694\ud558\ub2e4":[76,78],"\ud544\uc694\ud560":77,"\ud558\uaca0\ub2e4":79,"\ud558\uace0":81,"\ud558\uae30":[76,78],"\ud558\uae30\ub9cc":79,"\ud558\ub098":75,"\ud558\ub098\ub85c":80,"\ud558\ub098\ub9cc":81,"\ud558\ub098\uc758":[75,76,77,79,82],"\ud558\ub098\uc774\ub2e4":77,"\ud558\ub294":[76,77,80],"\ud558\ub294\ub370":80,"\ud558\ub294\uc9c0":76,"\ud558\ub2e4":75,"\ud558\uba74":[76,77,79],"\ud558\uc580":79,"\ud558\uc580\uc0c9":76,"\ud558\uc600\ub2e4":79,"\ud558\uc9c0\ub9cc":[75,76,77,79,80,82],"\ud55c":[77,80,82],"\ud55c\uad6d\uc5b4":15,"\ud55c\ub2e4":[76,77,78,79,80,81],"\ud55c\ubc88\ub9cc":76,"\ud55c\ubc88\ucbe4\uc740":75,"\ud560":[76,77,79,82],"\ud560\uae4c":79,"\ud560\ub2f9\ub41c":76,"\ud568\uc218":[76,77],"\ud568\uc218\uac00":[76,77],"\ud568\uc218\ub294":[76,77,80,81],"\ud568\uc218\ub4e4\uacfc":75,"\ud568\uc218\ub4e4\uc740":[75,76],"\ud568\uc218\ub4e4\uc744":[75,76],"\ud568\uc218\ub4e4\uc774":75,"\ud568\uc218\ub85c":75,"\ud568\uc218\ub97c":[76,79],"\ud568\uc218\ubcf4\ub2e4":77,"\ud568\uc218\uc5d0\uc120":79,"\ud568\uc218\uc640":[76,77],"\ud568\uc218\uc758":76,"\ud568\uc218\uc774\uae30":76,"\ud568\uc218\uc774\ub2e4":77,"\ud568\uc218\ud654":78,"\ud568\uc218\ud654\ub97c":79,"\ud56d\uc0c1":[75,76,78,80],"\ud574":75,"\ud574\uacb0\ud560":75,"\ud574\ub2f9":78,"\ud574\ub2f9\ub418\ub294":77,"\ud574\uc11c":81,"\ud574\uc57c":[76,79],"\ud5f7\uac08\ub9ac\uba74":76,"\ud604\uc7ac":79,"\ud615\uc2dd\uc5d0":76,"\ud615\uc2dd\uc744":76,"\ud615\uc2dd\uc774":76,"\ud638\ucd9c\ub418\ub294\ub370":76,"\ud638\ucd9c\ub418\uba74":76,"\ud638\ucd9c\ub418\uc5b4\uc57c":76,"\ud638\ucd9c\ub41c\ub2e4":76,"\ud638\ud658\uc131":75,"\ud654\ub824\ud55c":76,"\ud654\uba74":[76,79],"\ud654\uba74\uacfc":76,"\ud654\uba74\ubcf4\ub2e4\ub294":77,"\ud654\uba74\ubcf4\ud638\uae30\ucc98\ub7fc":77,"\ud654\uba74\uc744":[75,77],"\ud654\uba74\uc758":76,"\ud655\uc2e4\ud558\ub2e4":77,"\ud655\uc778\ud558\ub294":[75,79],"\ud655\uc778\ud558\ub77c":80,"\ud655\uc778\ud558\uba74":[79,80],"\ud655\uc778\ud560":[77,78,79],"\ud655\uc778\ud574\uc57c":78,"\ud655\uc815\ub41c\ub2e4\uba74":76,"\ud658\uacbd":75,"\ud658\uacbd\uacfc":75,"\ud658\uacbd\uc5d0\uc11c":75,"\ud658\uacbd\uc5d0\uc11c\uc758":75,"\ud658\uacbd\uc6a9":75,"\ud658\uacbd\uc740":75,"\ud65c\ub3d9\uc774\ub2e4":82,"\ud65c\uc131\ud654\ub418\uba74":80,"\ud65c\uc6a9\ud558\uace0":82,"\ud65c\uc6a9\ud55c":75,"\ud65c\uc6a9\ud574":75,"\ud69f\uc218\ub97c":77,"\ud6a8\uacfc\uac00":[80,82],"\ud6a8\uacfc\ub97c":81,"\ud6a8\uacfc\uc74c\uc744":81,"\ud6c4":[75,78],"\ud6e8\uc52c":79,"\ud765\ubbf8\ub85c\uc6b4":82,"always\ubb38":[76,77],"always\ubb38\uacfc":79,"always\ubb38\uc5d0":[76,77],"always\ubb38\uc5d0\uc11c":76,"always\ubb38\uc758":77,"always\ubb38\uc774":77,"b\u00e9zier":30,"b\uac12":76,"blit\uc774":76,"blit\ud568\uc218\ub294":76,"boolean":[25,28,32,33,39,56,62,64],"break":[18,30,33,46,54,62,88],"byte":[9,14,17,18,20,22,23,28,29,31,37,38,42,43,44,46,51],"c\ub85c":75,"case":[15,18,20,23,24,28,29,31,33,36,37,38,40,43,44,51,57,58,62,64,68,70,72,73,74,84,85,87,88],"catch":[44,64,89],"center\ub77c\ub294":76,"char":[1,6,9,28,29,51],"class":[0,15,17,19,20,26,29,31,32,35,37,38,45,51,57,61,62,66,84,86,89],"collidepoint\ub97c":80,"const":[1,6,9,70],"cui\uac00":75,"cui\uace0":75,"cui\ud658\uacbd\uc5d0\uc11c\ub9cc":76,"default":[1,14,18,20,22,23,24,25,26,28,29,31,33,35,36,37,38,40,43,44,45,48,50,51,53,54,56,58,59,62,63],"do":[8,15,18,19,20,22,23,24,25,26,27,29,32,33,35,41,42,44,45,50,51,56,57,58,59,60,61,63,64,65,67,68,69,74,85,86,87,88,89],"down\ub41c":80,"drawbuttons\uc5d0":80,"drawhp\ub77c\ub294":79,"else\ubb38\uc740":77,"event\ubb38":[76,78],"event\ubb38\uc5d0":80,"event\ubb38\uc5d0\uc11c":79,"event\ubb38\uc744":79,"event\ubb38\uc774":78,"export":[0,10,20,31,38,42,43,51],"fill\ud568\uc218\ub098":76,"final":[22,26,28,29,42,44,51,58,61,62,71,84,85,86,89],"float":[1,17,19,20,24,25,29,30,32,35,36,38,40,45,48,50,54,56,65],"fps\uac00":77,"fps\uac12\uc774":77,"fps\ub294":77,"fps\ub300\ub85c":77,"function":[0,1,7,8,9,12,15,18,19,20,22,23,24,25,26,27,28,29,30,31,32,33,35,36,37,38,39,40,41,43,44,45,46,47,48,49,50,51,52,53,54,56,57,58,60,61,63,64,66,67,68,69,70,72,73,84,85,87,88,89],"g\uac12":76,"gui\uac00":80,"gui\ub97c":76,"gui\uc5d0\uc11c\uc758":80,"gui\uc774\ubbc0\ub85c":76,"gui\uc774\uc9c0\ub9cc":81,"gui\uc784\uc744":76,"gui\ud658\uacbd\uc5d0\uc11c":76,"header\uc5d0\uc120":76,"header\uc758":78,"hp\ub294":[79,81],"hp\ub97c":[79,80],"hp\ubc14":83,"hp\uc744":79,"hp\uc758":79,"import":[1,10,15,18,22,24,25,26,27,28,29,30,32,34,36,38,39,44,47,49,52,53,59,61,62,63,64,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84,85,86,89],"import\ud558\ub294":76,"in\ubb38\uc744":76,"initial\ubb38":76,"initial\ubb38\uc5d0":76,"initial\ubb38\uc758":77,"input\ud568\uc218\ub97c":76,"input\ud568\uc218\uc640\ub294":76,"int":[1,2,3,4,5,6,7,8,9,11,12,17,18,20,23,24,25,28,29,30,32,33,35,36,37,41,42,44,45,47,48,50,51,55,56,71,72,73,79,80,81],"it\uc744":81,"k_\uc2dc\ub9ac\uc988\uc774\ub2e4":78,"key\ub294":78,"keydown\uc740":78,"keydown\uc774":80,"keyup\uc774\ub77c\ub294":78,"l_f4\ub4f1\uc774":78,"locals\ub85c\ubd80\ud130":78,"long":[6,25,27,29,35,38,40,44,50,53,63,64,84,88],"main\ud568\uc218\ub97c":79,"main\ud568\uc218\uc5d0":79,"mousebuttonup\uc774":80,"mytext\uac1d\uccb4\uc758":76,"mytext\ub77c\ub294":76,"mytextarea\ub294":76,"mytextarea\ub77c\ub294":76,"mytextfont\uac1d\uccb4\uc758":76,"mytextfont\ub77c\ub294":76,"new":[2,3,4,5,6,7,8,11,12,13,17,18,20,21,22,23,24,25,26,28,29,30,31,32,33,35,36,37,38,39,40,42,43,44,45,46,47,48,49,50,51,52,54,55,56,58,59,61,62,63,64,65,69,71,72,73,74,84,85,86,87,89],"null":[1,2,3,4,5,6,7,8,9,11,14,28],"play\ub77c\ub294":78,"play\ud55c\ub2e4":78,"pos\ub294":80,"print\ud568\uc218\ub098":76,"public":[26,86,89],"quit\uac19\uc740":76,"quit\ub77c\ub294":76,"r\uac12":76,"return":[1,2,3,4,5,6,7,8,9,11,12,13,14,17,18,19,20,22,23,24,25,27,28,29,30,31,32,33,35,36,37,38,39,40,41,42,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,62,63,64,65,66,68,71,72,73,79,80,81,84,85,86,87,88,89],"short":[25,63,65,84],"statement\uc5d0":77,"statement\uc5d0\uc11c":77,"static":[47,48,50,69],"super":[25,26,35,48,64],"switch":[22,23,29,44,50,59,89],"sys\ub294":76,"throw":[40,62,64],"tick\ud568\uc218\ub294":77,"true":[2,3,4,5,6,7,9,11,12,13,15,17,18,19,20,22,23,24,25,26,27,28,29,31,32,33,35,36,37,38,39,40,41,44,45,46,47,50,51,53,56,57,58,60,62,63,64,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84,85,87,89],"try":[25,27,29,44,51,58,61,62,63,64,65,67,84,85,86,89],"ttf\ud30c\uc77c\ub85c":76,"ttf\ud655\uc7a5\uc790\ub97c":76,"update\ud568\uc218\uac00":76,"void":[1,8,12],"while":[0,15,17,19,22,24,25,28,30,31,32,36,38,39,40,42,43,44,49,50,51,54,57,62,63,64,65,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84,85,87,89],"world\uac00":[77,78],"world\ub294":77,"world\uc758":77,"x\uac12\uacfc":80,"x\uc88c\ud45c\uac00":76,"y\uac12\uc744":80,"y\uc131\ubd84\uc744":76,"y\uc88c\ud45c\uac00":76,A:[1,4,7,8,11,12,14,15,17,18,20,22,23,24,25,26,29,30,32,33,35,38,39,40,42,43,45,46,49,50,51,53,54,56,58,63,64,85,86,89],AND:[18,33],ANDing:84,AS:18,And:[22,32,58,62,66,68,69,71,74,84,85,89],As:[24,29,35,38,39,44,51,52,56,57,58,64,65,67,68,84,85,86,88,89],At:[26,31,63,64,65],BE:18,BUT:18,BY:18,Be:[22,23,24,28,44,65],Being:[63,84],But:[26,39,43,51,58,62,64,65,68,69,71,72,74,85,87,88],By:[23,25,29,35,39,43,44,51,53,54,56,57,61,62,64,85,86],FOR:18,For:[1,15,17,18,20,22,23,24,25,26,27,28,29,30,31,32,33,35,36,37,38,40,42,43,44,45,46,47,48,49,50,51,53,56,57,60,61,62,63,64,65,67,68,71,84,85,86,88],IF:18,IN:[18,27],IS:[18,27],If:[5,9,11,12,17,18,19,20,22,23,24,25,26,27,28,29,30,31,32,33,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,56,58,59,60,61,62,63,64,65,67,68,69,71,72,73,74,84,85,87,88,89],In:[17,18,22,23,27,29,30,31,32,33,36,37,39,40,43,44,51,54,57,58,59,61,62,63,64,65,68,70,72,84,85,86,87,88,89],Is:[37,50,63],It:[2,9,12,15,17,18,19,20,23,25,26,28,29,31,32,33,36,37,38,40,42,43,44,45,47,50,51,53,54,56,57,58,59,60,61,63,64,65,66,68,69,72,73,84,86,87,88,89],Its:34,NO:18,NOT:[18,37,40,56],No:[1,5,6,8,11,33,44,45,50,51,67,70],Not:[23,25,41,45,51,60,63,64,65,69,70],OF:18,ON:18,OR:[18,29,35],ORed:38,ORing:33,Of:[57,69,70,71],On:[1,2,3,4,5,6,7,8,9,18,23,25,26,37,40,44,46,50,51,56,63,66,84],One:[10,19,32,63,65,84],Or:[20,26,44,64,74],SUCH:18,THE:18,TO:18,That:[15,29,56,58,62,63,64,67,68,69,70,71,73,74,84],The:[1,2,3,4,5,6,7,8,9,11,12,14,15,16,17,18,19,20,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,59,60,61,63,65,66,68,71,84,87,88],Then:[10,22,24,58,62,64,65,68,71,73,85,86,88,89],There:[16,18,19,20,22,23,24,25,26,27,28,30,32,33,37,39,45,46,50,51,57,58,59,60,62,64,65,69,70,72,73,85],These:[16,20,22,23,24,26,29,32,37,39,44,45,47,48,50,51,52,53,56,58,59,62,64,65,84,85,87],To:[22,23,25,30,32,33,35,37,38,39,40,41,42,43,47,50,54,57,58,65,87,88],Will:[4,5,6,11,32,38,54],With:[26,31,36,37,51,62,63,65,85,87,88],_:[20,33],__class__:45,__copy__:35,__dict__:25,__file__:[26,58,66],__init__:[32,45,50,57,58,62,64,66,86,87,88,89],__main__:[32,66,71,72,73,79,80,81,85,89],__name__:[32,45,66,71,72,73,79,80,81,85,89],__new__:[29,45],__repr__:45,__tags__:53,_camera:18,_default_lay:50,_freetyp:[0,10],_index:1,_layer:50,_pixels_address:51,_pygam:10,_rect:45,_sdl2:48,_spin:[58,66],_sprite__g:64,_spritegroup:64,_tag:53,_test:53,_time_threshold:50,_use_upd:50,_walk:[58,66],a0:37,a6f89747b551:44,a_mask:35,aa:[20,30,65],aaa:65,aacircl:30,aaellips:30,aalib:23,aalin:24,aapolygon:[24,30],aatrigon:30,abandon:18,abil:[44,58,63],abl:[31,32,42,58,63,84,89],abnorm:89,abort:37,about:[9,15,18,19,23,25,26,28,32,36,37,39,42,44,48,50,51,55,58,59,61,62,63,64,65,67,68,70,71,72,73,74,84,85,87,88],abov:[14,18,19,22,23,24,28,29,32,40,41,42,43,45,48,57,58,62,64,65,74,84,87],absent:23,absolut:[19,20,29,32,37,40,51,84],abspath:[58,66],abstractgroup:50,acceler:[23,24,30,41,48,51,56,63,69],accept:[14,24,26,28,29,30,31,38,39,45,50,51,52,62],access:[8,9,15,17,19,24,25,26,28,29,32,35,37,38,39,41,43,44,46,50,51,59,64,65,67,84],accident:84,accommod:62,accord:[20,43,56,69],accordingli:51,account:[32,33,36,38,40,42,56,68],accur:[39,40,54],achiev:[62,86],acolor:20,acquir:[9,51,52],across:[22,23,33,58,62,65,66,84,87,88,89],act:[36,58,63,70],action:[25,63,65,88],activ:[23,25,38,39,40,49,51,52,55,72,74],activeev:[23,25],actual:[4,11,18,19,22,23,25,28,30,32,37,38,40,41,43,45,50,51,54,56,58,59,62,63,64,65,73,84,85,88],ad:[10,23,24,25,29,31,32,33,35,38,39,40,42,43,44,45,47,50,54,56,58,64,65,68,69,70,72,73,84,87,89],add:[23,29,48,50,57,58,61,62,63,64,65,68,69,70,89],add_intern:64,add_map:47,addit:[22,23,24,25,26,32,33,36,37,44,46,48,50,51,84],addition:[23,24,47,62],address:[17,51,84,86],adequ:72,adjac:[24,30],adjust:[8,20,23,28,29,37,71,89],admit:84,adopt:[48,61],advanc:[15,28,29,47,50,51,58,62,63,67,70],advancedinputoutput1:[72,80],advancedinputoutput2:[72,80],advancedinputoutput3:[72,80],advancedinputoutput4:[72,80],advancedinputoutput5:[72,80],advancedoutputalpha1:[73,81],advancedoutputalpha2:[73,81],advancedoutputalpha3:[73,81],advancedoutputprocess1:[71,79],advancedoutputprocess2:[71,79],advancedoutputprocess3:[71,79],advancedoutputprocess4:[71,79],advancedoutputprocess5:[71,79],advancedoutputprocess6:[71,79],advancemam:56,advantag:[59,63,64,67,84,87],advic:63,advis:[18,22,23],ae:28,affect:[11,23,24,29,38,42,44,45,50,51,52,65,74],afraid:65,after:[17,19,23,24,28,29,31,32,33,37,38,40,47,49,50,51,57,58,59,62,63,65,68,69,70,84,89],again:[18,33,40,44,50,51,54,59,61,62,64,65,84,85,88,89],against:[29,31,44,45,56,57,64],ago:63,agp:65,ahead:[24,32],ai:[86,89],aid:29,aim:61,alexei:74,algorithm:[24,35,44,56,67,70,84],alia:[25,31,46,50],alias:[15,23,28,29,85],aliceblu:21,alien1:62,alien:[26,64],align:[29,35,36,45],aliv:[17,50,64],all:[15,17,18,19,20,22,23,24,25,26,27,28,29,30,31,32,33,35,36,37,38,39,40,41,42,44,45,47,48,50,51,52,53,56,57,59,60,61,63,64,65,67,68,84,85,86,87,88],all_my_sprites_list:84,allblack:65,alloc:[9,31,38,68],allot:53,allow:[15,18,20,22,23,25,27,28,29,31,32,36,38,42,44,45,46,50,51,56,57,58,59,62,64,65,87,89],allowedchang:38,allsprit:[58,66],almost:[15,58,61,63,64,65,67,86],alon:[26,84],along:[24,25,26,29,37,45,53,62,63,64,65,66,74,87],alpha:[1,20,23,24,26,28,29,30,31,35,43,44,48,51,52,56,63,65,86],alreadi:[9,19,23,25,29,32,37,38,40,45,47,50,57,62,63,64,65,84,88],alsa:37,also:[10,12,14,15,17,19,20,22,23,25,26,28,29,30,31,33,35,36,37,38,39,40,42,44,45,47,50,51,52,53,54,56,57,58,59,60,61,62,63,64,65,68,69,72,73,84,85,86,87,88,89],alt:[25,33],alter:[35,39,56],altern:[19,23,24,26,28,29,30,45],altgr:33,although:[58,68,86],alwai:[18,23,25,26,28,29,30,31,32,33,35,39,41,44,45,47,49,50,51,54,56,58,59,60,62,63,64,65,68,69,70,71,77,84,86,87,89],ambigu:38,among:18,amongst:15,amount:[23,25,28,29,32,39,54,55,56,58,63],ampersand:33,amplitud:49,an:[1,2,3,4,5,6,7,9,10,11,12,13,14,15,18,19,20,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,63,64,65,67,69,84,85,86,87,88,89],an_id:37,analog:[32,42,47],andal:29,andmask:[22,39],android:[25,33],angl:[24,29,30,35,36,48,56,57,87,89],angle_to:36,angular:87,ani:[1,9,15,17,18,19,21,22,23,24,25,26,27,28,29,30,31,32,35,37,38,40,41,42,43,44,45,47,48,50,51,52,53,54,56,57,58,59,60,61,62,63,64,65,67,68,74,84,85,86,87,88],anim:[15,26,31,50,56,62,63,64,67],anisotrop:44,annoi:[15,61,73],anoth:[10,20,23,24,26,28,29,30,32,33,35,36,39,40,42,43,45,46,47,48,50,51,62,63,64,65,73,74,84,86,89],ansi:37,ansi_not:37,answer:[63,84],anti:[15,23,29,85],antialia:28,antialias:[24,28,29,30,56,58],anticip:[44,69],antiquewhit:21,antiquewhite1:21,antiquewhite2:21,antiquewhite3:21,antiquewhite4:21,anymor:84,anyon:64,anyth:[15,19,23,27,44,50,51,58,62,63,64,65,74,84],anywai:[65,70,85],anywher:[12,50,62],apart:38,api:[15,18,23,25,30,33,37,46,47,48,57,84],app:[23,26,44,57],app_didenterbackground:25,app_didenterforeground:25,app_lowmemori:25,app_termin:25,app_willenterbackground:25,app_willenterforeground:25,appear:[15,23,25,54,62,63,64,70,84],append:[27,57,62,64,69,73,81,84],appli:[20,24,26,29,35,36,37,51,55,56,65,84],applic:[0,17,23,25,26,27,33,34,37,38,46,51,84,88],approach:61,appropri:[23,25,39,58,59,64,69,88],approxim:[32,35,51,57],aptitud:74,aqua:21,aquamarin:21,aquamarine1:21,aquamarine2:21,aquamarine3:21,aquamarine4:21,ar:[1,8,9,10,12,14,15,16,17,18,19,20,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,56,57,58,59,60,61,63,64,65,66,67,68,70,71,72,73,74,85,86,87,88,89],arang:65,arbitrari:[50,56,62],arbitrarili:23,arc:[24,30],arcad:[63,84],architectur:56,archiv:[28,29,84],area:[11,23,24,26,28,29,32,35,44,45,48,50,51,56,57,58,62,63,64,66,68,72,84,85,87,88,89],aren:[58,62,84],arg:[26,50,53],argb:31,argb_premult:31,argument:[1,2,3,9,11,15,17,18,19,20,22,23,24,25,26,28,29,30,31,32,33,35,36,38,39,40,41,43,44,45,47,48,50,51,53,54,56,58,59,62,64,84,85],aris:18,arithmet:[20,36,42,65],arkanoid:69,arm:84,around:[15,23,24,26,32,36,39,42,45,48,54,57,58,59,62,63,65,84,85,86,89],arrai:[1,2,3,15,20,22,26,29,31,33,38,42,49,51,62,63,65,72,73,80],arrang:[65,68],array2d:[52,65],array3d:[52,65],array_alpha:[52,65],array_blu:52,array_colorkei:[43,52,65],array_green:52,array_r:52,array_to_surfac:43,arraydemo:[15,26,65],arraytyp:[26,49,52],arrayxd:65,arriv:84,arrow:[22,26,33,62,70],art:[67,74],articl:[62,63],artifact:56,as_joystick:47,as_polar:36,as_spher:36,ascend:29,ascent:[28,29],ascii:[14,22,33,52,67],asid:67,ask:[23,28,29,57,58,59,62,63,84],aspect:[44,45],assembl:[33,63],assert:62,assertequ:56,assertnotequ:56,asset:84,assign:[20,25,29,32,42,45,47,50,53,58,62,65,88],assist:[51,84],associ:[7,29,48,55],assum:[1,9,11,20,28,29,37,39,45,51,57,61,64,86,88],asterisk:33,asteroid:84,astyp:65,asurf:31,asymmetr:36,asyncblit:51,asynchron:51,attach:[9,23,26,47,57],attack:[36,63],attempt:[18,28,29,37,51,58,60,65],attent:[63,70],attract:70,attribut:[5,23,25,27,28,29,30,32,33,34,35,36,39,44,45,50,51,53,54,58,64,85,87,88,89],attributeerror:[29,64],audio:[25,26,37,38,44,46,49,63],audio_allow_any_chang:38,audio_allow_channels_chang:38,audio_allow_format_chang:38,audio_allow_frequency_chang:38,audiodevic:25,audiodevicead:25,audiodeviceremov:25,august:16,author:[18,28,33,36,45,51,56,57,58,59,60,62,63,64,65,84],automat:[12,19,23,28,29,30,31,32,33,34,37,38,44,46,50,51,58,60,65,68,69],avail:[0,9,16,18,20,22,23,26,27,28,29,30,31,36,37,38,41,44,45,46,49,51,52,53,55,56,58,59,60,62,63,64,65,84],avalanch:74,averag:[28,29,53,54,56,57,65,84],average_color:[56,57],average_surfac:[56,57],avid:84,avoid:[19,23,28,33,36,38,42,53,65,84,89],awai:[15,23,25,36,64,71,89],awar:[23,25,28,44,65,84],awhil:19,awkward:[62,84],ax:[23,29,32,47,87],axi:[24,25,29,36,39,42,47,48,49,52,56,85,87],axis_numb:32,azimuth:36,azur:21,azure1:21,azure2:21,azure3:21,azure4:21,b0:47,b3:47,b:[20,24,26,32,33,36,42,43,45,46,47,51,56,65],b_black:[73,81],b_height:[73,81],b_red:[73,81],b_width:[73,81],bach:40,back:[19,28,29,32,33,37,38,50,57,58,59,66,68,88,89],backend:[18,23,25,43,44,56,59],background:[24,25,26,28,29,38,44,50,57,61,64,66,84,85,87,89],backslash:33,backslashreplac:44,backspac:33,backward:[23,25,29,38,54],backyard:63,bad:84,bagic:[68,69,70,76,77,78],baker:63,ball:[25,32,61,63,67,68,69,70,71,72,73,75,76,77,78,79,80,81,85,86,88],ball_numb:32,ballrect:[63,67,68,69,70,71,72,73,75,76,77,78,79,80,81],ballsprit:89,banner:[15,26,58,66],bar:[71,79],barrier:67,base:[0,10,17,18,24,25,26,28,29,35,36,48,50,57,58,62,64,65,66,67,68,84,87,89],baselin:[28,29],basic:[15,18,20,23,26,31,46,58,61,62,63,64,65,76,84,86,87,88,89],bat:61,battleship:[67,75],bayer:18,bb:20,bdf:29,beam:22,bear:[28,29,39],beauti:62,becam:25,becaus:[24,27,29,33,36,37,45,51,58,61,64,65,67,68,69,70,72,73,84,85,86,87,88,89],becom:[23,25,29,38,40,49,51,52,62,63,64,65,68,73,84],been:[19,20,22,23,24,25,29,32,36,38,39,40,44,46,47,50,51,59,60,62,63,64,84,88,89],befor:[9,17,18,19,22,23,27,28,29,32,33,35,37,38,39,40,44,46,47,50,51,53,54,57,58,59,60,61,62,63,64,65,68,69,70,71,84,87,88],beforehand:25,begin:[15,17,18,26,38,40,45,56,62,63,65,69,87],beginn:65,behalf:17,behavior:[29,31,36,39,50],behaviour:[23,39,44,46,89],behind:[15,23,31,50,87,88,89],beig:21,being:[8,18,23,24,25,31,32,33,35,36,37,38,39,40,42,46,62,63,64,69,84,88],believ:84,belong:[50,58,64,85],below:[15,18,20,23,24,26,27,32,37,44,45,47,50,57,62,71],bend:37,benefit:[50,62,64],besid:[19,26,65],best:[12,18,23,25,26,35,38,42,43,51,52,59,62,63,64,65,72,86],bet:59,better:[23,28,30,37,39,40,51,56,62,63,64,65,71,84,85],between:[1,3,20,23,24,25,28,29,32,33,35,36,37,38,40,42,43,44,45,46,47,48,49,50,51,52,53,54,55,57,64,65,67,70,84],beyond:84,bezier:30,bg:57,bgcolor:29,bgd:50,bgr:[31,51,65],bgra:31,bgsurf:50,bid:58,big:[17,18,44,50,57,62,64,65,70,71,72],bigger:[15,23,58,73],biggest:[23,59],bilinear:56,bin:[66,85,86,87],binari:[15,18,20,22,41],bind:[37,59],bisqu:21,bisque1:21,bisque2:21,bisque3:21,bisque4:21,bit:[1,14,15,17,18,22,23,25,26,28,29,30,31,32,35,38,40,42,43,49,50,51,52,56,57,58,59,61,62,64,65,84,87,89],bitblt:62,bitmap:[22,29,31,56,62],bitmap_1:22,bitmap_2:22,bitmask:[22,33,35,50,51],bitsiz:[23,35,59],bitstream:[28,29],bitstreamverasan:28,bitwis:[23,33,35],bl:89,bla:31,black:[21,22,23,24,26,29,35,42,51,56,57,58,63,65,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84],blade:63,blanchedalmond:21,blank:[28,57,62,84,85],blanket:[43,51],blend:[20,24,26,29,44,48,51],blend_add:51,blend_alpha_sdl2:51,blend_fil:26,blend_max:51,blend_min:51,blend_mod:48,blend_mult:51,blend_premultipl:51,blend_premultipli:[20,51],blend_rgb_add:51,blend_rgb_max:51,blend_rgb_min:51,blend_rgb_mult:51,blend_rgb_sub:51,blend_rgba_add:51,blend_rgba_max:51,blend_rgba_min:51,blend_rgba_mult:51,blend_rgba_sub:51,blend_sub:51,blend_xxx:26,blendmod:50,blink:67,blit:[11,20,23,26,28,29,30,32,43,48,50,51,52,56,57,58,61,63,64,65,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84,87,88,89],blit_arrai:[43,52,65],blit_blend:26,blit_hw:[23,59],blit_hw_a:[23,59],blit_hw_cc:[23,59],blit_sequ:51,blit_sw:[23,59],blit_sw_a:[23,59],blit_sw_cc:[23,59],blitter:[44,51,62],blitzbas:26,blob:[56,57],block:[17,18,25,35,40,50,51,57,58,63,73,84],block_list:50,blocks_hit_list:50,bloodi:63,blt:62,blue1:21,blue2:21,blue3:21,blue4:21,blue:[1,18,20,21,23,24,28,31,42,51,52,56,57,65,68,71,72,73,79,80,81,85,87],blueviolet:21,bluish:65,bmp:[31,46,62,63],board:[73,81],bodi:70,bold:[28,29],bomb:64,bonu:62,book:84,bool:[17,18,19,23,24,25,27,28,29,31,32,33,35,36,37,38,39,40,44,45,46,47,48,50,51,56],boom:64,boom_sound:64,border:[23,24,30,39,45,48],border_bottom_left_radiu:24,border_bottom_right_radiu:24,border_radiu:24,border_top_left_radiu:24,border_top_right_radiu:24,borderless:48,bore:[67,86],borrow:[1,63,86],both:[19,23,24,28,29,30,31,32,33,35,36,37,38,39,42,50,51,56,57,62,63,64,65,67,71,74,84,87,89],bother:[58,61,86],bottleneck:84,bottom:[23,24,26,28,29,30,31,35,45,50,63,67,68,69,70,71,72,73,75,76,77,78,79,80,81,89],bottomleft:[45,89],bottomright:[45,89],bounc:[26,36,56,63,89],bound:[22,24,29,35,50,51],boundari:[24,29],box:[24,29,30,33,57,64,88],br:89,bracket:[33,87],breakag:42,breakdown:63,brief:[19,26,44,61,62],briefli:85,bright:[18,57],brighten:23,brightmap:65,bring:[50,63,84],broadcast:[42,65],broader:84,broken:[39,44],broken_x:22,brought:56,brown1:21,brown2:21,brown3:21,brown4:21,brown:21,bu:65,buffer:[1,2,18,20,23,31,37,38,43,46,51,58,63,64],buffer_s:37,bufferproxi:[0,15,17,31,51],buffers:[37,38],bug:[20,44,56,63,84,89],build:[44,64,65,85,89],built:[14,18,28,29,31,44,47,84,85],builtin:[18,28,64],bullet:[64,84],bump:84,bumper:32,bunch:[57,64,84],bundl:[28,29],burlywood1:21,burlywood2:21,burlywood3:21,burlywood4:21,burlywood:21,busi:[18,19,38,54,62],button1:39,button2:39,button3:39,button4:39,button5:39,button:[24,25,26,27,32,33,39,47,58,61,62,69,73,80,84,85,88],bx:47,bye:68,bypass:44,byte_data:46,bytearrai:31,bytecod:63,byteord:17,bytes:[17,23,35,51,59],bytestr:38,c:[10,15,17,18,20,26,28,30,33,43,44,45,51,60,63,65,67,75],c_api:0,cach:29,cache_s:29,cadetblu:21,cadetblue1:21,cadetblue2:21,cadetblue3:21,cadetblue4:21,cadillac:64,calcnewpo:[87,89],calcul:[24,29,35,36,40,42,50,68,69,72,73,87,88],calibr:57,call:[1,9,12,17,18,19,22,23,24,25,26,27,28,29,30,31,32,33,35,36,37,38,39,40,41,44,45,46,47,48,50,51,53,54,57,58,59,60,61,62,63,64,65,66,68,69,70,84,85,86,87,88,89],callabl:[17,44,50],callback:[1,2,17,50],caller:[9,37],calling_mask:35,cam:57,came:[26,31],camera:[15,26,44,84],camlist:57,can:[1,8,9,13,14,15,17,18,19,20,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,49,50,51,52,53,54,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,84,85,86,87,88,89],candid:[12,33],cannot:[14,18,23,25,28,30,38,40,45,48,50,51,52,54,62,63,64,69,84,86,89],canva:[68,71],cap:33,capabl:[23,59,63,86,87],capslock:33,caption:[23,68],captur:[15,18,25,26,31,32],capword:25,card:[23,38,65],care:[9,27,38,58,65,72,85],caret:33,carri:89,castl:63,categor:64,categori:84,caught:63,caus:[2,18,20,25,28,35,40,42,44,56,58,64,65,66,68,84,88],caveat:23,cc:57,ccolor:57,cd:19,cdrom:63,cdrom_tag:53,cdrom_test:53,ceil:63,center:[24,26,30,32,35,44,45,48,50,51,57,66,68,69,70,71,72,73,76,77,78,79,80,81,85],centeri:45,centerx:[45,58,66,85],centr:44,centroid:[35,57],certain:[20,25,33,36,57,58,61,67,68,69,70,71,72,84],certainli:[62,65],cff:29,challeng:[63,84],chanc:[38,65,84],chang:[15,18,20,23,24,25,26,28,29,30,31,32,33,35,36,37,38,39,40,42,44,45,46,47,48,49,50,51,52,54,56,57,58,59,61,63,64,65,68,69,70,71,73,84,87,88,89],change_color:56,change_lay:50,channel:[7,20,31,37,38,40,51,65,84],channelnum:7,char_bit:35,charact:[14,17,23,28,29,33,43,44,52,62,84,88],character:36,characterist:[20,74],charset:46,chart:65,chartreus:21,chartreuse1:21,chartreuse2:21,chartreuse3:21,chartreuse4:21,chase:64,chaser:64,chatroom:62,cheap:[50,64],check:[3,4,5,6,7,8,11,15,18,23,25,26,28,32,35,38,39,40,42,44,46,47,49,50,52,57,58,60,62,63,64,67,68,70,71,72,84,85,86,87,88,89],checkout:44,chew:54,chief:86,child:51,chimp:[15,26,61,64,86],chimpanze:15,chocol:21,chocolate1:21,chocolate2:21,chocolate3:21,chocolate4:21,choic:[18,23,38,57],choos:[18,22,23,26,59,61,65,73,84],chop:56,choppi:62,chord:84,chore:84,chose:73,chosen:[18,23,29],chromin:18,chunk:[7,17,89],circl:[15,22,24,26,30,32,50,57],circular:64,circumst:89,claim:18,clamp:[26,36,45],clamp_ip:45,clamp_magnitud:36,clamp_magnitude_ip:36,clariti:35,clark:84,classless:[86,87],classmethod:48,claus:89,clean:[39,42,58,60,62,63,64,84],cleaner:[62,63],cleanli:[60,62,63,64,89],cleanup:42,clear:[15,23,24,25,32,33,35,38,43,48,50,51,62,63,64,67,75,84],clear_callback:50,clearer:60,clench:[58,66],click:[15,22,24,26,32,33,39,69,72,84,85],client:25,clip:[24,26,45,50,51,62],clipboard:[15,25,26],clipboardupd:25,cliplin:45,clipped_lin:45,clist:57,clock:[15,22,24,32,39,54,58,62,66,69,70,71,72,73,77,78,79,80,81,84,89],clockwis:[29,30,36,56,87],clone:20,close:[9,15,18,22,23,24,25,26,29,32,37,40,42,47,56,57,62,65,85,86],close_to_play:64,close_to_player2:64,close_to_player3:64,closest:[23,37,38,59],cmy:20,co:[87,89],cocoa:23,code:[5,14,15,17,18,22,23,24,25,26,27,28,29,30,32,33,35,39,42,43,44,45,46,50,51,56,57,58,62,63,64,65,67,68,69,70,71,72,73,84,85,86,87,88,89],codec:44,codepoint:28,coder:84,coercion:65,col:30,collect:[12,23,33,50,51,60,84],collid:[35,45,50,58,64,66,87,89],collide_circl:50,collide_circle_ratio:50,collide_mask:[35,50],collide_rect:50,collide_rect_ratio:50,collided_cal:50,collidedict:45,collidedictal:45,collidelist:45,collidelistal:45,collideobject:45,collideobjectsal:45,collidepoint:[45,72,73,80,81,84,89],colliderect:[45,50,58,66,89],colliding_sprit:50,collis:[26,35,36,45,50,57,67,89],collision_box:45,colon:33,color:[0,1,10,15,22,23,24,28,29,30,31,35,42,43,48,50,51,52,56,57,58,59,62,63,65,67,68,71,72,73,80,81,84,85],color_valu:20,colordict:21,colorkei:[23,26,28,29,31,43,51,52,56,58,63,65,66],colormap:[26,43,52],colorspac:18,colour:[20,44,51],column:[35,42,65,73,81],com:56,combin:[23,29,33,45,50,51,56,63,64,84,89],come:[25,26,28,29,37,44,57,62,63,64,65,66,84,87,88,89],comfort:[15,87],comma:[28,29,33,37,65],command:[24,25,26,32,53,65,67,68,69,70],comment:[26,61,64,66,87,89],commerci:[15,63],commit:[33,63],common:[23,28,29,47,50,56,60,61,62,63,84],commun:[25,48,84],compani:63,compar:[33,35,36,42,44,50,56,63,68,84],comparison:[20,25,26,42,65,70,74],compat:[23,25,29,31,38,48,50,54,67,86],compens:63,compil:[13,15,19,22,25,28,29,31,38,41,44],complement:36,complet:[24,33,38,40,45,50,51,52,63,64,84,89],complex:[38,58,63,64,67,69,84,86,87,88],complic:[68,84],compon:[20,35,36,43,51,65,68],composit:33,compositor:44,compound:46,compound_text:46,compress:[40,51],comput:[15,18,19,26,28,31,32,38,54,58,62,63,64,69,74,84],concept:[15,61,62,63,65,69,74,84],concern:[64,74],conclud:63,conclus:74,concret:84,concurr:84,condit:[18,22,38,68,89],confid:84,configur:[15,23,29,39,65],confin:[48,62],confus:[24,62,68,84],connect:[24,26,30,32,35,47,61,62,74,86],connected_compon:[35,57],connenct:32,consequ:86,consequenti:18,consid:[23,24,25,29,35,36,38,40,44,45,50,51,56,84,89],consider:51,consider_alpha:56,consist:[19,24,25,27,38,46,61,65,85,86],consol:[26,44,53,67],constant:[15,22,25,29,33,38,39,44,46,47,54,57,60,62,68,72],constrain:[24,39,70],construct:[36,45,62],constructor:[29,32,50,58,64],consum:[1,54,65,84],contact:[57,58,59,60,62,63,64,65],contain:[0,6,8,15,19,22,23,24,25,28,29,31,32,33,34,35,37,38,39,43,45,46,47,50,51,53,55,58,59,62,63,64,66,84,86,88,89],content:[23,34,41,42,46,58,59,68,70],context:[9,23,42,48,84],contigu:[17,24,51],continu:[35,38,44,50,51,63,85],contract:[18,56],contrast:[39,70,87],contribut:64,contributor:18,control:[4,15,18,22,25,26,28,29,33,34,38,41,44,50,51,52,53,54,56,58,59,60,61,62,63,69,70,78,84,86],controller_axis_lefti:47,controller_axis_leftx:47,controller_axis_righti:47,controller_axis_rightx:47,controller_axis_triggerleft:47,controller_axis_triggerright:47,controller_button_a:47,controller_button_b:47,controller_button_back:47,controller_button_dpad_down:47,controller_button_dpad_left:47,controller_button_dpad_right:47,controller_button_dpad_up:47,controller_button_guid:47,controller_button_i:47,controller_button_leftshould:47,controller_button_leftstick:47,controller_button_rightshould:47,controller_button_rightstick:47,controller_button_start:47,controller_button_x:47,controlleraxismot:47,controllerbuttondown:47,controllerbuttonup:47,controllerdevicead:[25,47],controllerdeviceremap:[25,47],controllerdeviceremov:[25,47],controllertouchpaddown:47,controllertouchpadmot:47,controllertouchpadup:47,convei:22,conveni:[36,44,50,53,62,63,84],convent:47,convers:[1,18,20,35,42,52,84],convert:[1,18,20,22,24,29,31,37,38,40,43,45,49,51,52,58,59,62,65,66,85,86,89],convert_alpha:[26,31,51,86,89],convolut:[35,65],convolv:35,cool:84,cooper:19,coord:57,coordin:[8,22,24,29,30,32,35,36,39,50,51,56,85,89],copi:[17,22,26,31,35,36,38,42,45,46,48,49,50,51,52,56,58,62,63,64,65,84,85],copyright:18,coral1:21,coral2:21,coral3:21,coral4:21,coral:21,cord:26,core:84,coremidi:37,corner:[24,29,30,35,39,42,44,50,51,56,57,58,62,84,89],cornflowerblu:21,cornsilk1:21,cornsilk2:21,cornsilk3:21,cornsilk4:21,cornsilk:21,correct:[18,22,28,41,45,51,56,62,65,68,73],correct_gamma:20,correctli:[24,36,37,38,56,57,58,62,65],correpsond:33,correspond:[29,33,35,37,42,51,53,68,84,89],could:[22,25,33,36,41,45,48,49,50,52,56,57,58,61,62,64,65,85,86,87],couldn:[86,89],count:[17,19,32,35,38,42,56,57,69,73],counterclockwis:[24,29,36,56],coupl:[32,58,62,64,65],courier:29,cours:[57,69,70,71,85,86,87,89],cover:[15,24,33,44,45,51,56,57,59,62,74,84,85],coverag:24,cprofil:84,cpu:[24,54,67,75,84],crash:[41,64],creat:[1,2,5,6,15,16,17,19,20,21,22,23,25,26,28,29,31,32,33,35,36,38,39,41,42,43,45,47,48,49,50,51,52,54,56,57,59,63,64,65,66,74,84,85,86,87,88,89],create_graphics_screen:62,create_screen:62,creation:[28,29,32,47,49,50,52,62],creativ:74,crect:57,crimson:21,critic:84,critter:[58,66],crop:[45,56],cross:[32,36,58,63,65,86],crossbar:26,crossbon:22,crossfad:65,crosshair:22,crt:23,crucial:84,crude:[26,58,62],cryptic:64,ctrl:25,cube:26,cui:[67,68,73,81],current:[13,18,19,22,23,25,26,27,28,29,32,33,35,36,37,38,39,40,41,42,44,45,46,47,48,49,50,51,52,53,56,58,59,62,63,64,67,68,69,71,84,87,88,89],current_h:[23,59],current_level:36,current_w:[23,59],cursor:[15,26,39,58,63,84],cursor_arg:22,cursor_index:22,cursorfil:22,curv:30,custom:[15,23,25,26,38,50,63],custom_typ:25,customis:86,cut:[38,46,84],cutout:62,cx1:45,cx2:45,cy1:45,cy2:45,cyan1:21,cyan2:21,cyan3:21,cyan4:21,cyan:21,d:[32,33,51,62,84,85,87],da:61,dai:[63,64,84],damag:18,dark:[58,63],darkblu:21,darkcyan:21,darken:23,darkgoldenrod1:21,darkgoldenrod2:21,darkgoldenrod3:21,darkgoldenrod4:21,darkgoldenrod:21,darkgrai:21,darkgreen:21,darkgrei:21,darkkhaki:21,darkmagenta:21,darkolivegreen1:21,darkolivegreen2:21,darkolivegreen3:21,darkolivegreen4:21,darkolivegreen:21,darkorang:21,darkorange1:21,darkorange2:21,darkorange3:21,darkorange4:21,darkorchid1:21,darkorchid2:21,darkorchid3:21,darkorchid4:21,darkorchid:21,darkr:21,darksalmon:21,darkseagreen1:21,darkseagreen2:21,darkseagreen3:21,darkseagreen4:21,darkseagreen:21,darkslateblu:21,darkslategrai:21,darkslategray1:21,darkslategray2:21,darkslategray3:21,darkslategray4:21,darkslategrei:21,darkturquois:21,darkviolet:21,data1:37,data2:37,data3:37,data:[0,2,15,17,18,19,22,26,31,35,37,38,39,40,41,43,46,51,56,58,62,63,65,66,68,70,71,72,73,84,86,89],data_dir:[58,66],datatyp:65,date:84,david:84,dead:27,deal:[33,44,65,68,84,88],dealloc:32,dealt:[25,27,88],death:63,debat:84,debug:[25,56,84,88],decapit:63,decept:72,decid:[25,27,35,58,61,64,68,84],decim:24,decis:84,declar:[8,62,65],decod:[22,29,33,46,63],decor:85,decreas:[38,45,72],decrement:9,dedic:64,deeper:84,deeppink1:21,deeppink2:21,deeppink3:21,deeppink4:21,deeppink:21,deepskyblu:21,deepskyblue1:21,deepskyblue2:21,deepskyblue3:21,deepskyblue4:21,def:[29,32,35,39,45,50,56,57,58,62,64,66,71,72,73,79,80,81,85,86,87,88,89],default_id:37,default_lay:50,defin:[1,2,3,6,7,8,10,11,25,28,29,34,36,44,46,50,58,61,62,84,85,88],definit:[46,60,64,65,70],deflat:89,degre:[29,30,35,36,52,56,58,87,89],del:[25,32,65],delai:[26,33,37,54,69,84],delet:[25,33,62,64,84],deliv:37,delta:[15,36],demo:[26,58,65],demonstr:[15,26,36,58,65],denot:[40,45,54],depend:[14,23,25,28,29,31,35,38,40,42,43,46,49,58,63,66,69,71,84],deprec:[20,23,24,25,26,27,29,32,36,49,50,51,52],deprecationwarn:[49,52],depth:[15,18,23,35,42,48,51,52,56,57,58,59,62],deriv:[26,44,50,58,64,84],descend:29,descent:[28,29],describ:[1,17,22,23,37,39,45,57,59,86],descript:[1,18,22,25,29,33,34,44,46,62,64],design:[17,50,61,62,63,64,68,71,88],desir:[18,51,56,58,59,84],desktop:[23,48,59,84],desper:62,dest:[29,35,48,51,56,65],dest_rect:56,dest_siz:56,dest_surf:56,dest_surfac:56,destin:[18,28,29,30,35,42,50,51,56,62,63,85],destroi:[1,11,48,64],destruct:[56,58],destsurfac:18,detail:[15,23,24,29,32,33,34,35,36,37,41,50,51,56,57,61,71,84],detect:[23,26,35,36,42,44,45,50,57],determin:[19,20,22,23,24,25,28,29,31,33,36,37,40,41,50,51,56,59,64,65,68,69,72,87],dev13:25,dev3:[25,54],dev7:39,dev8:24,dev:[18,57],deva:28,devanagari:28,develop:[23,28,33,36,39,44,45,48,51,53,56,63,67,84,86],devic:[15,18,19,25,26,32,33,37,39,44,47,55,57,59,63,84],device_id:[26,37],device_index:[25,32],devicenam:38,dga:23,diagon:[35,56],diagram:[87,88],diamond:22,dict:[1,5,17,20,23,25,32,35,45,47,51,53,55],dictionari:[1,5,23,25,45,47,50,53,64,84],did:[18,24,62,63,64],didn:[84,85],diff:65,differ:[15,18,19,20,22,23,25,26,27,28,29,30,32,33,35,38,40,42,44,48,50,51,56,57,58,59,60,62,63,64,65,68,69,70,84,85,87,88],difficult:[37,61,63],digit:[20,32,36],dilemma:67,dimens:[17,18,23,24,26,28,29,30,35,36,42,43,45,50,51,52,56,58,65,89],dimension:[20,24,29,30,35,36,42,62,65],dimgrai:21,dimgrei:21,direct:[18,23,24,25,26,29,30,32,36,39,43,47,51,58,62,65,70,84,87,89],directfb:23,directli:[14,17,22,24,25,28,29,31,33,42,43,46,49,51,52,53,62,64,65,67,84],directmedia:63,directori:[23,28,29,53,58,62,67,68,86],directx:[23,37,63],dirti:[50,64,84],dirty_rect:84,dirtysprit:50,disabl:[23,25,29,33,44,47,51,54,58,66],disable_advanced_featur:44,disadvantag:[59,84],disallow:23,disappear:[18,30,46],disc:19,discard:54,disclaim:18,disconnect:[32,61],discontinu:[17,51,67],discourag:27,discov:[63,84],discret:71,discuss:[64,84],disk:[50,84],displac:69,displai:[0,1,15,22,24,25,26,30,32,33,34,36,38,39,41,44,46,48,50,51,53,57,62,63,64,65,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84,85,87,88,89],display_index:[25,48],distanc:[36,42,56],distance_squared_to:36,distance_to:36,distil:84,distort:23,distribut:[15,18,53,58],dive:68,divers:61,divid:[33,61],divis:[20,22,84],dizzi:[58,66],doc:[23,27,48,50,51,70,78,84],document:[18,34,38,44,51,57,59,61,62,63,64,65,84,86,87],dodgerblu:21,dodgerblue1:21,dodgerblue2:21,dodgerblue3:21,dodgerblue4:21,doe:[3,4,7,12,19,22,23,24,25,26,28,29,30,32,33,35,36,37,38,40,41,43,44,45,46,47,50,51,54,56,57,58,62,64,65,68,84,86,88],doesn:[18,23,26,28,29,44,51,58,62,64,72,73,84,86,87,88,89],dokil:[50,64],dokill1:[50,64],dokill2:[50,64],dollar:33,domain:26,don:[9,18,24,26,29,32,36,50,54,56,57,58,61,62,63,64,65,68,70,74,85,86,87,89],done:[24,28,29,32,40,58,62,63,65,68,70,71,84,87],doreturn:51,dot:[29,36],doubl:[9,22,23,56,58,63,65],doublebuf:[23,84],doubler:56,down:[23,29,32,33,39,44,47,51,57,59,62,63,65,68,69,71,72,84,88],download:[63,89],dpad:47,dpi:29,drastic:85,draw:[15,20,23,28,29,31,32,35,48,50,51,57,62,63,64,66,67,68,71,72,73,79,80,81,84,85,89],draw_blend_mod:48,draw_bottom_left:24,draw_bottom_right:24,draw_circle_part:24,draw_color:48,draw_lin:48,draw_point:48,draw_rect:[45,48],draw_top_left:24,draw_top_right:24,drawboard:[73,81],drawbutton:[72,73,80,81],drawhp:[71,72,73,79,80,81],drawn:[23,24,30,32,35,50,51,58,62,63,64,68,84,85],dream:67,drew:62,drift:32,drive:19,driven:[23,50,67],driver:[23,37,44,59],drivernam:44,drop:[25,29,38],dropbegin:25,dropcomplet:25,dropfil:25,dropout:38,droptext:25,dstobj:11,dstrect:[11,48],dt:15,dualshock:47,due:[20,23,25,36,51,84],dull:84,dummi:[58,64,88],dump:53,dungeon:63,duplic:[50,51],durat:[32,47],dure:[18,23,29,30,33,40,42,52,65],dvd:[19,69,77],dx:[25,35,51,87,89],dy:[25,35,51,87,89],dynam:[9,69,84],e:[5,20,23,24,28,30,31,32,33,35,36,37,38,44,45,50,51,57,61,84,85,87,88],each:[17,18,19,20,23,24,25,26,27,28,29,30,32,35,36,38,39,42,46,49,50,51,52,53,56,57,58,60,61,62,63,64,65,71,84,85,87,88,89],earli:[15,48,63,84],earlier:[26,40,43,62],easi:[15,26,48,59,60,61,62,63,64,65,70,84,88,89],easier:[45,50,51,59,60,62,87],easiest:[26,47,62,71,85],easili:[22,44,57,58,60,62,64,65,69,74,84,87],east:22,eat:64,echo:26,eclass:9,eclecti:57,ed:[51,64],edg:[24,28,39,45,56,89],edit:[33,47],editbox:84,editor:[33,84],effect:[15,23,25,26,28,32,40,44,46,47,50,51,56,57,58,62,63,64,65,73,84,86],effici:[15,23,25,28,50,62,64,84],effort:74,eg:[26,29,31],eight:26,either:[14,17,19,22,23,29,31,32,36,38,39,45,50,51,56,58,59,89],eject:19,element:[3,17,20,29,36,37,42,43,49,51,62,64,65],elementari:87,elementwis:36,elif:[39,58,66,69,70,71,72,73,77,78,79,80,81,88,89],ellips:[15,24,30],ellipt:[24,30],els:[1,8,33,36,43,44,45,46,50,58,59,64,66,69,86,89],elsewher:[1,84],emb:23,embed:[23,29],emit:39,emoji:28,emphasi:84,emploi:84,empti:[5,19,23,25,27,28,29,32,33,35,44,45,46,50,53,59,64],emul:[23,28,39,59],enabl:[23,25,28,29,33,44,47,48,63,67],encapsul:88,enclos:[24,50,68],encod:[9,14,22,28,29,40,44,46,51],encode_file_path:44,encode_str:44,encount:35,end:[19,24,25,30,31,33,38,40,44,45,50,58,61,62,63,66,69,74,84,85,88],end_index:65,end_po:24,endcap:[24,30],endev:[38,40],endian:[17,44],endpoint:[24,30,45],enemi:[36,64],engin:[63,67,84],enhanc:[15,28,29],enjoy:70,enlarg:29,enough:[29,50,57,58,60,62,63,64,65,67,71,84],ensur:[23,25,27,33,38,53,62,63,84,85,86],entail:84,enter:[25,28,33,39,48,58,67],entertain:32,entir:[15,19,23,24,35,38,48,50,51,62,63,64,68,69,70,72,84],entiti:62,entri:[17,33,51,84],enumer:18,env:[66,86],env_var:44,environ:[23,25,28,32,37,44,46,63,67,68,86,87],epsilon:36,equal:[13,17,20,22,24,25,29,33,35,36,51,67,73],equat:[35,69],equip:65,equival:[17,29,35,37,42,43,84],eras:[32,35,50,58,62,63,64,84],err:[86,89],errno:37,error:[1,2,6,9,11,20,23,25,26,28,29,33,36,37,43,44,46,51,53,56,58,60,64,65,74,84,86],error_msg:44,errorstr:44,es:23,esc:26,escap:[14,29,33,58,84,89],especi:[25,29,44,64,65,86],essenti:65,etc:[25,31,32,37,42,45,61,65,67,84],etyp:44,euclidean:[36,42],euro:33,eval:53,evalu:58,even:[14,18,19,23,24,26,28,29,32,36,40,42,44,54,57,62,63,64,65,67,68,72,84],event:[0,15,18,22,23,24,26,32,33,34,37,38,39,40,47,54,57,61,62,63,66,67,69,71,72,73,75,76,77,78,79,80,81,89],event_nam:25,eventlist:[25,26],eventtyp:[5,25],eventu:[12,23,25],ever:[37,40,59],everi:[15,18,19,22,25,26,32,33,35,36,38,40,44,50,51,52,54,56,59,61,62,63,64,65,67,68,69,70,71,74,84,85,86,88],everyth:[25,43,44,62,63,64,65,66,67,68,73,84,85,89],evil:84,evolv:26,ex:[39,70],exact:[20,22,23,24,31,32,39,57,59,72,73],exactli:[37,42,45,51,58,62,63,64,65,68,84],examin:15,exampl:[10,15,17,20,22,23,24,25,27,28,29,30,31,32,33,35,36,37,38,39,40,44,45,46,47,49,50,51,53,56,57,60,61,62,63,64,67,68,70,71,74,84,85,86,87,88],exce:51,exceedingli:29,excel:[58,62,63],except:[1,3,4,5,6,7,8,9,11,14,20,22,23,24,25,28,29,31,33,37,38,44,45,46,51,56,60,61,65,69,86,89],exchang:42,excit:[58,62,63,65],exclaim:33,exclud:[17,25,51,53],exclus:[37,51],execut:[15,39,53,66,67,68,69,70,71,73,84],exemplari:18,exist:[18,19,22,23,24,25,28,29,32,33,37,47,48,50,51,64],exit:[23,24,32,37,44,48,62,63,67,68,69,70,71,72,73,75,76,77,78,79,80,81,85,86,89],expand:[56,61],expans:56,expect:[20,27,31,33,36,37,38,39,43,53,57,62],expens:65,experi:[25,63,64,70],experiment:[18,23,25,28,30,33,36,44,45,46,48,51,56,57],expir:53,explain:[58,62,64,68,71,73,86,87,89],explan:[26,36,60,64,66,68,70,84],explanatori:58,explicit:[23,39,43],explicitli:[18,23,29,30,42,54,56,58,60],explor:63,explos:64,expos:[2,10,29,43,51],express:[18,20],extend:[17,24,31,44,50],extens:[1,2,3,4,5,6,7,8,9,11,12,15,31,56,63],extern:[22,25,49,52,67],extptr:9,extra:[22,29,38,45,50,51,58,59,62,63,64,65,70,84,89],extract:[42,56,58],extrem:[26,58,62,65],ey:63,f10:33,f11:33,f12:33,f13:33,f14:33,f15:33,f1:33,f2:33,f3:33,f4:33,f5:33,f6:33,f7:33,f8:33,f9:33,f:[1,17,18,32,33,45,84,86,89],face:15,fact:[30,64,84],factor:[26,29,56,65],fade:[26,38,40,65],fade_m:[38,40],fadeout:[38,40],fail:[23,25,27,29,44,53,58,60,84],failur:[1,2,3,4,5,7,8,53],fairli:[50,60,63,84,86,87],fake:[26,28,53],fall:[28,29,50],fals:[2,4,5,6,11,12,15,17,18,19,22,23,24,25,26,28,29,31,32,33,35,36,37,38,39,40,44,45,46,47,48,50,53,56,57,58,62,66,89],famili:28,familiar:[62,63,64,84,85,88],fan:15,fantast:62,far:[20,36,58,62,71,84,87,88,89],farther:[58,62],fast:[32,35,42,51,52,56,58,62,63,64,65,69,84],faster:[31,36,44,50,51,52,56,58,64,65,67,84],fastest:[23,51,58,63],fastev:27,fastrendergroup:26,favorit:84,favour:27,fbcon:23,fear:84,featur:[15,23,25,26,27,28,29,35,36,44,45,50,51,56,59,64,65,84,87],fed:18,feed:89,feedback:[23,28,33,36,45,51,56],feel:[26,62,64,84],felt:63,fetch:50,fever:[58,66],few:[22,28,54,62,63,64,65,74,84,86],ffff:14,fgcolor:29,field:[4,8,12,17,32,37],fighter:63,figur:[62,84],file:[0,2,3,4,5,6,7,8,9,10,11,12,13,15,22,23,25,26,28,29,31,38,40,44,49,53,58,61,63,65,67,68,73,84,86],file_path:[26,28],filenam:[6,22,28,29,31,38,40,62,86],filenotfounderror:[86,89],fileobj:[31,40],filesystem:14,fill:[5,8,9,15,22,23,24,25,26,29,30,32,35,39,43,48,50,51,56,57,58,62,63,64,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84,85,89],fill_rect:48,filled_:30,filled_circl:30,filled_ellips:30,filled_polygon:30,filled_trigon:30,filter:[15,25,35,56,65],fin:26,final_level:36,find:[15,18,19,23,24,25,26,28,32,35,36,38,50,51,56,57,58,59,62,63,64,65,69,71,84,87,89],find_channel:38,fine:[38,51,59,64,84],finer:57,finger:[55,70,86],finger_id:25,fingerdown:25,fingermot:25,fingerup:25,finish:[12,33,38,40,51,60,61,62,63,65,84],finit:35,fire:[65,84],firebrick1:21,firebrick2:21,firebrick3:21,firebrick4:21,firebrick:21,firmer:[61,63],first:[3,17,19,24,26,28,29,30,31,32,33,35,36,37,38,40,42,43,45,49,50,51,52,54,57,58,59,60,61,63,64,65,68,69,70,71,72,73,84,85,87,88,89],firstli:70,fist:[15,58,66],fist_offset:[58,66],fit:[16,18,23,29,38,45,51,85],five:[38,39,40],fix:[25,29,51,56,63,65,68,69,71,84,89],fixed_s:29,fixed_width:29,flag:[1,17,20,23,24,26,28,29,32,34,35,43,50,51,58,59,64,84],flame:65,flash:15,flavor:84,flesh:15,flexibl:[15,60,64,86,87],flip:[15,18,22,23,24,25,31,32,35,39,42,45,48,50,56,57,58,63,65,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84,85,89],flip_i:[48,56],flip_x:[48,56],flood:[73,81],floor:20,floralwhit:21,flourish:70,flush:[31,37],fly:[45,84,85,88],fnt:29,focu:[23,25,32,33,39,44,48,57,67],folder:[58,63],folk:62,follow:[17,18,22,24,25,26,29,30,31,32,33,35,36,37,38,40,45,46,47,49,50,52,58,61,63,64,65,66,67,84],font:[6,15,26,32,44,58,60,63,66,68,69,70,71,72,73,76,77,78,79,80,81,84,85],font_index:[6,29],fonti:26,foo:84,fool:[63,65],forbidden:9,forc:[23,26,38,44,63],forcibl:38,forego:25,foreground:[25,29],foreign:62,forestgreen:21,forev:[62,84],forget:[32,64,87],form:[18,22,25,30,37,51,84,85],formal:62,format:[18,20,22,23,24,29,30,31,35,37,38,40,41,42,43,44,45,49,51,52,56,58,59,62,63,65,68,84,85],formula:[20,36,42,44,51,56,87],forth:58,fortun:[26,62,65],forward:33,found:[15,20,26,28,29,33,34,35,37,45,49,50,51,52,58,59,62,65,70,85,88],four:[3,19,22,23,24,29,45,52,70,89],fourth:[22,71],fout:26,fp:[15,46,50,69,77,84],fpsclock:[69,70,71,72,73,77,78,79,80,81],fraction:29,frame:[15,18,23,25,27,32,44,50,54,57,58,62,63,64,69,77,84,85,88,89],framebuff:23,framer:[15,18,54,57,58,62],framework:[26,63,84],free:[9,25,26,40,62,64,84],freed:11,freedom:[15,63],freesansbold:28,freetyp:[6,15,26,28,29,44],freetype2:29,freetype_misc:[26,29],frequenc:[37,38],frequency_to_midi:37,frequent:[32,62,64,69,84],fresh:84,friendli:[24,68],friendlier:63,frill:64,from:[0,3,5,6,8,15,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,42,43,44,45,46,47,48,49,50,51,52,53,56,57,58,60,61,63,64,65,67,68,69,70,71,72,73,76,77,78,79,80,81,84,85,86,87,88,89],from_display_modul:48,from_joystick:47,from_polar:36,from_spher:36,from_surfac:[35,48,50],from_threshold:[35,57],from_window:48,frombuff:31,frombyt:31,fromstr:31,front:50,frontend:25,frozen:[53,84],frustrat:84,ftfont:[28,29],fuchsia:21,full:[15,23,25,26,27,28,29,31,32,38,39,40,42,50,51,56,57,58,59,62,63,64,66,70,84,85,86,89],fulli:[20,23,33,45,48,50,51,52,58,62,65,84],fullnam:[58,66,86,89],fullscreen:[23,34,48,50,59],fullscreen_desktop:48,fun:[26,57,62,63,65,84],func:45,fundament:[61,84],funni:64,further:46,furthermor:[68,70,71],futur:[18,19,23,25,27,28,29,32,37,38,41,46,64],g:[20,23,24,26,28,30,31,33,36,37,42,43,44,45,50,51,56,84,87,88],gain:[23,25,29,57,84],gainsboro:21,game:[15,16,18,22,23,25,26,27,32,39,44,47,51,54,57,59,60,62,64,66,67,68,69,70,71,72,73,74,86,88,89],gameobject:[62,84],gamepad:[25,47],gameplai:[33,64,89],gamma:[20,23],gap:[17,51],garbag:[12,50,51],gather:59,gaussian:65,gener:[1,15,23,25,26,31,32,33,36,37,39,40,42,47,51,52,56,57,58,63,64,65,68,73,84,86,89],generateboard:[73,81],geniu:74,geometri:[67,71],get:[2,15,18,19,20,22,23,24,25,26,27,28,29,31,32,33,35,37,38,39,40,42,43,44,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,62,63,64,65,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,85,86,87,88,89],get_abs_offset:51,get_abs_par:51,get_act:23,get_al:19,get_allow_screensav:23,get_alpha:[51,86,89],get_and_flip:57,get_arraytyp:[49,52],get_asc:28,get_at:[35,51,56,58,65,66,84],get_at_map:[20,51],get_axi:[32,47],get_backend:18,get_bal:32,get_bits:51,get_block:25,get_bold:28,get_bottom_lay:50,get_bounding_rect:[35,51],get_buff:[2,17,51],get_busi:[19,38,40],get_button:[32,47],get_bytes:[42,51],get_cache_s:29,get_capt:23,get_clip:[50,51],get_colorkei:51,get_control:[18,57],get_count:[19,32,37,47],get_curr:19,get_cursor:39,get_default_font:[28,29],get_default_input_id:37,get_default_output_id:37,get_default_resolut:29,get_desc:28,get_desktop_s:23,get_devic:55,get_device_info:37,get_driv:[23,59],get_empti:19,get_endev:[38,40],get_error:[29,44],get_eventst:47,get_extend:31,get_fing:55,get_flag:51,get_focus:[23,33,39],get_font:28,get_fp:54,get_grab:25,get_guid:32,get_hardwar:41,get_hat:32,get_height:[15,28,51],get_id:[19,32],get_imag:[18,57],get_init:[19,23,27,28,29,32,37,38,44,46,47,49,58,60,66],get_instance_id:32,get_ital:28,get_keyboard_grab:25,get_layer_of_sprit:50,get_length:38,get_lines:28,get_lock:51,get_loss:51,get_map:47,get_mask:51,get_metr:29,get_mod:33,get_nam:[19,32],get_num_channel:38,get_num_devic:55,get_num_displai:23,get_num_fing:55,get_numax:32,get_numbal:32,get_numbutton:32,get_numhat:32,get_numtrack:19,get_offset:51,get_palett:51,get_palette_at:51,get_par:51,get_paus:19,get_pitch:51,get_po:[39,40,58,66,84],get_power_level:32,get_press:[15,33,39,62,84],get_queu:38,get_raw:[18,38],get_rawtim:54,get_rect:[29,35,48,50,51,56,58,62,63,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,85,86,87,88,89],get_rel:39,get_repeat:33,get_sdl_byteord:44,get_sdl_image_vers:31,get_sdl_mixer_vers:38,get_sdl_ttf_vers:28,get_sdl_vers:[23,44],get_shift:51,get_siz:[18,29,35,42,51,58,66,85,89],get_sized_ascend:29,get_sized_descend:29,get_sized_glyph_height:29,get_sized_height:29,get_smoothscale_backend:56,get_sound:38,get_sprit:50,get_sprites_at:50,get_sprites_from_lay:50,get_strikethrough:28,get_surfac:[23,58,66,87,88,89],get_tick:54,get_tim:54,get_top_lay:50,get_top_sprit:50,get_track_audio:19,get_track_length:19,get_track_start:19,get_typ:46,get_underlin:28,get_vers:29,get_view:[17,51],get_viewport:48,get_vis:39,get_volum:[38,40],get_width:[15,51,58,66],get_window_s:23,get_wm_info:23,getbufferproc:2,getch:[67,75],getfilesystemencod:[14,44],getopt:[86,89],gfxdraw:[10,15,24,30],gg:20,ggi:23,ghost:64,ghostwhit:21,gif:[31,63,71,72,73,79,80,81,84],gil:[9,30],github:[44,46,56],give:[15,17,22,23,29,32,37,40,42,50,51,58,59,60,61,65,74,84,85,86,87],given:[8,13,17,20,22,23,24,25,28,29,30,32,35,36,37,38,39,44,45,46,47,50,51,52,54,55,56,58,59,64,68,70,73,84],gl:23,gl_accelerated_visu:23,gl_accum_alpha_s:23,gl_accum_blue_s:23,gl_accum_green_s:23,gl_accum_red_s:23,gl_alpha_s:23,gl_buffer_s:23,gl_context_flag:23,gl_context_major_vers:23,gl_context_minor_vers:23,gl_context_profile_:23,gl_context_profile_compat:23,gl_context_profile_cor:23,gl_context_profile_mask:23,gl_context_release_behavior:23,gl_depth_siz:23,gl_framebuffer_srgb_cap:23,gl_get_attribut:23,gl_multisamplebuff:23,gl_multisamplesampl:23,gl_set_attribut:23,gl_share_with_current_context:23,gl_stencil_s:23,gl_stereo:23,glcube:26,glitch:89,global:[28,53,60,68,86,89],glsl:36,glue:89,glyph:[28,29],gnu:[86,89],go:[20,22,23,24,32,36,37,51,53,57,58,59,60,61,64,65,66,68,70,74,84,85,86,88,89],goal:63,goe:[58,62,64,68,84,89],gold1:21,gold2:21,gold3:21,gold4:21,gold:21,golden:59,goldenrod1:21,goldenrod2:21,goldenrod3:21,goldenrod4:21,goldenrod:21,gone:[62,89],good:[15,18,22,23,26,28,38,44,51,58,61,62,63,64,65,67,68,84,85,86,87,89],goodluck:26,googl:84,got:[15,25,40,57,62,64,84],gotten:62,grab:[25,33,39,48,62,64,89],grace:89,gradient:[26,35,65],grai:[21,29,71,72,73,79,80,81],grain:84,graphic:[23,26,56,58,59,62,63,64,65,67,68,84],grasp:[61,89],grave:33,gray0:21,gray100:21,gray10:21,gray11:21,gray12:21,gray13:21,gray14:21,gray15:21,gray16:21,gray17:21,gray18:21,gray19:21,gray1:21,gray20:21,gray21:21,gray22:21,gray23:21,gray24:21,gray25:21,gray26:21,gray27:21,gray28:21,gray29:21,gray2:21,gray30:21,gray31:21,gray32:21,gray33:21,gray34:21,gray35:21,gray36:21,gray37:21,gray38:21,gray39:21,gray3:21,gray40:21,gray41:21,gray42:21,gray43:21,gray44:21,gray45:21,gray46:21,gray47:21,gray48:21,gray49:21,gray4:21,gray50:21,gray51:21,gray52:21,gray53:21,gray54:21,gray55:21,gray56:21,gray57:21,gray58:21,gray59:21,gray5:21,gray60:21,gray61:21,gray62:21,gray63:21,gray64:21,gray65:21,gray66:21,gray67:21,gray68:21,gray69:21,gray6:21,gray70:21,gray71:21,gray72:21,gray73:21,gray74:21,gray75:21,gray76:21,gray77:21,gray78:21,gray79:21,gray7:21,gray80:21,gray81:21,gray82:21,gray83:21,gray84:21,gray85:21,gray86:21,gray87:21,gray88:21,gray89:21,gray8:21,gray90:21,gray91:21,gray92:21,gray93:21,gray94:21,gray95:21,gray96:21,gray97:21,gray98:21,gray99:21,gray9:21,grayscal:[20,56],great:[23,57,63,65,84],greater:[17,24,28,29,33,35,36,37,40,51,74],green1:21,green2:21,green3:21,green4:21,green:[1,18,20,21,23,24,42,51,52,56,57,58,65,68,69,70,71,72,73,76,77,78,79,80,81,85],greenyellow:21,grei:[21,58],grey0:21,grey100:21,grey10:21,grey11:21,grey12:21,grey13:21,grey14:21,grey15:21,grey16:21,grey17:21,grey18:21,grey19:21,grey1:21,grey20:21,grey21:21,grey22:21,grey23:21,grey24:21,grey25:21,grey26:21,grey27:21,grey28:21,grey29:21,grey2:21,grey30:21,grey31:21,grey32:21,grey33:21,grey34:21,grey35:21,grey36:21,grey37:21,grey38:21,grey39:21,grey3:21,grey40:21,grey41:21,grey42:21,grey43:21,grey44:21,grey45:21,grey46:21,grey47:21,grey48:21,grey49:21,grey4:21,grey50:21,grey51:21,grey52:21,grey53:21,grey54:21,grey55:21,grey56:21,grey57:21,grey58:21,grey59:21,grey5:21,grey60:21,grey61:21,grey62:21,grey63:21,grey64:21,grey65:21,grey66:21,grey67:21,grey68:21,grey69:21,grey6:21,grey70:21,grey71:21,grey72:21,grey73:21,grey74:21,grey75:21,grey76:21,grey77:21,grey78:21,grey79:21,grey7:21,grey80:21,grey81:21,grey82:21,grey83:21,grey84:21,grey85:21,grey86:21,grey87:21,grey88:21,grey89:21,grey8:21,grey90:21,grey91:21,grey92:21,grey93:21,grey94:21,grey95:21,grey96:21,grey97:21,grey98:21,grey99:21,grey9:21,greyscal:56,grid:29,ground:62,group1:[50,64],group2:[50,64],group:[26,35,50,58,63,84,86],group_list:50,groupcollid:[50,64,87],groupmulti:64,groupsingl:[50,64],grow:[24,45,84],guarante:[18,23,25,33,38,46,53],guess:[38,84],gui:[25,63,67,71,72,73,75,79],guid:[15,32,57,65,85],gun:71,h:[1,2,3,4,5,6,7,8,9,10,11,12,13,20,25,26,30,33,42,43,45,51,56],ha:[1,2,10,15,17,18,19,22,23,24,25,26,27,28,29,31,32,33,35,36,38,39,40,42,43,44,45,46,47,49,50,51,52,53,54,56,57,58,59,60,62,63,64,65,67,68,69,70,71,72,73,84,86,87,89],habit:[84,87],had:[24,51,63,64,84],hadn:84,half:[24,26,36,50,63],hand:[20,22,42,43,58,60,62,84,87],handi:[26,61,84,86,87,88,89],handili:89,handl:[9,14,15,23,25,28,29,32,33,38,43,44,50,56,59,61,63,64,65,66,70,84,87,89],handler:[25,27],hang:32,happen:[19,24,58,60,62,63,64,84,85,88,89],hard:[62,63,65,68],hardcod:26,harder:[61,65,84],hardest:64,hardwar:[22,23,24,30,32,37,38,41,51,63,64,65,84],harfbuzz:28,harmless:23,hasattr:28,hash:33,hashabl:45,hasn:29,hat:[25,32,47],hat_numb:32,have:[1,3,8,10,18,19,20,22,23,24,25,26,27,28,29,30,32,33,35,36,38,40,41,42,43,44,45,47,50,51,52,53,54,56,57,58,59,60,61,62,63,64,65,67,68,69,70,72,73,84,85,86,87,88,89],haven:84,he:[58,84,85],header:[0,1,2,3,4,5,6,7,8,9,11,12,13,68,70,76],headless:26,headless_no_windows_need:26,heavili:25,height:[8,18,22,23,24,26,28,29,31,32,35,41,42,45,48,50,51,56,59,62,63,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84],held:[33,62,88],hello:[68,69,70,76,77,78,85],help:[15,22,25,26,28,32,33,36,37,38,39,48,50,51,53,54,58,59,61,64,65,70,84,86,87,88],helper:63,henc:29,here:[15,16,18,23,25,26,34,35,39,41,44,50,51,58,59,60,64,65,66,68,70,84,85,86,87,88,89],hex:20,hflip:[18,57],hi:[58,62],hidden:[23,25,39],hide:[33,39,44,48,58],high:[0,23,26,50,67,69],high_frequ:[32,47],higher:[15,16,23,36,37,47,50,52,59,63,64],highest:44,highgui:44,highli:[27,51,84],him:[58,62],hindi:28,hint:[23,84],hit:[25,26,58,61,84,85,87,88],hitbox:[58,66],hkey_local_machin:37,hline:30,hmm:62,hold:[1,25,28,32,33,50,56,64,65,70],holdov:64,home:[32,33],honeydew1:21,honeydew2:21,honeydew3:21,honeydew4:21,honeydew:21,hook:50,hoonwhitecatr:[68,69,70,71,72,73,76,77,78,79,80,81],hope:64,hopefulli:[62,65,87],horizont:[18,24,25,26,29,30,39,56,58,62,65],horizontal_advance_i:29,horizontal_advance_x:29,hot:25,hotkei:25,hotpink1:21,hotpink2:21,hotpink3:21,hotpink4:21,hotpink:21,hotplug:32,hotspot:[22,39],hour:84,how:[14,15,19,20,23,24,26,29,31,32,33,35,36,38,40,44,46,50,51,54,56,58,61,63,64,65,68,69,70,71,72,73,74,84,87,88,89],howev:[12,18,29,31,33,34,36,42,44,45,47,50,56,62,67,68,69,70,74,84],hp:[70,71,72,73,78,79,80,81],hsl:20,hsla:20,hsv:[18,20,57],hsva:20,html:[20,70,78],http:[39,56,70,78,86,89],hue:18,human:[63,74,86],humung:63,hundr:63,husano896:39,hw:[23,59],hwaccel:51,hwsurfac:[23,51,65,84],hx:47,i1:20,i1i2i3:20,i2:20,i3:20,i686:56,i:[1,4,15,17,18,22,24,26,27,32,33,35,36,38,45,57,61,63,64,65,68,70,71,72,73,79,80,81,84,85,86,87,88,89],icc:25,iceberg:64,icon:[23,48],iconifi:23,icontitl:23,id3:40,id:[19,23,25,32,33,37,38,48,55],idea:[23,51,57,59,61,63,65,70,71,72,73,86,87],ident:[25,42,51,72],identif:37,identifi:[25,28,32,33,44,46,57,84],idiom:[58,84],idl:[24,25,27,32,38,40],idx:50,ie:[31,65],ignor:[25,29,35,37,38,40,51,53,84],illeg:45,illus:62,illustr:[62,65,87],im:33,imag:[11,12,15,16,18,20,22,23,26,28,29,30,41,46,48,50,52,56,58,59,61,63,64,65,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84,85,86,87,88,89],image_fil:26,imagefil:26,imagin:[62,85,89],imgsurfac:65,immanuel:74,immedi:[23,25,32,37,38,58],immut:20,implement:[9,10,12,17,18,20,23,26,29,33,37,42,43,44,50,61,63,64,73,74,84,89],impli:[18,29],implicitli:[23,51],import_pygame_bas:[1,10],importantli:62,importerror:[65,86,89],impos:[33,38],imprecis:84,impress:63,improperli:41,improv:[24,28,40,58,73,84],inact:[29,38],inch:29,incident:18,inclin:36,includ:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,15,18,22,23,26,28,29,30,31,32,34,37,44,45,50,51,53,54,56,58,61,63,64,65,67,70,74,84,86,87],inclus:[1,3,20,25,29,38],incom:15,incomplet:53,incorrect:43,increas:[23,29,38,45,50,68,72,74,84],incred:[74,84],increment:[10,56,65],inde:3,indefinit:[38,40],indent:[32,61],independ:[15,31,58,86],index:[1,6,15,18,19,23,25,29,32,33,42,43,44,45,47,48,49,50,51,52,55,62,65,84],indexerror:[30,35,37,51],indexexcept:17,indexoutofbound:50,indianr:21,indianred1:21,indianred2:21,indianred3:21,indianred4:21,indic:[11,23,24,25,30,35,37,39,40,42,45,46,49,50,52,53,65],indices0:45,indices1:45,indices2:45,indices3:45,indices4:45,indices5:45,indigo:21,indirect:18,individu:[15,19,29,35,44,51,53,58,84],ineffici:58,inequ:25,infinit:[58,63,68],inflat:[29,45,58,66,84,89],inflate_ip:45,influenc:23,info:[4,23,37,59,86],inform:[5,13,15,17,18,19,23,25,28,32,33,36,37,39,47,51,52,53,55,59,62,64,65,68],inherit:[36,50,51,64,86,87],init:[1,15,18,19,22,23,24,27,28,29,32,37,38,39,44,46,47,53,54,58,59,62,63,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84,85,89],initi:[15,18,19,23,24,25,27,28,29,32,33,34,37,38,44,46,47,49,50,51,54,57,59,63,64,66,68,69,77,84,87],initialis:[32,44,47,61,85,88,89],innat:74,inner:72,input:[15,18,23,25,26,28,32,33,39,44,45,48,51,53,56,57,61,63,66,67,68,69,72,73,74,78,84,87],input_onli:48,inputimag:26,insensit:[43,51],insert:[33,69,73],insid:[22,24,26,31,44,45,50,51,57,58,63,65,71,72,89],insight:84,inspir:15,instal:[15,18,26,28,31,58,65],instanc:[2,3,4,5,6,7,8,11,12,17,19,25,26,29,30,32,35,36,38,42,45,50,51,58,61,64,85,87,88],instance_id:[25,32],instantli:84,instead:[8,14,20,22,23,24,25,26,28,29,32,33,36,38,42,44,50,51,53,56,62,64,65,69,84,87],instruct:56,instrument:37,instrument_id:37,int32:65,int8:65,int_valu:51,integ:[1,8,13,17,19,20,23,24,25,26,28,29,30,31,32,33,37,38,39,40,42,43,44,45,47,49,50,51,52,53,54,55,65,71,73],integr:18,intend:[25,43,50,51],interact:[5,15,26,33,34,42,47,50,53,62,63,65,70,87],interest:[25,46,63,67,74,84],interf:37,interfac:[1,17,20,29,33,37,38,42,43,47,51,57,63,70,73],interior:56,intern:[0,12,23,24,25,27,33,38,44,46,51,58,68,70,84,89],internet:84,interpol:[20,30,36],interpret:[14,26,29,44,61,84],interrupt:18,intersect:[35,45,50,64,67],interv:33,intimid:65,intric:63,intro_bal:63,introduc:[23,25,31,43,61,62,65,87,89],introduct:[15,60,67,75],introspect:43,intuit:[70,84],invad:84,invalid:[1,23,38,45,47,56],invalu:87,inverse_set:56,invert:[29,35,36,42],invis:85,invok:[17,29,88],involv:[15,26,29,51,64,84,87,89],inward:24,ip:45,is_control:47,is_norm:36,iscaptur:25,ish:24,isn:[19,27,48,57,61,62,64,69,71,84,88],iso:28,isol:[51,57],issu:[23,62,63],ital:[28,29],item:[29,30,42,43,45,50,62,64],items:42,iter:[28,29,33,42,45,50,64,84,85,88,89],its:[2,9,12,17,18,23,24,25,29,30,32,35,36,37,38,42,44,45,46,48,50,51,58,59,61,62,63,64,68,69,70,84,85,86,87,88,89],itself:[12,18,28,29,36,42,43,44,48,51,58,61,62,63,64,84,87,88,89],ivori:21,ivory1:21,ivory2:21,ivory3:21,ivory4:21,iyuv_overlai:41,j:[33,35,73,81],jaggi:56,jid:32,jitter:32,job:[63,64],joi:25,join:[31,45,58,66,86,89],joint:24,journal:84,joyaxismot:[25,32],joyballmot:[25,32],joybuttondown:[25,32],joybuttonup:[25,32],joydevicead:[25,32],joydeviceremov:[25,32],joyhatmot:[25,32],joystick:[15,25,26,44,47,63,84,85,88],joystick_count:32,jp:18,jpeg:[31,84],jpg:[31,63],juli:16,jumbl:84,jump:[39,62,63,65],just:[24,26,28,29,30,32,35,38,40,41,50,51,52,53,56,57,58,59,60,63,64,65,67,68,69,70,71,73,84,85,87,88,89],k:[33,36],k_0:33,k_1:33,k_2:33,k_3:33,k_4:33,k_5:33,k_6:33,k_7:33,k_8:[33,70,78],k_9:33,k_:[15,33,34,70],k_a:[15,33,70,78,89],k_ac_back:33,k_ampersand:33,k_asterisk:33,k_at:33,k_b:33,k_backquot:33,k_backslash:33,k_backspac:33,k_break:33,k_c:33,k_capslock:33,k_caret:33,k_clear:33,k_colon:33,k_comma:33,k_d:[15,33],k_delet:[33,70,78],k_dollar:33,k_down:[33,62,70,71,72,73,78,79,80,81,88,89],k_e:33,k_end:33,k_equal:33,k_escap:[22,33,57,58,66],k_euro:33,k_exclaim:33,k_f10:33,k_f11:33,k_f12:33,k_f13:33,k_f14:33,k_f15:33,k_f1:33,k_f2:33,k_f3:33,k_f4:[33,70],k_f5:33,k_f6:33,k_f7:33,k_f8:33,k_f9:33,k_f:[33,84],k_g:33,k_greater:33,k_h:33,k_hash:33,k_help:33,k_home:33,k_i:33,k_insert:33,k_j:33,k_k:33,k_kp0:33,k_kp1:33,k_kp2:33,k_kp3:33,k_kp4:33,k_kp5:33,k_kp6:33,k_kp7:33,k_kp8:33,k_kp9:33,k_kp_divid:33,k_kp_enter:33,k_kp_equal:33,k_kp_minu:33,k_kp_multipli:33,k_kp_period:33,k_kp_plu:33,k_l:[33,70,78],k_lalt:33,k_lctrl:[33,70,78],k_left:[33,62,70,78],k_leftbracket:33,k_leftparen:33,k_less:33,k_lmeta:33,k_lshift:33,k_lsuper:33,k_m:33,k_menu:33,k_minu:33,k_mode:33,k_n:33,k_numlock:33,k_o:33,k_p:33,k_pagedown:33,k_pageup:33,k_paus:33,k_period:33,k_plu:33,k_power:33,k_print:33,k_q:33,k_question:33,k_quot:33,k_quotedbl:33,k_r:33,k_ralt:33,k_rctrl:33,k_return:33,k_right:[33,62,70,78],k_rightbracket:33,k_rightparen:33,k_rmeta:33,k_rshift:33,k_rsuper:33,k_scrollock:33,k_semicolon:33,k_slash:33,k_space:33,k_sysreq:33,k_t:[33,84],k_tab:33,k_u:33,k_underscor:33,k_up:[33,62,70,71,72,73,78,79,80,81,88,89],k_v:33,k_w:[15,33],k_x:33,k_y:33,k_z:[33,89],kanji:29,kant:74,kb:16,kde:44,keep:[12,17,25,32,50,54,56,57,58,59,60,61,62,63,64,84,88],kei:[15,17,20,22,23,25,26,33,34,35,45,51,55,57,58,61,62,63,64,66,70,71,72,73,74,78,79,80,81,84,88,89],kern:[28,29],key_cod:33,key_rect:45,keyboard:[15,25,26,34,39,62,63,67,70,84,85],keycod:33,keydown:[22,25,33,34,57,58,62,66,70,71,72,73,78,79,80,81,88,89],keymap:25,keymapchang:25,keypad:33,keypress:84,keyup:[25,33,34,70,88,89],keyword:[15,18,23,24,25,26,29,31,35,38,44,45,48,51,53,56],khaki1:21,khaki2:21,khaki3:21,khaki4:21,khaki:21,khz:49,kick:61,kill:[50,53,64],kind:[25,37,43,51,61,65,67,84],kmod_alt:33,kmod_cap:33,kmod_ctrl:33,kmod_lalt:33,kmod_lctrl:33,kmod_lmeta:33,kmod_lshift:33,kmod_meta:33,kmod_mod:33,kmod_non:33,kmod_num:33,kmod_ralt:33,kmod_rctrl:33,kmod_rmeta:33,kmod_rshift:33,kmod_shift:33,know:[18,28,31,33,46,57,58,62,64,65,67,68,69,70,73,87,88,89],knowledg:[74,84],known:[33,84],korean:15,kwarg:[35,48,50,51],kwd:53,l:[20,32,33],l_margin:[73,81],lack:23,laggi:38,laid:[29,61],lambda:45,landscap:[62,84],languag:[25,26,28,33,62,63,84],lantinga:63,laplacian:56,larg:[15,29,36,43,45,51,61,64,65,84,87],larger:[23,38,51,56,58,65,67,84],largest:[35,57,59,63,65],last:[15,24,29,30,32,50,51,52,54,58,62,63,64,65,68,84],lastli:[22,50,58,59,62,64,65],late:84,latenc:[37,38,84],later:[18,23,30,33,37,38,42,46,50,57,58,59,60,62,63,64,65,68,84,86,89],latest:[29,70],latin1:[28,29],latter:25,lavend:21,lavenderblush1:21,lavenderblush2:21,lavenderblush3:21,lavenderblush4:21,lavenderblush:21,lawngreen:21,layer1:50,layer1_nr:50,layer2:50,layer2_nr:50,layer:[16,50,63,64],layer_nr:50,layereddirti:50,layeredupd:50,layout:[17,25,28,29,43,52,88],lbm:31,lead:36,leak:62,learn:[26,51,59,62,63,65,67,68,69,70,73,74,84],learnt:89,least:[13,24,30,35,43,51,55,61,65,67,70,84],leav:[24,28,29,33,36,45,51,56,65],left:[20,23,24,25,29,33,35,38,39,44,45,47,50,51,57,58,62,63,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84,85,86,87,88,89],leftclick:26,leftmost:47,legaci:[25,27,33,67,75],legacy_logo:16,lemonchiffon1:21,lemonchiffon2:21,lemonchiffon3:21,lemonchiffon4:21,lemonchiffon:21,len:[17,20,22,24,30,50,64,65],length:[1,3,8,17,19,20,23,24,25,30,32,33,36,38,42,47,51,58,68,87],length_squar:36,leonidovich:74,lerp:[20,36],less:[24,27,28,33,35,37,38,54,55,56,67,85],lesson:84,let:[9,20,21,26,44,51,58,59,61,63,65,69,70,71,84,88],letter:[19,28,51],level:[0,15,17,20,23,28,32,36,37,38,40,41,51,53,56,63,64,65,67,70],lgpl:15,li:84,liabil:18,liabl:18,lib:26,librari:[15,18,26,28,29,30,31,32,37,38,44,47,59,62,63,67],libsdl:39,licens:[15,86,89],lie:[30,45],life:[57,59],lifetim:[12,42,52,65],lightblu:21,lightblue1:21,lightblue2:21,lightblue3:21,lightblue4:21,lightcor:21,lightcyan1:21,lightcyan2:21,lightcyan3:21,lightcyan4:21,lightcyan:21,lightgoldenrod1:21,lightgoldenrod2:21,lightgoldenrod3:21,lightgoldenrod4:21,lightgoldenrod:21,lightgoldenrodyellow:21,lightgrai:21,lightgreen:21,lightgrei:21,lightpink1:21,lightpink2:21,lightpink3:21,lightpink4:21,lightpink:21,lightsalmon1:21,lightsalmon2:21,lightsalmon3:21,lightsalmon4:21,lightsalmon:21,lightseagreen:21,lightskyblu:21,lightskyblue1:21,lightskyblue2:21,lightskyblue3:21,lightskyblue4:21,lightslateblu:21,lightslategrai:21,lightslategrei:21,lightsteelblu:21,lightsteelblue1:21,lightsteelblue2:21,lightsteelblue3:21,lightsteelblue4:21,lightweight:[50,84],lightyellow1:21,lightyellow2:21,lightyellow3:21,lightyellow4:21,lightyellow:21,like:[1,9,15,18,20,22,23,24,25,26,27,28,29,31,32,33,34,35,36,38,39,40,42,44,45,47,50,51,56,57,58,59,60,61,62,63,64,65,68,69,70,72,73,74,84,85,86,87,88,89],lime:21,limegreen:21,limit:[15,18,19,23,24,25,29,31,32,38,40,51,52,53,54,60,63,64,65,73],line:[15,24,26,28,30,31,32,45,48,53,60,61,62,63,64,65,66,68,69,70,84,87,89],line_height:32,line_spac:29,linear:[20,23,36,50,65],linearli:[36,51],linen:21,link:[28,29,31,38,44],linux:[18,23,37,40,44,57,63,84],liquid:[26,62],list:[15,18,19,20,22,23,24,25,26,27,28,29,30,32,33,34,35,37,41,44,45,46,50,51,53,58,59,64,65,68,70,84],list_camera:[18,57],list_mod:[23,59],list_of_double_tupl:45,list_of_list:45,list_of_object_with_callable_rect:45,list_of_object_with_rect_attribut:45,list_of_rect:45,list_of_tupl:45,listen:39,littl:[17,22,26,44,50,54,58,59,61,62,64,65,68,84,87,88],live:[15,26,65],ll:[22,36,58,61,62,63,64,65,84,85,87,88,89],load:[7,15,18,22,26,30,31,40,50,59,61,62,63,65,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84,87,89],load_background_imag:62,load_bas:31,load_extend:31,load_imag:[58,66],load_player_imag:62,load_png:[86,87,88,89],load_sound:[58,66,86],load_xbm:22,local:[8,15,25,33,34,38,39,44,57,58,60,61,68,69,70,71,72,73,76,77,78,79,80,81,85,86,89],localechang:25,locat:[25,26,34,35,39,41,58,68,69,70,71,72,73,84],lock:[12,17,24,25,27,33,42,43,50,51,52],lockobj:12,logger:26,logic:[48,58,61,63,65,68,69,70,71,84,86],logical_s:48,logo:[15,23],loki:63,longer:[19,28,32,38,47,87,88],longest:[38,84],look:[22,24,28,36,37,44,46,51,52,56,57,58,62,63,64,65,68,69,70,72,84,85,86,87,88,89],lookout:26,lookup:[23,25,50,58,65],loop:[15,24,25,32,38,40,54,57,61,62,63,64,66,68,84,86,87,88,89],lose:[19,23,25,33,56],loss:[18,23,58,59,84],lost:[25,40,46,62],lostsprit:64,lot:[26,32,51,54,56,58,59,61,62,63,64,65,67,73,84,85,87,89],loud:38,love:84,low:[25,32,63,67],low_frequ:[32,47],lower:[19,23,32,36,37,38,41,52,84],lowercas:28,lowest:37,lowli:84,ls:18,lt:32,luck:65,luckili:84,luma:18,luminos:[20,56],m:[15,20,26,33,53,58,65],mac:[18,23,26,37,40,44,46,63,84],machin:[26,56,64,65],maco:22,macro:[3,4,5,6,7,8,11,13],made:[5,6,11,15,23,25,49,62,71,73,74,84,87],magazin:[15,71],magenta1:21,magenta2:21,magenta3:21,magenta4:21,magenta:21,magic:[62,84,87],magnifi:26,magnitud:36,magnitude_squar:36,mai:[1,2,18,19,20,22,23,25,27,28,29,30,31,32,33,36,37,38,40,43,44,45,46,51,53,56,57,59,62,63,64,65,84],mail:[62,84],main:[18,25,26,27,32,39,41,46,50,53,56,57,61,62,64,65,66,71,72,73,79,80,81,85,86,87,88,89],main_dir:[58,66],mainli:[28,51,62,64,65],maintain:[28,44,50,61,62,84],major:[13,23,28,31,38,44,63],make:[0,15,20,22,24,25,26,27,31,33,38,42,44,45,50,51,52,54,56,57,58,59,60,63,64,65,67,68,70,71,72,73,74,84,85,86,87,88,89],make_sound:[38,49],make_surfac:[42,43,52],maker:[67,74],malform:29,man:67,manag:[15,19,25,32,38,42,50,51,59,63,64],mani:[19,23,24,25,28,29,33,36,38,39,40,44,45,50,51,54,56,58,59,62,63,84],manifest:84,manipul:[15,31,42,45,51,63,65,84,87],manner:[59,87],manual:[19,28,29,32,41,44,51,57,64],map:[17,20,24,42,43,47,51,52,59,65],map_arrai:[43,52],map_rgb:[20,24,42,51,52],mapped_int:51,margin:[26,36,68,71,72,73,80,81],mario:88,mark:[33,42,57,63],maroon1:21,maroon2:21,maroon3:21,maroon4:21,maroon:21,mask:[22,23,26,29,42,50,51,59,84],maskfil:22,mass:35,master:[65,67],match:[17,23,28,29,31,32,33,37,38,42,43,51,52,56,58,59,62,63,64,65,68],match_font:28,materi:[18,84],math:[24,30,35,36,86,87,89],mathemat:[51,61,65],matter:[31,51,62,64,84,87],max:[24,32,35,36,57,71],max_i:29,max_length:36,max_level:36,max_x:29,maxhp:[71,72,73,79,80,81],maxi:28,maxim:[25,48],maximum:[23,28,29],maxtim:38,maxx:28,mayb:[62,64,70,84,86],mb:16,me:[59,63,65,84],mean:[15,20,22,23,24,29,32,33,36,37,38,39,40,43,47,48,50,56,57,58,59,62,63,64,68,69,70,71,72,84,88],meant:[44,48,64],measur:[24,37,84,87],mechanim:67,mechanin:75,mediev:63,medium:[24,32],mediumaquamarin:21,mediumblu:21,mediumorchid1:21,mediumorchid2:21,mediumorchid3:21,mediumorchid4:21,mediumorchid:21,mediumpurpl:21,mediumpurple1:21,mediumpurple2:21,mediumpurple3:21,mediumpurple4:21,mediumseagreen:21,mediumslateblu:21,mediumspringgreen:21,mediumturquois:21,mediumvioletr:21,meet:[45,84],mega_jc:16,megabyt:23,member:[25,50,59,64,68,84],membership:[50,64],memori:[9,17,23,24,25,29,31,38,51,52,62,67],memoryview:31,mental:74,mention:[54,58,62,64,68,70,84],menu:[23,33,61],merchant:18,mercuri:44,mere:[37,84],merg:64,merrili:89,mess:15,messag:[25,27,37,44,53,58,84],messi:64,messier:84,met:[18,22],meta:[25,33],method:[8,9,11,14,15,17,19,20,22,23,24,25,26,28,29,30,31,32,35,36,38,39,40,42,44,45,46,49,50,51,52,54,58,59,61,62,63,64,65,71,73,84,85,86,87,89],metric:[28,29],mice:39,micro:44,microsoft:23,midbottom:45,middl:[39,47,57,62,65],midi:[25,26],midi_ev:37,midi_event_list:37,midi_not:37,midi_output:37,midi_to_ansi_not:37,midi_to_frequ:37,midiexcept:37,midiin:[25,37],midiout:[25,37],midis2ev:37,midisport:37,midleft:[45,88,89],midnightblu:21,midpoint:36,midright:[45,88,89],midtop:45,might:[20,23,25,26,29,36,44,46,60,61,62,64,65,84,86,87,89],mighti:62,migrationguid:39,milli:54,millisecond:[25,33,37,38,40,50,54,63],mime:46,mimic:29,min:[24,36,57],min_alpha:51,min_i:29,min_length:36,min_x:29,mind:[39,54,60,65,84],minhp:73,mini:28,miniatur:32,minim:[23,25,48,64],minimum:[23,24,28,30,31,35,51],minor:[13,28,31,38,44,63],mintcream:21,minu:33,minx:28,mirror:[23,58],miss:[58,66,84],missingmodul:[49,52],mistyros:21,mistyrose1:21,mistyrose2:21,mistyrose3:21,mistyrose4:21,misunderstand:84,misunderstood:84,miter:24,mix:[28,38,51,63,65,84],mix_chunk:7,mix_setmusicposit:40,mixer:[0,15,26,38,44,49,58,66],mizuno:18,mmsystem:37,mmx:[26,56],moccasin:21,mod:[25,33,34,40,67],mod_:34,modal:48,mode:[15,20,23,25,26,28,29,30,33,38,39,44,46,48,50,51,53,58,63,65],mode_ok:[23,59],model:51,modern:84,modest:84,modif:[12,18,51,86],modifi:[25,29,30,33,34,37,39,45,48,49,50,51,64,65,85],modnam:1,modul:[0,1,2,3,4,5,6,7,8,9,11,12,15,17,21,26,34,41,42,48,51,53,59,60,61,62,65,66,68,84,87,89],modulu:20,moment:[46,49,84,87],momma:62,monitor:[23,34,59,63,68],monkei:[58,63,66],mono:[29,38,49],monochrom:29,monster:64,month:63,more:[15,18,19,20,23,24,25,26,28,29,30,31,32,35,36,37,38,39,42,43,44,47,50,51,54,57,58,59,60,61,63,64,70,71,73,74,84,85,86,87,88,89],most:[15,19,23,24,26,28,29,31,38,39,40,44,45,50,51,54,57,59,60,61,63,64,65,74,84,85],mostli:70,motion:[32,39,48,63,87,89],motiv:70,mous:[15,22,23,25,26,32,46,48,58,61,62,63,66,67,70,72,84,85,88],mousebuttondown:[22,25,27,39,58,66,84],mousebuttonup:[25,39,58,66,72,73,80,81,84],mousemot:[25,39],mousewheel:[25,39],movabl:[62,88,89],move:[15,25,26,36,39,44,45,50,51,56,58,61,63,65,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84,85,87,88,89],move_and_draw_all_game_object:62,move_ip:[45,58,66],move_to_back:50,move_to_front:50,move_toward:36,move_towards_ip:36,movedown:[70,78,88,89],moveit:[26,62],movement:[32,39,50,58,87,88],movepo:[88,89],moveright:[69,70,77,78],moveup:[69,77,88,89],movi:74,mozart:40,mp3:40,ms:[32,37,39,47],msg:37,msmf:18,much:[18,24,26,28,31,54,56,58,59,60,61,62,63,65,67,68,71,73,74,84,85,87,89],multi_thread:53,multicolor:26,multidimension:65,multigestur:25,multimedia:[33,63,84],multipl:[1,6,18,19,22,23,24,25,26,28,32,33,36,38,41,44,45,50,51,56,57,61,63,64,65,69],multipli:[20,24,29,33,36,42,45,48,51,56,65],multisampl:23,multithread:27,music:[7,15,26,37,38,40,63,74,86],must:[1,2,3,17,18,19,20,22,23,24,25,27,28,29,30,31,32,35,36,37,38,41,42,43,44,45,46,47,49,50,51,52,53,56,58,59,60,63,64,65,68,72,84],mustlock:51,mutabl:25,mute:38,my:[26,61,62,63,65,84,86,89],my_appl:44,my_data_typ:46,mygroup:64,myscreen:[68,69,70,71,72,73,76,77,78,79,80,81],mysprit:64,mysurf:51,mytext:[68,69,70,71,72,73,76,77,78,79,80,81],mytextarea:[68,69,70,71,72,73,76,77,78,79,80,81],mytextfont:[68,69,70,71,72,73,76,77,78,79,80,81],n:[28,33,38,65],name:[1,9,18,19,20,23,25,26,28,29,32,33,36,37,44,45,47,50,51,53,58,59,61,62,63,64,65,66,68,70,78,86,88,89],name_forindex:47,name_of_environment_vari:44,namehint:[31,40],namespac:[34,58,60],nano:31,nasti:86,nativ:[15,18,22,44,57,84],natur:[40,65,74,84,86],navajowhit:21,navajowhite1:21,navajowhite2:21,navajowhite3:21,navajowhite4:21,navi:21,navyblu:21,ndigit:36,ndim:42,nearest:[24,38],nearli:[36,64,67],neat:84,neater:88,nebul:84,necessari:[9,23,25,27,29,38,51,60,61,64,68,84,85,87,88,89],necessarili:63,need:[10,15,18,19,20,22,23,25,26,27,28,31,32,33,35,42,44,45,46,50,51,53,57,58,59,60,62,63,64,65,66,67,68,69,70,71,72,73,85,86,87,88,89],needless:[68,71,73,84],needn:[26,61,89],neg:[8,9,17,25,29,35,36,38,39,40,42,45,51,56,65,89],neglig:18,neither:[50,69,70],nest:51,network:[61,86],never:[29,32,36,38,39,40,51,54,57,63,65,84],new_height:26,new_lay:50,new_mask:35,new_width:26,newarrai:42,newbi:15,newcom:84,newer:[23,40],newest:68,newli:[20,35],newlin:28,newpo:[58,66,87,88,89],newrect:64,newtonian:63,next:[15,18,27,29,30,35,36,38,41,46,58,64,68,73,84,85,88,89],nice:[31,58,61,62,65,84,87],nirav:57,node:44,noevent:[25,27,38,40,84],nofram:23,nois:[32,35,57],nomin:29,non:[17,19,23,24,26,29,31,33,41,42,44,50,51,56,58,64,84,86],none:[17,18,19,20,23,24,25,26,27,28,29,30,31,32,33,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,54,56,58,60,65,66,85,86,87,89],nonesound:[58,66],nonetheless:26,nonetyp:[30,35,50],nonlinear:51,nonzero:[35,45],nor:[50,70],normal:[8,11,19,20,25,28,29,35,36,44,45,50,51,53,55,58,64,65,71,87,89],normalize_ip:36,north:22,northeast:22,northwest:22,nosubprocess:53,notabl:[23,40,87],notat:39,notdef:29,note1:35,note2:35,note:[18,19,20,23,24,25,26,27,29,31,32,33,35,36,37,38,39,40,42,44,45,47,51,54,56,57,58,59,60,62,64,66,84,86,87,89],note_off:37,note_on:37,noteworthi:63,noth:[19,23,24,30,32,50,58,59,63,64,84,85,86],notic:[18,36,45,62,68,69,70,72,84,87,88,89],notifi:64,notimplementederror:[31,40],novel:74,now:[23,24,26,27,28,29,32,33,35,36,37,38,43,44,46,56,57,58,61,62,63,64,65,67,68,69,70,71,72,73,74,84,85,87,89],nowadai:[63,84],nrp:57,nuanc:84,num:[33,72,73,80,81],num_button:39,num_devic:37,num_ev:37,num_fing:25,num_threshold_pixel:56,num_track:19,number:[1,7,10,17,18,19,20,23,24,25,26,28,29,30,31,32,33,35,36,37,38,40,42,44,47,49,50,51,52,53,54,55,56,62,63,64,65,69,73,74,84],numbit:35,numer:[36,38],numev:[25,38],numfail:44,numlock:33,numpass:44,numpi:[1,15,26,31,42,43,49,51,52,63],o:[6,22,33,45,62],obj:[1,2,3,4,7,8,9,44,45],obj_list:45,object:[1,2,3,4,5,6,8,9,11,12,15,18,19,21,22,23,24,25,26,27,28,29,30,31,32,35,38,39,40,43,44,46,47,48,49,52,54,56,57,59,61,62,63,64,65,66,68,69,70,84,86,89],objectwithcallablerectattribut:45,objectwithrectattribut:45,objectwithsomrectattribut:45,obliqu:29,obscur:[61,84],obsolet:[23,44,51],obtain:[29,37,51],obviou:61,obvious:[64,85],occasion:64,occupi:85,occur:[25,29,33,37,38,42,62,68],octob:63,odd:[24,65,89],ofcod:89,off:[18,24,25,26,29,32,37,38,40,45,56,58,61,62,64,84,89],offcourt:89,offer:[22,23,25,47,57],offici:[62,84],offset:[17,24,26,28,29,30,35,37,40,42,45,50,51,58],often:[19,31,36,51,61,63,65,69,84,86],ogg:[38,40],oh:89,ok:[65,84,85,87],okai:[69,72],old:[23,39,44,50,51,54,58,62,64,74,84,88],older:[23,25,27,32,40,44,64,84],oldest:68,oldlac:21,oliv:21,olivedrab1:21,olivedrab2:21,olivedrab3:21,olivedrab4:21,olivedrab:21,omit:[29,37,42,53],onc:[15,18,19,23,25,28,29,31,32,33,35,37,38,40,42,44,47,51,53,54,58,60,62,63,64,65,84,85,86,87,89],one:[15,17,18,20,22,23,24,25,26,27,28,29,30,31,32,33,35,36,38,39,40,42,43,44,45,46,47,49,50,51,52,54,56,57,58,59,60,61,62,63,64,65,67,68,69,70,71,73,84,85,86,87,88,89],ones:[29,40,51,60,64,87,88],onli:[17,18,19,20,23,24,25,28,29,31,32,34,35,36,37,38,39,40,42,43,44,45,46,47,48,49,50,51,52,53,54,56,57,58,59,60,61,62,63,64,65,67,68,72,73,74,84,85,86,87,89],onlin:84,ons:63,onscreen:23,onto:[11,20,27,28,29,30,35,36,50,51,58,62,63,65,68,84,85,87],opac:[48,84],opaqu:[20,26,29,35,43,48,51,52,65,84],open:[1,6,9,15,18,19,23,32,33,37,44,46,47,57,58,63,85,86],opencv:[18,44,57],opengl:[23,26],opentyp:29,oper:[14,20,22,23,24,25,27,33,36,42,44,45,48,50,51,52,56,64,65,84,85],operand:65,oppos:29,opposit:25,optim:[23,26,28,51,56,64,65,84],optimis:84,option:[1,17,18,20,23,24,26,28,29,30,32,35,37,38,39,40,43,44,50,51,53,54,56,57,58,60,62,65],orang:21,orange1:21,orange2:21,orange3:21,orange4:21,orangered1:21,orangered2:21,orangered3:21,orangered4:21,orchid1:21,orchid2:21,orchid3:21,orchid4:21,orchid:21,order:[1,18,23,28,29,33,37,38,40,42,44,50,51,53,54,58,62,65,68,84,89],orderedupd:50,ordinari:64,org:[39,58,59,60,62,63,64,65,70,78,86,89],organ:[15,50,64,84],organis:61,organiz:84,orient:[29,35,62],origin:[20,22,23,24,26,29,31,32,33,35,42,45,47,48,50,51,56,58,62,63,64,65,66],original_color:56,original_dest_color:56,orthogon:35,os:[9,18,23,25,26,31,32,37,44,46,58,66,84,86,89],oss:37,osx:[23,63],other:[0,15,18,19,20,23,24,25,26,27,28,29,30,31,32,33,35,36,38,40,42,43,44,45,46,48,49,50,51,52,53,54,56,57,58,61,62,63,64,68,70,71,84,85,86,87,88,89],otherarrai:42,othermask:35,othersurfac:35,otherwis:[1,2,5,9,12,17,18,22,23,24,25,26,28,29,31,35,36,37,38,39,43,45,46,50,51,52,56,62,72,73,89],ouput:[68,76],our:[58,62,63,65,66,74,85],out:[15,18,24,29,32,35,36,37,38,40,44,57,58,59,61,62,64,65,84,85,86,87,88,89],outdat:67,outer:72,outgo:37,outlin:[24,29,35,57],outpng:26,output:[18,25,26,28,32,35,56,67,69,70,73,74,84,88],outputimag:26,outsid:[24,28,30,33,35,36,39,45,51,56,58,62,63,68],over:[22,23,25,29,31,33,35,36,38,40,42,44,47,50,57,60,62,63,64,65,66,84,86],overal:65,overboard:86,overcom:67,overflow:65,overhead:[51,64],overlai:64,overlap:[35,45,50,62,64,84,87,89],overlap_area:35,overlap_mask:35,overlin:29,overrid:[18,29,35,44,50,51,63,84],overridden:[23,29,35,38,45],overshoot:36,overwrit:[17,24,26,32,47],overwritten:[24,32,47,51],own:[12,16,22,25,26,27,37,38,46,48,59,61,63,65,84,86],own_data_typ:46,owner:[11,12],ownership:46,p1:48,p2:48,p:[31,33,43,62],pac:64,pack:[23,51,52,59],packag:[15,26,28,29,30,31,49,52,58,60,63,65],pacman:64,pad:[29,32,56],page:[33,39,46,65],pai:70,painless:86,paint:[50,85],pair:[12,23,24,28,29,31,32,45,51],pajitnov:74,palegoldenrod:21,palegreen1:21,palegreen2:21,palegreen3:21,palegreen4:21,palegreen:21,palett:[23,28,31,35,51,52,56],palette_color:[35,56],paleturquois:21,paleturquoise1:21,paleturquoise2:21,paleturquoise3:21,paleturquoise4:21,palevioletr:21,palevioletred1:21,palevioletred2:21,palevioletred3:21,palevioletred4:21,papayawhip:21,paper:84,paradigm:84,param:24,paramat:33,paramet:[18,20,23,24,25,26,29,30,31,33,35,36,37,38,39,40,44,45,46,51,55,56,57,70,71],parametr:36,parent:[2,12,17,48,50,51],parenthesi:33,pars:[84,87],part:[23,24,25,29,30,35,51,53,56,62,63,64,65,68,69,70,72,86],parti:[18,63],partial:[23,24,37,51,84],particular:[18,29,40,42,49,50,53,54,59,84],particularli:37,pass:[8,9,14,17,18,19,20,22,23,24,25,26,28,29,31,32,33,35,36,37,38,39,40,41,42,44,46,50,51,53,54,56,58,62,63,64,66,84,86],past:[24,36,46,58,63,84],patch:[13,26,28,31,38,44,63],patel:57,path:[6,9,15,25,26,28,29,31,36,38,44,53,57,58,66,84,86,89],pathlib:[28,29,31,38],pathlib_path:38,pathnam:[38,58,86],pattern:[29,40],paus:[19,26,33,38,40,54,86],pbm:[31,46],pc:[17,37],pcf:29,pci:65,pcx:31,peachpuff1:21,peachpuff2:21,peachpuff3:21,peachpuff4:21,peachpuff:21,peek:25,pellet:64,penalti:[51,59,64],pend:37,peopl:[15,31,58,61,62,63],per:[15,24,25,29,30,31,32,35,49,50,51,52,53,54,58,59,62,63,65,69,77,84,88,89],percentag:36,perfect:35,perform:[11,23,25,26,28,29,30,35,36,38,42,50,51,52,56,57,59,64,65,84,85,88],perhap:[62,64,84,86],period:[33,40,84],permiss:23,permit:[14,18,29],person:[62,63,88],perspect:[26,63],peru:21,pete:[58,59,60,62,63,64,65,84],pfr:29,pg:[22,58,66],pg_buffer:[1,2],pg_encodefilepath:9,pg_encodestr:9,pg_floatfromobj:1,pg_floatfromobjindex:1,pg_getdefaultwindow:1,pg_getdefaultwindowsurfac:1,pg_intfromobj:1,pg_intfromobjindex:1,pg_major_vers:13,pg_minor_vers:13,pg_mod_autoinit:1,pg_mod_autoquit:1,pg_patch_vers:13,pg_registerquit:1,pg_rgbafromobj:[1,10],pg_setdefaultwindow:1,pg_setdefaultwindowsurfac:1,pg_twofloatsfromobj:1,pg_twointsfromobj:1,pg_uintfromobj:1,pg_uintfromobjindex:1,pg_version_atleast:13,pg_versionnum:13,pg_view_p:1,pgbuffer_asarrayinterfac:1,pgbuffer_asarraystruct:1,pgbuffer_releas:1,pgbufproxy_check:2,pgbufproxy_getpar:2,pgbufproxy_new:2,pgbufproxy_trip:2,pgbufproxy_typ:2,pgchannel_asint:7,pgchannel_check:7,pgchannel_new:7,pgchannel_typ:7,pgchannelobject:7,pgcolor_check:3,pgcolor_new:3,pgcolor_newlength:3,pgcolor_typ:3,pgdict_asbuff:1,pgevent_check:5,pgevent_filluserev:5,pgevent_new2:5,pgevent_new:5,pgevent_typ:5,pgeventobject:5,pgexc_buffererror:1,pgexc_sdlerror:1,pgfont_check:6,pgfont_is_al:6,pgfont_new:6,pgfont_typ:6,pgfontobject:6,pglifetimelock_check:12,pglifetimelock_typ:12,pglifetimelockobject:12,pgm:31,pgobject_getbuff:1,pgrect_asrect:8,pgrect_check:8,pgrect_fromobject:8,pgrect_new4:8,pgrect_new:8,pgrect_norm:8,pgrect_typ:8,pgrectobject:8,pgrwops_fromfileobject:9,pgrwops_fromobject:9,pgrwops_isfileobject:9,pgrwops_releaseobject:9,pgsound_aschunk:7,pgsound_check:7,pgsound_new:7,pgsound_typ:7,pgsoundobject:7,pgsurface_assurfac:11,pgsurface_blit:11,pgsurface_check:11,pgsurface_lock:12,pgsurface_lockbi:12,pgsurface_locklifetim:12,pgsurface_new2:11,pgsurface_new:11,pgsurface_prep:12,pgsurface_typ:11,pgsurface_unlock:12,pgsurface_unlockbi:12,pgsurface_unprep:12,pgsurfaceobject:[1,11,12],pgvidinfo_asvidinfo:4,pgvidinfo_check:4,pgvidinfo_new:4,pgvidinfo_typ:4,pgvidinfoobject:4,phase:[69,70],phi:36,photo:44,photograph:56,photoshop:16,physic:[15,17,23,25,32,61,63,86,88,89],pi:[24,89],pick:[23,44,56,59,62,64],pictur:[31,63],pie:30,piec:[29,84],pil:31,pile:26,pinch:25,pink1:21,pink2:21,pink3:21,pink4:21,pink:21,pip3:15,pip:15,pipe:23,pitch:[37,51,62],pitch_bend:37,pixel2d:65,pixel3d:65,pixel:[15,17,18,20,22,23,24,26,28,29,30,31,35,41,45,48,51,56,58,59,63,65,85,88,89],pixel_arrai:42,pixelarrai:[15,26,42,51],pixelcopi:[26,42,43,52],pixelformat:18,pixels2d:[52,65],pixels3d:[52,65],pixels_alpha:[52,65],pixels_blu:52,pixels_green:52,pixels_r:52,pixels_within_threshold:56,place:[1,23,25,27,29,34,36,37,39,42,44,45,46,50,51,52,53,58,62,63,64,65,68,84,87,88],placehold:44,placement:44,plai:[7,15,19,23,26,32,36,37,40,47,58,62,63,64,66,70,74,84,86],plain:46,plan:[51,63],plane:[41,51],plant:65,plateau:84,platform:[15,18,23,24,25,30,33,35,37,38,41,44,46,51,53,54,57,58,59,63,84,86,88],playabl:49,playback:[19,26,38,40,63],player1:[61,89],player2:89,player:[26,50,58,61,62,64,68,70,73,74,84,88,89],player_po:15,playerimag:62,playerpo:62,playersprit:89,playmu:26,pleas:[16,23,28,33,36,44,45,51,56,84],plenti:[61,84],plot:61,plu:[28,29,33,37,44,64],plug:[25,32],plum1:21,plum2:21,plum3:21,plum4:21,plum:21,pm_recommended_input_devic:37,pm_recommended_output_devic:37,pmdeviceid:37,png:[16,26,31,58,63,65,66,67,68,69,70,75,76,77,78,84,86,87,88,89],pnm:31,po:[25,27,35,40,50,58,62,66,72,73,80,81],point:[14,17,19,20,22,24,26,29,30,35,36,37,45,48,50,56,57,62,63,64,65,67,72,84,89],pointer:[8,9,11,58],polar:36,polish:[22,63],poll:[15,25,27,37,84],polygon:[24,30],pong:[61,86,89],poor:61,poorli:[61,84],pop:44,popul:9,popular:[26,32,66],port:[18,26,37,44,48],portabl:[33,37,63],portion:[11,23,45,48,51,56,63,64,84],portmidi:37,posit:[1,8,17,19,22,23,24,25,26,28,29,30,32,33,35,38,39,40,44,45,48,50,51,53,55,57,58,62,63,64,66,68,69,84,85,87,88,89],possibl:[18,23,25,28,29,31,32,36,44,50,51,57,58,61,63,65,67,68],post:[25,27,38,47,54],potenti:[31,64,84,88],powderblu:21,power:[18,32,33,38,64,74,84],power_level:32,ppem:29,ppm:[31,46],pr:39,practic:[84,86],pre:[20,29,51],pre_init:38,prealloc:51,prebuilt:15,preced:[35,68],precis:[23,38,51,65,84],precise_i:25,precise_x:25,precisei:25,precisex:25,predecessor:[65,84],predefin:[25,46],predomin:43,prefer:[23,25,30,31,33],prefix:58,prematur:84,premul_alpha:[20,51],prepar:[26,40,66],present:[44,48,50,60,62,63,64,84],preserv:[20,44,45,51],preset:[22,38],press:[25,26,32,33,39,47,57,58,62,70,71,84],pressur:55,pretend:62,pretti:[59,62,64,65,84,85,88,89],prettier:33,prevar:84,prevent:[25,84,89],prevent_display_stretch:23,previou:[1,23,29,38,39,48,50,54,56,58,62,64,65,69,70,72,73,84,85,89],previous:[18,20,25,28,29,31,38,40,44,59,62,85],primari:39,primarili:[25,29,44,48],prime:48,primer:65,primit:30,principl:[88,89],print:[25,28,32,33,36,39,44,45,46,57,58,59,62,66,68,70,71,72,73,76,84,86,89],printboard:[73,81],printf:[67,75],prior:23,prioriti:18,privat:[51,58],probabl:[26,36,62,64,68,84,85],problem:[28,43,44,58,61,69,84,86],proce:84,procedur:[67,68],process:[18,25,26,27,32,46,52,53,54,60,62,67,68,70,72,73,77,84],processor:[54,56],procur:18,produc:[29,51,84],product:[36,61],profil:[23,25,84],profit:18,program:[15,19,23,25,27,32,33,37,38,41,44,45,53,54,58,61,62,63,64,65,66,67,68,69,70,71,73,74,84,85,86,87,88],programm:[63,64,84,86,87],programmat:44,progress:43,project:[16,26,36,61,63,67,68,69,70,71,72,73,76,77,78,79,80,81,84,86,87,89],prolif:84,prolog:73,promis:[58,62],prompt:[44,63,65],proper:[29,33,38,39,64,65],properli:[9,15,25,39,53,58,62,64,65,84],properti:[17,25,29,39,50,52],propos:63,protect:[26,58],protocol:[2,15,31,43,51],proud:62,provid:[15,18,20,23,26,27,28,29,33,35,36,38,41,42,44,45,50,51,53,54,56,57,58,67,86,87],proxi:[2,17],ps4:32,ps:[26,32],pseudo:87,pt:56,pull:[18,58,66],pummel:[58,66],pump:[25,27,32,88,89],punch:[58,63,66],punch_sound:[58,66],punchabl:58,punctuat:28,pure:[44,51],purpl:[15,21,84],purple1:21,purple2:21,purple3:21,purple4:21,purpos:[1,18,48,57,61],push:[33,71,88],pushabl:32,put:[15,16,32,46,54,60,61,63,64,66,68,85,86,87],puyopuyo:[67,75],pxarrai:42,py:[15,23,26,44,46,53,56,61,62,65],py_buff:[1,17],pybuf:1,pybuffer_releaseproc:1,pycdio:19,pygam:[10,14,21,68,69,70,71,72,73,74,75,76,77,78,79,80,81,86,87,89],pygame2:25,pygame_blend_add:11,pygame_blend_alpha_sdl2:[11,44],pygame_blend_max:11,pygame_blend_min:11,pygame_blend_mult:11,pygame_blend_premultipli:11,pygame_blend_rgba_add:11,pygame_blend_rgba_max:11,pygame_blend_rgba_min:11,pygame_blend_rgba_mult:11,pygame_blend_rgba_sub:11,pygame_blend_sub:11,pygame_bufferproxi:2,pygame_camera:44,pygame_displai:44,pygame_force_scal:44,pygame_freetyp:[6,28,44],pygame_hide_support_prompt:44,pygame_lofi:16,pygame_logo:16,pygame_mix:7,pygame_pow:16,pygame_powered_lowr:16,pygame_tini:16,pygameapi_base_numslot:10,pygamevers:44,pyobject:[1,2,3,4,5,6,7,8,9,11,12],pyopengl:[26,31,63],pypi:42,pyportmidi:37,pysdl:[63,84],pythagorean:[36,69],python26:26,python2:26,python3:18,python:[1,2,3,4,5,6,7,8,9,11,12,14,15,17,18,19,22,26,28,29,31,38,43,44,45,50,51,53,58,60,61,62,64,66,67,68,85,86,87],pytypeobject:[2,3,4,7,8,11,12],pyunicode_asencodedstr:[9,44],pyzin:63,q:33,qce:18,quadrant:24,quadruplet:[24,30],quake3:63,qualiti:[26,44,58,63],quaternion:67,queri:[23,37,46,59],query_imag:[18,57],question:[15,33,62,84],queu:[25,38,40],queue:[5,18,23,32,33,34,37,38,39,40,47,54,58,63,84,88],quick:[25,26,32,51,53,60,64,65,88],quicker:[50,51,56,64],quickest:51,quickli:[31,58,59,62,63,64,65],quietli:25,quit:[1,15,19,22,23,24,25,26,28,29,32,37,38,39,44,47,51,53,57,58,62,63,65,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84,85,88,89],quiz:73,quot:33,quotedbl:33,r:[8,20,24,26,30,32,33,36,42,43,45,51,56,64,65,71,72,73,79,80,81],r_margin:[72,73,80,81],radial:[36,65],radian:[24,30,36,87,89],radii:[24,30],radiu:[24,30,50],radom:74,rais:[1,2,3,4,5,6,7,8,9,14,17,20,22,23,24,25,26,28,29,30,31,33,35,36,37,38,40,43,44,45,46,48,49,50,51,52,56,57,60,65,84,86,89],ramp:23,ran:85,rand:89,randint:[73,81,89],random:[53,61,73,74,81,86,89],randomli:73,rang:[20,23,24,25,28,29,32,33,35,36,37,38,42,44,47,50,57,62,63,65,68,71,72,73,79,80,81],rank:84,rapid:32,rapidli:[33,62],rare:[36,44,51,84],rate:[38,40,50,69,84],rather:[24,28,29,30,33,44,50,54,56,69,85,87],ratio:[45,50],raw:[17,31,42,43,51,52,65],rb:46,re:[22,26,29,36,44,56,59,61,62,64,71,84,85,87,88],reach:[63,84],read:[9,17,20,25,26,29,37,39,46,48,50,51,61,62,65,84],readabl:[20,38,68,86],readi:[18,22,32,48,53,57,58,62,63],readlin:22,readm:15,readonli:50,real:[25,28,29,37,38,51,57,59,62,64,70,84],realist:[86,89],realiti:[59,84],realiz:[63,84],realli:[23,41,56,58,59,62,63,64,65,70,85,87,88],realtim:[51,63,65],reason:[23,27,28,29,38,39,51,56,57,61,62,64,84,87],rebel:63,rebind:47,recalcul:29,recap:61,receiv:[23,27,32,33,39,47,84],recent:[58,63,64,65],recogn:[17,25,29,32,43,45,56],recogniz:31,recommend:[25,27,28,31,50,51,62,84],recommended_input_devic:37,recommended_output_devic:37,recompil:89,reconstruct:51,record:[18,29,52,53,72,84],recreat:[26,50,53],rect1:45,rect2:45,rect:[0,15,24,29,30,33,35,41,45,48,50,51,56,57,58,62,63,64,66,71,72,73,79,80,81,86,87,88,89],rect_area_pt:56,rect_list:[45,50],rect_sequ:45,rectangl:[8,15,23,24,26,28,29,30,33,45,48,50,51,56,64,68,84,85,87,89],rectangle_list:23,rectangular:[8,29,30,35,42,48,50,51,62,63,71,84],rectstyl:84,red1:21,red2:21,red3:21,red4:21,red:[1,15,18,20,21,23,24,31,42,50,51,52,56,65,68,69,70,71,72,73,76,77,78,79,80,81,85],redimg:65,redistribut:18,redraw:[41,62],redrawn:[23,41],reduc:[29,38,51,64,88],reentrant:53,ref:[70,78],refcount:9,refer:[1,12,17,23,24,31,33,35,42,46,47,49,50,51,52,57,58,59,62,63,64,65,68,69,70,71,72,73,84,88],referenc:[50,52,58,64,65],reflect:36,reflect_ip:36,refresh:84,regard:[24,68],regardless:[28,44],region:[31,51,56,84,86],regist:[1,44,46,87],register_quit:44,registri:37,regular:[15,23,40,41,50,51,56,64],regularli:32,reinit:[88,89],reiniti:38,reinitialis:32,rel:[25,32,37,39,40,41,48,50,61,86],relat:[1,23,25,29,34,39,43,47,50,84],relationship:50,relative_mous:48,releas:[1,12,17,18,23,25,29,30,32,33,37,39,42,44,46,51,58,63,84,86,88,89],release_buff:1,relev:[29,84],reli:[8,33,88],reliabl:[18,23,37],remain:[20,40,45,51,52,85],remap:[42,47],rememb:[25,39,50,51,59,61,63,64,65,68,73,84,86,87],remind:68,remov:[12,17,18,25,27,28,32,36,48,50,56,58,62,64,89],remove_intern:64,remove_sprites_of_lay:50,renam:47,render:[15,23,24,26,32,41,48,50,58,63,66,68,69,70,71,72,73,76,77,78,79,80,81,84,85,87,88],render_device_reset:25,render_raw:29,render_raw_to:29,render_targets_reset:25,render_to:29,renderclear:[50,64],renderplain:[50,58,64,66,89],renderupd:[26,50,64,84],renderupdatesdraw:64,repaint:[25,27,50],repaint_rect:50,repcolor:42,repeat:[23,33,35,38,40,44,61,62],repeatedli:[50,51,54,56,58,84],replac:[1,23,27,29,38,42,43,44,51,52,54,62,65,68],report:[26,32,44,56,84],repositori:[44,84],repr:44,repres:[1,11,12,13,15,17,19,20,22,23,24,25,29,31,32,33,35,36,38,39,40,41,44,45,47,48,52,54,56,58,62,63,64,65,84,87],represent:[1,3,8,15,21,24,30,42,56,58],reproduc:18,request:[1,23,26,28,29,35,38,49,51,52,59],requir:[17,18,23,25,26,28,33,35,36,37,38,44,45,46,47,50,51,55,56,59,61,62,63,64,65,68,70,84,85,86,88],resampl:38,rescal:26,resembl:[26,43,52],reserv:[25,37,38],reset:[29,32,37,38,40,50,58,89],resist:84,resiz:[15,23,25,26,35,45,48,56,58,85],resolut:[15,16,23,29,41,51,54,56,58,59,63],resolv:[46,68,87],resourc:[26,38,40,44,59,61,66,84,87],respect:[1,22,23,24,33,35,36,46,50,51],respond:[25,27,62,84],respons:[9,53],rest:[19,25,27,57,58,62,63,65,84],restart:[40,86],restor:[23,25,33,48],restrict:[28,51],result:[20,24,28,29,35,36,37,40,42,45,51,53,56,58,62,63,64,65,68,69,70,73,76,84,89],resultscreen:[69,70,77,78],resum:[19,38,40],retail:63,retain:[18,36,51],retrac:23,retriev:[13,36,38,46,84,88],reus:[18,86],reusabl:86,rev:44,revamp:62,revers:[1,51,58,63,65,89],revis:[44,61,63],reward:[58,63],rewind:40,rewound:26,rewrit:84,rgb:[18,20,23,24,28,30,31,41,43,51,57,58,63,65,84,85],rgba:[1,3,20,23,24,30,31,51],rgba_premult:31,rgbarrai:65,rgbx:31,rich:58,rid:57,ridicul:61,right:[20,23,24,25,29,30,33,35,38,42,44,45,47,50,51,58,62,63,64,65,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84,87,88,89],rins:62,rle:51,rleaccel:[51,58,66],rleaccelok:51,road:62,roll:[32,39,64],root:[36,53,84],rosybrown1:21,rosybrown2:21,rosybrown3:21,rosybrown4:21,rosybrown:21,rotat:[25,26,29,36,48,50,56,58,63,66],rotate_i:36,rotate_ip:36,rotate_ip_rad:36,rotate_rad:36,rotate_rad_ip:36,rotate_x:36,rotate_x_ip:36,rotate_x_ip_rad:36,rotate_x_rad:36,rotate_x_rad_ip:36,rotate_y_ip:36,rotate_y_ip_rad:36,rotate_y_rad:36,rotate_y_rad_ip:36,rotate_z:36,rotate_z_ip:36,rotate_z_ip_rad:36,rotate_z_rad:36,rotate_z_rad_ip:36,rotozoom:56,rough:84,round:[20,24,36,37,38,88],routin:[1,18,25,28,29,51,56,58,59,68,84],row1:65,row2:65,row:[35,42,51,65],royalblu:21,royalblue1:21,royalblue2:21,royalblue3:21,royalblue4:21,rr:20,rrggbb:20,rrggbbaa:20,rt:32,rudder:32,rudimentari:26,ruin:65,rule:[59,70,73,74,89],rumbl:[32,47],run:[15,23,25,26,31,32,38,44,53,54,56,58,59,62,63,64,65,66,67,84,85,89],run_speed_test:26,run_test:53,run_tests__test:53,rundown:26,runner:53,runtim:[23,38,54,56,63],runtimeerror:[29,44],rw:9,rwobject:0,rx:30,ry:30,s:[8,11,12,14,17,20,22,23,24,25,26,28,29,31,32,33,35,36,37,38,39,40,41,42,43,44,45,46,47,48,50,51,54,56,58,59,61,63,64,65,67,68,69,70,71,74,84,85,86,87,88,89],saddlebrown:21,safe:[9,19,23,25,28,29,32,37,38,42,44,47,50,51,57,60],sai:[51,61,62,64,68,84],said:[24,61,68,70,74,84,85],sake:[84,85],salmon1:21,salmon2:21,salmon3:21,salmon4:21,salmon:21,salt:84,sam:63,same:[9,18,19,20,22,23,24,25,28,29,30,31,32,33,35,36,38,39,42,43,44,45,47,50,51,52,54,56,57,58,59,62,63,64,65,67,68,70,71,72,73,84,85,89],sampl:[15,20,38,40,56,57,62,64,65,88],san:29,sandybrown:21,satisfactori:86,satisfi:23,satur:18,sauf:35,save:[15,18,31,61,63,84],save_extend:31,saw:[57,58,62],scalabl:29,scalar:[36,45,56,58],scale2x:56,scale:[18,23,24,26,29,31,35,36,44,45,48,50,56,58,63,65,66],scale_bi:[45,56],scale_by_ip:45,scale_to_length:36,scaledown:65,scalei:45,scaler:26,scaletest:26,scaleup:65,scalex:45,scan:19,scancod:[25,33],scanf:[67,75],scanlin:62,scant:26,scene:[31,57],school:39,scipi:65,scope:[42,65],score:[26,50,61,70,89],scoreboard:61,scoreup:61,scrap:[15,46],scrap_bmp:46,scrap_clipboard:[26,46],scrap_pbm:46,scrap_ppm:46,scrap_select:46,scrap_text:46,scratch:[64,84],scratchi:38,screen:[1,4,15,22,24,25,26,28,31,32,33,34,39,44,45,46,48,50,51,57,58,59,61,63,64,65,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84,85,87,88,89],screen_dim:26,screen_height:23,screen_rect:[45,50],screen_width:23,screensav:[23,44,69],screenshot:58,script:[28,29,60,63,66],scroll:[25,26,39,51,62,63,64,84],scrollabl:26,scrollock:33,sdl1:[19,23,41,51],sdl2:[25,38,39,44,47,51,55],sdl3:23,sdl:[1,5,7,9,11,12,23,25,26,27,32,38,41,43,44,47,48,56,59,63,84],sdl_appact:25,sdl_appinputfocu:25,sdl_appmousefocu:25,sdl_audiodriv:44,sdl_delai:54,sdl_event:5,sdl_gfx:30,sdl_hint_video_allow_screensav:23,sdl_imag:[31,63],sdl_joystick_allow_background_ev:[32,44],sdl_mixer:[38,40],sdl_mousewheel_flip:25,sdl_rect:[8,11],sdl_rwop:9,sdl_surfac:11,sdl_ttf:[28,29,44],sdl_video:51,sdl_video_allow_screensav:44,sdl_video_cent:44,sdl_video_window_po:44,sdl_video_x11_net_wm_bypass_compositor:44,sdl_videodriv:[23,44],sdl_videoinfo:4,sdl_window:1,sdl_windowid:23,sdlerror:40,sdlversion:44,sea:24,seagreen1:21,seagreen2:21,seagreen3:21,seagreen4:21,seagreen:21,search:[15,28,29,35,45,50,56],search_color:56,search_surf:56,seashel:21,seashell1:21,seashell2:21,seashell3:21,seashell4:21,second:[15,17,19,20,22,24,26,30,32,36,38,40,42,49,50,53,54,58,60,62,63,65,69,70,71,72,77,84,88,89],secondari:84,section:[15,19,51,58,61,62,64,65,67,68,86,89],secur:86,see:[9,16,18,19,20,23,24,26,28,29,30,31,32,33,35,36,37,38,39,43,44,45,47,50,51,52,56,57,58,60,61,62,63,64,65,84,85,87,88,89],seed:[53,65],seek:9,seem:[31,46,61,62,63,64,68,72,73,84,85,87],seemingli:61,seen:[23,61,62,65,88,89],segment:[24,30],select:[18,23,25,29,33,35,37,38,40,44,46,53,59,62,67,69,73,84],self:[20,32,35,36,45,50,51,56,57,58,62,64,66,87,88,89],sell:62,semi:[26,84],semicolon:33,semiton:37,send:[25,37,38,40,89],sens:[33,62,84,87,88,89],sent:[23,25,32,33,38,40],separ:[18,22,26,28,29,37,38,44,45,50,51,52,53,56,57,61,62,64,65,72,84,86],sequenc:[1,8,14,22,23,24,25,29,30,33,35,37,39,42,45,50,51,56,59,63,64,84,88],sequenti:24,seri:[37,63,70],serv:[27,63,84],server:[26,39],servic:18,session:53,set:[1,6,17,18,20,22,23,24,25,28,29,31,32,33,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,54,56,58,60,62,63,65,67,68,70,71,72,73,84,85,86,87,88,89],set_allow:25,set_allow_screensav:23,set_alpha:[43,51],set_at:[35,51,65,84],set_behavior:56,set_block:[25,84],set_bold:28,set_capt:[22,23,24,32,58,66,68,69,70,71,72,73,76,77,78,79,80,81,85,89],set_clip:[50,51],set_color:56,set_colorkei:[43,51,58,66,84],set_control:[18,57],set_cursor:[22,39],set_default_resolut:29,set_endev:[38,40],set_error:44,set_eventst:47,set_fullscreen:48,set_gamma:23,set_gamma_ramp:23,set_grab:[25,33,39],set_icon:[23,48],set_instru:37,set_ital:28,set_keyboard_grab:25,set_length:20,set_loc:41,set_map:47,set_mask:51,set_mod:[1,15,22,23,24,32,33,34,39,44,46,48,51,57,58,59,62,63,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84,85,89],set_modal_for:48,set_num_channel:38,set_palett:[23,51],set_palette_at:51,set_po:[39,40],set_repeat:33,set_reserv:38,set_script:28,set_shift:51,set_smoothscale_backend:56,set_strikethrough:28,set_text_input_rect:33,set_tim:54,set_timing_threshold:50,set_timing_treshold:50,set_underlin:28,set_viewport:48,set_vis:[39,58,66],set_volum:[38,40],set_window:48,setcolor:35,setsurfac:35,settabl:44,setup:[15,22,26,84],sever:[15,22,23,24,26,29,38,45,50,54,59,60,62,63,64,65,84,85,89],sf:42,sfnt:29,shade:24,shall:18,shallow:35,shape:[15,17,28,42,43,51,63,65,72,84],share:[15,25,28,31,32,33,38,46,48,51,52,54],sharp:24,she:85,shell:26,shift:[23,25,26,33,35,42,44,51,59,65],shinner:[58,59,60,62,63,64,65,84],shoot:63,shortcut:[25,64],shorter:[23,58,65],shortest:36,shot:64,should:[18,19,22,23,25,26,28,29,30,31,32,33,35,36,37,38,42,44,45,47,50,51,54,56,58,59,61,62,63,64,65,68,69,84,85,86,87,89],shoulder:47,shouldn:88,show:[15,22,23,26,29,33,39,41,48,53,56,57,58,59,62,65,84,88,89],show_output:53,showcas:[26,84],shown:[23,25,33,41,57,62,84,87],shrink:[45,84,89],shrinkag:56,shrunk:51,shut:[23,29,32,44,69],shutdown:[7,63],side:[23,24,42,45,47,58,61,63,87,88],sienna1:21,sienna2:21,sienna3:21,sienna4:21,sienna:21,sign:[17,33,37,38,39,44],signal:[25,40,85],signific:51,silenc:53,silent:[23,60],silver:21,simd:51,similar:[29,31,32,42,49,50,51,54,58,62,64,65,85,86,88,89],simpl:[15,22,23,24,25,26,32,42,50,51,56,57,58,60,61,62,63,64,65,66,67,69,71,72,73,74,84,85,86,89],simpler:[62,63,64],simplest:[57,71],simpli:[22,25,37,41,44,50,57,58,59,62,63,65,67,84,85,87,89],simul:[58,67],simultan:[38,67],sin:[87,89],sinc:[15,19,20,23,25,28,31,32,36,38,39,44,48,50,51,52,54,57,58,59,60,62,63,64,65,84],singl:[17,20,22,23,24,25,28,29,30,32,33,38,40,41,42,43,45,50,51,52,53,58,60,61,62,63,64,65,67,68,69,70,71,73,84,85],sit:84,site:26,situat:[23,36,51,58,64,65,84],six:[38,40,61,63],sizabl:84,size:[8,17,18,22,23,24,25,26,28,29,31,35,38,39,41,42,43,45,48,50,51,52,56,57,58,59,61,62,63,65,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84,85],sizeabl:23,sizeal:22,sizenesw:22,sizenws:22,sizeof:35,sizer_x_str:22,sizer_xy_str:22,sizer_y_str:22,skeleton:84,skew:28,skill:84,skip:[23,35,38,58],skyblu:21,skyblue1:21,skyblue2:21,skyblue3:21,skyblue4:21,sl:32,slash:[22,33],slateblu:21,slateblue1:21,slateblue2:21,slateblue3:21,slateblue4:21,slategrai:21,slategray1:21,slategray2:21,slategray3:21,slategray4:21,slategrei:21,sleep:[25,54],slerp:36,slice:[36,42,43,65,84],slight:[58,64],slightli:[15,20,25,26,36,39,40,51,54,58,64,84,85],slope:24,sloppi:26,slot:0,slow:[23,48,51,57,59,62,65,84,85],slower:[50,51,54,62,84],slowest:[44,51,85],small:[18,22,23,28,29,32,36,39,44,45,58,62,63,65,71,72,84,85,87],smaller:[18,23,35,38,45,51,71,72,84],smallest:[23,51,59],smart:[62,64],smooth:[28,58],smoother:62,smoothli:[56,62],smoothscal:[26,56],smoothscale_bi:56,sn9c101:18,snakeviz:84,snapshot:57,sndarrai:[15,26,38,49,63],snow1:21,snow2:21,snow3:21,snow4:21,snow:21,so:[10,17,18,20,22,23,24,26,29,30,31,32,33,36,38,42,43,44,45,50,51,52,56,58,59,61,63,64,65,67,68,69,70,71,72,74,84,85,86,87,88,89],socket:[86,89],soften:65,softwar:[15,18,23,30,37,41,50,51,58,63],solarwolf:63,solid:[24,28,30,50,51,52,56,65,84],solut:[84,85],solv:[28,38,61,67,84],some:[15,18,22,23,24,25,27,28,29,30,31,32,34,36,37,38,39,42,44,45,46,50,56,57,58,59,60,61,63,64,65,67,68,69,70,72,84,85,86,87,88,89],someimag:26,someth:[15,18,24,26,39,56,57,59,62,63,64,65,68,69,70,84,85],sometim:[22,23,27,44,64,84],somewhat:[26,64,84],somewher:[64,68],soni:32,sonix:18,soon:[40,44,64,88],sophist:[86,87],sorri:[57,59],sort:[23,25,26,27,50,58,59,62,64,84,89],sound:[7,15,26,40,58,61,63,64,66,67,70,73,84,86],sound_array_demo:26,sourc:[15,18,26,31,37,38,42,43,48,50,51,56,58,61,62,63,64,67,68,69,70,84,86,89],source_rect:50,sourcecod:[68,69,70,76,77,78],south:22,southeast:22,southwest:22,space:[20,28,29,31,33,37,42,62,84],sparingli:48,speak:[85,86],speaker:38,special:[18,22,23,25,33,35,38,43,50,51,56,58,59,62,64,65,68,84,85],special_flag:[48,50,51],specif:[23,25,28,29,33,35,36,38,43,46,50,51,52,57,58,59,62,64,65,68,70,71,72,84],specifi:[13,18,21,23,24,28,29,31,32,35,36,37,38,40,46,47,50,56,59,65,89],sped:24,speed:[25,26,29,44,54,62,63,64,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84,85,87,88,89],spend:84,spent:[63,74,84],spheric:36,spill:24,spin:[26,58,66,88,89],split:[22,29,58,63,64,66,84],sport:63,spot:84,spread:84,spring:56,springgreen1:21,springgreen2:21,springgreen3:21,springgreen4:21,springgreen:21,sprite1:50,sprite2:50,sprite:[15,26,35,36,50,61,66,84,88,89],sprite_dict:50,sprite_height:62,sprite_list:50,sprite_width:62,spritecollid:[50,64,87],spritecollideani:50,spritedict:64,sprites_click:84,sqrt:[36,69],squar:[24,32,36,72],squeez:84,sr:32,src:65,src_c:[0,10,13],srcalpha:[35,43,51,56],srccolorkei:51,srcobj:11,srcrect:[11,48],sse:[26,56],stabil:33,stabl:43,stack:25,stage:57,stai:[61,65,70],stand:26,standard:[1,22,25,27,41,43,44,46,50,57,58,59,61,63,64,65,68,84],star:26,starfield:26,start:[1,17,18,19,24,25,26,29,30,32,33,35,38,39,40,42,44,45,47,50,51,57,58,59,61,62,63,64,65,66,67,69,74,84,85,86,88,89],start_angl:[24,30],start_index:65,start_po:24,start_text_input:33,startup:[7,38],state:[23,25,26,29,31,32,33,37,38,39,46,47,48,50,51,58,64,66,67,84,88,89],statement:[68,69,70,71,72,84,89],stationari:64,statu:[26,32,37,70],stderr:53,stdin:65,stdout:53,steelblu:21,steelblue1:21,steelblue2:21,steelblue3:21,steelblue4:21,steep:24,stencil:23,step:[15,30,32,42,58,65,68,70,73,88],stereo:[23,38,49],stick:[32,47,62,65,84],still:[23,25,28,31,33,36,38,39,43,45,46,47,48,51,56,57,58,61,62,63,64,65,71,72,73,84,85,88,89],stop:[1,18,19,24,30,32,33,38,39,40,44,47,54,57,62,64,84,88,89],stop_angl:[24,30],stop_rumbl:[32,47],stop_text_input:33,store:[8,22,23,25,29,30,35,50,51,52,58,62,64,68],str:[17,18,20,28,29,32,33,35,37,44,47,49,52,71,72,73,79,80,81],straight:[24,30,51,62,63,65,86],straighten:62,straightforward:[62,63],strang:[23,63],stream:[15,37,38,48,63],strength:[29,32,47],stress:[63,84],stretch:[23,26,28,29],strict:[18,61,84],strictli:[30,85],stride:[17,42],strike:29,strikethrough:28,string:[9,14,17,18,19,20,22,23,25,28,29,31,32,33,37,38,41,44,46,48,49,51,52,53,56,68,84,86,87],strip:51,stripe:65,stroke:24,strong:29,strongli:[23,50],struct:[1,4,5,6,7,9,17,29,42,43,51,52],structur:[7,10,43,85,87,88],stuck:62,studi:[65,87],studio:63,stuff:[46,57,61,62,65],stump:84,style:[25,26,29,32,42,63,86],style_default:29,style_norm:29,style_obliqu:29,style_strong:29,style_underlin:29,style_wid:29,sub:[50,84],subarrai:42,subclass:[2,3,4,5,6,7,11,12,17,20,35,36,45,50,51],subdirectori:[26,58],subgroup:67,subject:[23,44],submask:35,submit:84,submodul:[44,53],subpackag:53,subprocess:53,subprocess_ignor:53,subscript:[36,42],subsect:62,subsequ:[23,57],subset:[22,23,84],substanti:84,substitut:[18,22],substr:37,subsubsurfac:51,subsurfac:[12,51,56],subtract:89,subview:42,succe:[18,23],succeed:18,success:[1,2,5,8,9,11,23,44,84],successfulli:[32,47],sudden:63,suffix:45,suggest:[42,58,84],suit:[27,51,59],suitabl:[23,28,29,46,63,68,86],sum:[24,74],summari:64,summer:63,superclass:36,superior:51,suppli:[18,35,37,38,57,58,65,85],support:[1,6,17,18,19,20,22,23,24,25,26,28,29,31,32,33,35,36,37,38,39,40,41,42,43,44,45,47,48,49,50,51,52,54,56,57,58,59,63,84,87],suppos:[56,84],supposedli:84,sure:[10,50,54,57,58,62,63,64,65,69,70,84,89],surf:[22,26,29,30,42,50,51,56],surfac:[0,1,2,10,12,15,18,20,22,23,24,26,28,29,30,31,35,36,39,41,43,46,48,50,51,57,58,59,62,63,64,66,85,89],surface_dest:50,surface_to_arrai:43,surfarrai:[15,17,26,43,49,51,52,63,84],surfdemo_show:65,surflock:0,surfobj:12,surpris:[62,63],surrog:[14,29],surround:[56,85],suspend:84,svg:[16,31],svgalib:23,swap:[23,31,42,45],swatch:20,swig:63,switch_lay:50,swizzl:36,swsurfac:51,sy:[14,44,53,62,63,67,68,69,70,71,72,73,75,76,77,78,79,80,81,86,89],symbol:33,symmetri:36,sync:[23,25],synchron:37,synonym:25,syntax:[65,84],syntaxerror:44,synthes:37,sysfont:[28,29],sysrq:33,system:[14,15,18,19,22,23,25,26,27,28,29,32,33,36,37,39,40,44,48,49,51,52,53,59,68,70,84,85,88],system_cursor_arrow:22,system_cursor_crosshair:22,system_cursor_hand:22,system_cursor_ibeam:22,system_cursor_no:22,system_cursor_sizeal:22,system_cursor_sizen:22,system_cursor_sizenesw:22,system_cursor_sizenws:22,system_cursor_sizew:22,system_cursor_wait:22,system_cursor_waitarrow:22,systemexit:[84,86,89],t:[9,18,19,23,24,26,27,28,29,32,33,36,40,44,46,48,49,50,51,52,54,56,57,58,61,62,63,64,65,68,69,70,71,72,73,74,85,86,87,88,89],ta:61,tab:[25,33],tabl:[23,43],tackl:84,tag:[40,53],taka:18,takafumi:18,take:[14,22,24,25,26,28,29,31,32,33,35,36,37,38,40,42,45,50,51,53,56,57,58,59,63,64,65,68,84,85,86,88,89],taken:[3,21,23,26,27,36,38,39,56,87],talk:62,tan1:21,tan2:21,tan3:21,tan4:21,tan:21,tango:29,tank:84,target:[29,36,42,43,45,48,50,58,64,66],target_textur:48,task:[59,86],tau:24,teach:62,teal:21,technic:41,techniqu:[50,84],tell:[9,38,39,44,51,58,59,62,64,84,88],temp:8,templat:84,temporari:[56,65],temporarili:[19,24,33,38,40,51,52],tempt:84,temptat:84,ten:54,tenni:[88,89],term:[62,64,85],termin:[1,25,37,44,58,62,68],terminolog:[39,84],terrain1:62,terrain2:62,test:[15,19,23,25,26,28,29,31,32,33,36,38,39,41,44,45,47,50,51,56,58,59,60,65],test_threshold_dest_surf_not_chang:56,test_util:56,testin:37,testout:37,testsprit:26,tetri:74,text:[25,26,28,29,32,33,46,66,68,69,70,71,84,85,87],text_bitmap:32,text_print:32,textedit:[25,33],textinput:[25,33],textmarker_str:22,textpo:[58,66,85],textprint:32,textur:[30,48],textured_polygon:30,textureorimag:48,tga:[31,63],than:[15,18,19,23,24,26,28,29,30,31,32,33,35,36,37,38,40,44,45,47,50,51,52,54,55,56,57,58,59,60,62,63,64,65,69,70,74,84,85,86,87,88,89],thank:88,the_arg:11,the_dirty_rectangl:84,thecorruptor:16,thei:[1,10,18,19,22,23,24,25,26,28,29,32,35,36,38,39,44,45,46,47,50,51,56,58,59,62,63,64,65,68,69,71,72,84,86,87,88,89],them:[15,16,22,24,25,26,27,28,31,32,33,34,35,38,42,44,45,49,50,51,52,57,58,59,62,63,65,71,84,85,86,87,88,89],themselv:[51,61,64,85],theorem:36,theori:[18,84],therefor:[24,30,35,36,50,62,64,84,87],theta:[35,36],thi:[1,2,3,4,5,6,8,9,10,11,12,15,17,18,19,20,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,72,73,74,84,85,86,87,88,89],thick:[24,28],thickarrow_str:22,thin:58,thing:[19,25,27,36,40,44,57,58,59,60,61,62,63,64,65,85,87],think:[18,23,36,62,64,65,70,74,84,89],third:[17,18,22,30,36,62,63,65,71,85],third_surfac:56,thirteen:15,thistl:21,thistle1:21,thistle2:21,thistle3:21,thistle4:21,thorough:89,those:[15,18,22,23,24,25,26,28,29,39,42,43,50,57,61,62,64,65,84,86,87,88,89],though:[18,23,30,37,38,43,53,61,65,84,85,86,87,88,89],thought:84,thousand:49,thread:[9,18,25,26,27,28,30,38,39,50,53,57],three:[32,36,38,44,51,52,64,65,68,84,85,88],threshold:[35,42,50,56],threshold_behavior_from_search_color:56,threshold_color:56,throttl:32,through:[2,18,25,27,28,29,31,35,37,38,44,50,51,58,61,62,63,64,74,84,85,88],throughout:34,thrown:[56,89],thru:63,thu:[36,42,87],thumbnail:26,ti:40,tick:[15,22,24,32,39,54,58,62,66,69,71,72,73,77,79,80,81,84,89],tick_busy_loop:54,tie:[57,72],tif:31,tiff:[31,46],tile:62,time:[13,15,18,19,22,23,24,25,26,28,29,32,34,37,38,39,40,48,49,50,51,53,56,57,58,59,61,63,64,65,66,67,68,69,70,71,72,73,74,77,78,79,80,81,84,85,86,87,89],time_m:50,time_out:53,time_proc:37,timeout:25,timer:[37,54],timer_resolut:[34,54],timestamp:37,tini:[23,84],tip:[15,64,84],titl:[23,48,58,63,68],tl:89,to_surfac:[35,48],tobyt:31,todo:64,togeth:[33,38,56,61,63,65,86],toggl:[22,47],toggle_fullscreen:23,toler:[32,36],tom:[86,89],tomato1:21,tomato2:21,tomato3:21,tomato4:21,tomato:21,tomchanc:[86,89],tompong:[61,89],tone:37,too:[25,27,29,36,42,45,50,51,53,58,62,65,67,68,69,73,84,85],took:63,tool:[26,64,67,84,87],toolkit:48,top:[15,23,24,26,28,29,31,35,38,39,45,50,51,62,63,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84,85,86,89],topic:[15,84],topleft:[35,42,45,50,56,58,62,66,89],toplevel:38,topmost:50,topright:[45,89],tort:18,tortur:84,tostr:31,total:[38,43,44,53,62,68,84,86],touch:[25,32,84],touch_id:25,touchid:55,toward:[23,25,36,61,63,84],tp:[67,75],tprint:32,tr:89,traceback:[65,86],track:[15,19,50,54,57,62,64,84],trackbal:[25,47],tradition:22,trail:[53,63],trait:[68,74],transfer:[15,46,56,61,65],transform:[15,18,29,57,58,62,63,65,66],transform_test:56,translat:[8,14,26,29,33,42],transluc:84,transmiss:37,transpar:[23,24,26,28,29,31,35,43,48,51,52,56,58,62,63,84,86],transpos:42,travel:89,treat:[23,29,32,52,65],tree:57,trend:63,tri:[44,62,63,84,86],tri_left:22,tri_right:22,trial:[74,84],triangl:[24,26,30,32],trick:[84,89],tricki:[64,65,84],trickier:65,trigger:[25,32,33,39,40,47,68,70],trigon:30,trigonometri:87,tripl:56,triplet:[23,24,30,58],troubl:84,truetyp:[15,28,58,63],truli:84,truncat:[24,30,38,45,65],truth:64,ttf:[28,29,68,69,70,71,72,73,76,77,78,79,80,81],tune:[29,64],tupl:[8,17,19,20,22,23,24,28,29,30,31,32,33,35,36,37,38,39,42,44,45,47,49,50,51,52,53,62,65,84],turn:[29,37,56,58,62,64,66,84],turquois:21,turquoise1:21,turquoise2:21,turquoise3:21,turquoise4:21,turtl:68,tutori:[26,61,63,66,73,74,84,85,89],tweak:64,twice:[50,51,56,65],twitch:84,two:[1,15,18,19,22,23,24,25,26,28,29,30,32,33,35,36,37,38,42,43,45,47,49,50,51,53,54,56,58,62,63,64,65,70,71,72,73,84,85,87],tx:30,ty:30,type1:29,type42:29,type:[1,2,3,4,5,6,7,8,9,11,12,14,15,17,20,22,23,24,25,27,28,29,30,31,32,33,34,35,37,38,39,40,41,42,44,45,46,47,49,50,51,52,54,56,57,58,59,62,63,65,66,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84,85,87,88,89],typeerror:[24,28,43,45,50,51],typelist:25,typestr:17,typic:[18,23,37,51,58],u00000001:28,u0001:28,u0010ffff:28,u4:17,u:[14,17,33,36,41],u_margin:[73,81],uc:[14,28,29],ucs4:[28,29],uffff:[14,28],ufo:63,ufunc:65,ui:[26,48],uint32:1,uint8:[1,3],uint:65,uk:[86,89],ultim:74,unabl:[31,46,64],unalt:[44,84],unari:20,unavail:[23,58],unchang:[14,20,23,29],uncommit:44,uncommon:58,uncompress:[31,38],undefin:[29,65],under:[15,37,39,40,62,63,86,89],underli:[28,29,32,37,44,84],underlin:[28,29,40],underline_adjust:29,underneath:65,underscor:[33,58],understand:[15,41,59,61,62,63,64,65,68,69,70,71,84,85,86,88,89],understood:71,undesir:24,unencod:44,unfamiliar:[62,84],unfil:[30,35],unfilt:56,unfortun:[53,84],unicod:[14,25,28,29,33,38,44,46,51],unicode_escap:44,unicodeencodeerror:[29,44],unicodeerror:28,unind:32,uniniti:[18,19,23,28,32,37,38,44,47],union:[45,64,84],union_ip:45,unional:45,unionall_ip:45,uniqu:[32,33,48,50,51,62,67,72,74],unit:[29,44,51],uniti:[67,75],unix:[22,23,63],unknown:[23,25,28,32],unless:[6,19,23,29,31,41,46,51,54,56,58,62,64,68,69,87,88],unlik:[15,38,43,51,65,88],unload:40,unlock:[24,51],unmap:43,unmap_rgb:[20,24,43,51,52],unmodifi:18,unnorm:67,unnot:33,unpack:[20,39],unpaus:[19,38,40],unplay:[23,63],unpleas:62,unpredict:29,unpunch:[58,66],unreal:[63,67,75],unrealist:89,unrecogn:[28,31],unrel:23,unscal:29,unset:[29,35,51,89],unsetcolor:35,unsetsurfac:35,unsign:[1,17,20,35,38,51,65],unspecifi:29,unstructur:51,unsupport:[18,40],until:[18,23,24,25,27,32,35,37,40,47,51,54,57,62,64,66,84,85,88],untransform:29,unus:[31,38,50],unwieldi:84,up:[15,16,18,22,23,24,25,27,28,32,33,37,38,39,40,42,44,47,50,51,58,60,62,64,65,71,72,73,80,81,84,85,86,87,88],updat:[11,15,20,23,24,25,32,36,41,44,45,47,48,50,54,59,60,61,62,63,64,66,68,69,70,71,72,73,76,77,78,79,80,81,84,85,87,88,89],update_rect:26,upon:89,upper:[25,29,32,51,57],upscal:16,us:[0,1,8,9,10,13,14,15,16,17,19,20,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,53,54,56,58,59,61,62,63,64,65,66,67,68,71,72,85,86,87,88,89],usabl:[20,31,57],usag:[23,27,37,51,53],usb:37,use_alpha:26,use_arraytyp:[49,52],use_bitmap_strik:29,use_compat:33,use_fastrendergroup:26,use_stat:26,use_valu:45,user:[5,15,18,23,24,25,27,28,33,37,44,46,51,53,58,61,63,64,65,67,72,84,85,87],userev:[25,38],usr:[26,28,66,85,86,87],usual:[23,25,28,29,32,38,44,50,51,58,59,60,62,63,64,65,70,86],utf8_str:46,utf:[14,28,29,46],util:[63,74,84],uxxxxxxxx:[14,29],uyvy_overlai:41,v1:44,v2:[1,40],v3:17,v4l2:[18,57],v:[17,20,33,36,41],val1:1,val2:1,val:[1,65],valid:[17,23,28,38,39,41,45,51,57,64],valu:[1,3,8,9,17,18,19,20,22,23,24,25,26,28,29,30,31,32,33,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,56,58,59,62,64,65,68,69,71,72,84,85,88,89],value_to_set:44,valueerror:[17,22,24,26,29,30,31,33,35,36,43,46,49,51,52,57,65],vari:71,variabl:[17,22,23,28,32,37,40,44,57,58,62,63,64,65,69,70,71,74,89],variant:22,variat:84,varieti:[25,63,70,84],variou:[1,15,22,25,26,34,36,89],vast:31,ve:[15,24,32,58,61,62,63,64,65,84,85,87,88,89],vec:36,vector2:[15,24,30,35,36,45],vector3:36,vector:[24,30,35,61,89],vectorelementwiseproxi:36,veloc:[37,69],ver:44,vera:[28,29],veri:[18,23,26,37,54,58,60,62,63,64,65,70,84,87,88,89],verifi:23,vernum:44,versatil:[47,56],version:[0,11,17,20,23,25,26,27,28,29,30,31,32,33,38,40,43,45,47,50,51,56,57,58,60,61,62,63,64,84,86,89],vertic:[18,23,24,25,26,29,30,31,39,42,56,58,62,65],vflip:[18,57],vgl:23,vgrade:[26,65],via:[18,23,25,29,35,57,84],vidcaptur:44,video0:[18,57],video:[15,23,25,26,32,44,48,51,57,59,64],video_mem:[23,59],videocaptur:[18,57],videoexpos:[23,25],videoinfo:[23,59],videores:[23,25],vidinfo:[23,59],view:[1,2,15,42,51],view_p:1,violet:21,violetr:21,violetred1:21,violetred2:21,violetred3:21,violetred4:21,virtual:[23,26,37,39,45],visibl:[17,22,23,39,41,50,58,59,62,63],vision:[15,18],visit:63,vista:23,visual:[70,71,72,73,84],visualis:87,vline:30,vm:25,vnc:25,volatil:51,volum:[15,38,40,63],vs:25,vsync:[23,48],w:[8,25,26,30,33,35,36,42,43,45,56],wa:[9,15,16,19,20,22,23,24,25,26,27,28,31,32,33,35,37,38,39,40,42,43,44,47,54,57,58,62,63,64,65,68,70,73,84,86,88],wai:[18,23,24,25,26,28,29,31,32,33,36,38,39,43,44,47,48,50,51,53,58,60,61,62,63,64,65,71,85,87,88,89],wait:[22,23,25,27,32,37,39,54,58,84],waitarrow:22,walk:[58,66],wall:[36,57,89],want:[9,18,20,23,25,27,31,32,33,36,44,50,51,54,56,57,58,59,61,62,63,64,65,67,68,84,85,86,87,88,89],warn:[44,48,58,66],warranti:18,warrior:63,was_init:29,wasn:63,wast:[84,87],watch:[15,57,61,85],wav:[38,40,46,58,66],wave:49,wavelength:[20,56],wayland:[22,23],we:[22,24,26,32,51,56,57,58,60,62,63,64,65,67,68,69,70,71,72,73,74,84,85,86,87,88,89],weak:[12,17,84],weakdirtysprit:50,weakli:50,weaksprit:50,web:[26,66],webcam:18,webp:31,websit:63,week:63,weight:[20,36,42,56,65],weird:33,welcom:[15,44],well:[1,2,12,22,24,27,30,31,33,39,42,44,47,51,56,61,62,63,64,65,84,89],were:[1,10,23,25,28,33,50,53,62,63,64,69,84,86,89],west:22,what:[15,20,22,23,24,25,26,28,32,38,39,44,46,50,57,58,59,60,61,63,64,65,68,69,71,74,85,86,88,89],whatev:[50,57,58,61,64,84],wheat1:21,wheat2:21,wheat3:21,wheat4:21,wheat:21,wheel:[25,39],when:[11,12,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,36,37,38,39,40,41,42,44,45,46,49,50,51,52,53,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,73,74,84,85,87,88,89],whenev:[23,25,38,39,44,65],where:[10,15,17,19,20,22,23,24,26,29,30,33,35,36,38,39,40,41,42,44,46,47,50,51,53,56,57,58,62,64,65,68,84,85,88,89],wherea:[23,45,87],wherev:[16,25,65],whether:[18,23,25,26,28,29,35,38,39,45,46,48,56,61,62,71,72,84,85],which:[1,12,14,15,17,18,20,22,23,24,25,26,28,29,31,32,33,35,36,37,38,39,40,42,44,45,46,47,49,50,51,52,53,54,56,58,59,60,61,62,63,64,65,67,68,69,70,71,72,73,74,85,86,87,88,89],whiff:[58,66],whiff_sound:[58,66],whilst:23,white:[21,22,24,32,35,42,57,58,68,69,70,71,72,73,76,77,78,79,80,81,85],whitesmok:21,whitespac:61,who:[15,48,61,62,64,86,89],whole:[23,24,37,44,62,84,87],whoop:62,whose:[24,55],why:[44,63,70,74,84,87],wide:[24,28,29,35,44,62,63],wider:[18,33],widget:26,width:[8,18,22,23,24,26,28,29,31,32,35,41,42,45,48,50,51,56,59,62,63,67,68,69,70,71,72,73,75,76,77,78,79,80,81,84],wiki:39,wikipedia:36,win32:37,win:[1,58,66,73],windib:23,window:[1,4,15,18,25,26,27,28,29,32,33,34,37,39,44,46,48,51,58,59,62,63,68,69,84,85,88],window_surfac:23,windowclos:25,windowdisplaychang:25,windowent:25,windowev:[23,25],windowevent_minim:23,windowexpos:25,windowfocusgain:25,windowfocuslost:25,windowhidden:25,windowhittest:25,windowiccprofchang:25,windowleav:25,windowmaxim:25,windowminim:25,windowmov:25,windowpos_cent:48,windowpos_undefin:48,windowres:25,windowrestor:25,windowshown:25,windowsizechang:25,windowtakefocu:25,wipe:15,wire:32,wireless:32,wisdom:65,wise:[29,65],wish:86,within:[12,17,22,29,33,35,36,44,50,51,56,62,68,74,84],without:[18,22,23,24,29,32,36,38,42,45,48,50,51,52,58,59,62,63,64,65,69,70,72,84,88,89],wm:[23,59],won:[40,50,58,61,62,64,65,84,86,87,88],wonder:[57,84],word:[28,29,46,54,62,64,88],word_wrap:29,work:[15,18,19,20,22,23,24,25,26,28,29,30,31,32,34,35,37,38,42,43,45,46,50,51,52,53,56,57,58,59,61,62,63,64,65,68,69,85,87,89],world:[57,68,69,70,74,76,77,78,84],worri:[26,48,50,64,65,74,84],wors:58,worst:61,worth:[51,54],would:[17,22,24,25,28,35,36,38,44,50,51,56,57,58,60,61,62,63,64,65,84,85,87,88,89],wow:68,wrap:[2,4,9,17,22,28,30,42,51,63],wrapper:[9,84],wrestl:26,writabl:17,write:[1,9,17,26,37,51,61,63,67,84,86,87],write_short:37,write_sys_ex:37,written:[15,24,28,60,61,63,64,84,86,87],wrong:[38,65,84],wrote:84,www:[70,78],x00:[14,28],x10:37,x11:[23,25,37,39,44,46,48],x12:37,x13:37,x1:[24,30,45],x2:[24,30,45],x3:[24,30],x7d:37,x86:56,x:[2,4,5,6,7,8,11,12,15,16,22,23,24,25,26,28,29,30,33,35,36,37,39,42,44,45,46,47,48,49,50,51,52,55,56,58,59,62,68,69,70,72,73,76,77,78,80,81,84,85,87,89],x_offset:35,x_scale:48,xbm:22,xbox:47,xf0:37,xf7:37,xfade:65,xor:22,xormask:[22,39],xpm:31,xx:22,xxx:22,xxxx:22,xxxx_test:53,xxxxx:22,xy:[36,89],y1:[24,30,45],y2:[24,30,45],y3:[24,30],y:[8,15,20,22,23,24,25,28,29,30,32,33,35,36,39,41,42,44,45,47,48,49,50,51,52,55,56,58,62,66,68,69,70,72,73,77,78,80,81,84,85,87,88,89],y_offset:35,y_scale:48,ye:[65,67,85],yeah:71,year:[63,74,84],yellow1:21,yellow2:21,yellow3:21,yellow4:21,yellow:21,yellowgreen:21,yet:[29,48,65,84,88],you:[9,10,15,16,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,36,37,38,39,40,41,42,44,46,50,51,54,56,57,58,59,60,61,63,64,65,67,68,71,72,74,85,86,87,88,89],your:[15,16,18,19,22,23,25,26,27,28,30,31,33,37,38,44,46,54,56,57,58,59,60,61,63,65,67,70,85,86,87,88,89],yourself:[28,50,58,63,65,86,87],yup:84,yuv:[18,41,57],yuy2_overlai:41,yv12_overlai:41,yvyu_overlai:41,z:[33,36,87,89],zero:[19,20,24,28,29,36,37,38,45,51,59,64,65,89],zip:[16,84],zl:32,zoom:26,zr:32},titles:["pygame C API","High level API exported by pygame.base","Class BufferProxy API exported by pygame.bufferproxy","Class Color API exported by pygame.color","API exported by pygame.display","API exported by pygame.event","API exported by pygame._freetype","API exported by pygame.mixer","Class Rect API exported by pygame.rect","API exported by pygame.rwobject","Slots and c_api - Making functions and data available from other modules","Class Surface API exported by pygame.surface","API exported by pygame.surflock","API exported by pygame.version","File Path Function Arguments","Pygame Front Page","Pygame Logos Page","pygame.BufferProxy","pygame.camera","pygame.cdrom","pygame.Color","Named Colors","pygame.cursors","pygame.display","pygame.draw","pygame.event","pygame.examples","pygame.fastevent","pygame.font","pygame.freetype","pygame.gfxdraw","pygame.image","pygame.joystick","pygame.key","pygame.locals","pygame.mask","pygame.math","pygame.midi","pygame.mixer","pygame.mouse","pygame.mixer.music","pygame.Overlay","pygame.PixelArray","pygame.pixelcopy","pygame","pygame.Rect","pygame.scrap","pygame._sdl2.controller","pygame.sdl2_video","pygame.sndarray","pygame.sprite","pygame.Surface","pygame.surfarray","pygame.tests","pygame.time","pygame._sdl2.touch","pygame.transform","Pygame Tutorials - Camera Module Introduction","Pygame Tutorials - Line By Line Chimp Example","Pygame Tutorials - Setting Display Modes","Pygame Tutorials - Import and Initialize","Making Games With Pygame","Pygame Tutorials - Help! How Do I Move An Image?","Pygame Intro","Pygame Tutorials - Sprite Module Introduction","Pygame Tutorials - Surfarray Introduction","pygame/examples/chimp.py","Author: Youngwook Kim (Korean)","Author: Youngwook Kim (Korean)","Author: Youngwook Kim (Korean)","Author: Youngwook Kim (Korean)","Author: Youngwook Kim (Korean)","Author: Youngwook Kim (Korean)","Author: Youngwook Kim (Korean)","Author: Youngwook Kim (Korean)","Author: Youngwook Kim (Korean)","Author: Youngwook Kim (Korean)","Author: Youngwook Kim (Korean)","Author: Youngwook Kim (Korean)","Author: Youngwook Kim (Korean)","Author: Youngwook Kim (Korean)","Author: Youngwook Kim (Korean)","Author: Youngwook Kim (Korean)","\ud55c\uad6d\uc5b4 \ud29c\ud1a0\ub9ac\uc5bc","A Newbie Guide to pygame","Revision: Pygame fundamentals","Kicking things off","Game object classes","User-controllable objects","Putting it all together"],titleterms:{"1":[32,61,85,86,87,88,89],"2":[32,62,85,86,87,89],"3":[85,86,88,89],"360":32,"4":[32,85,87],"5":[32,85,88],"6":89,"\uadf8\ub9ac\uace0":[79,80,81],"\uae30\ubc18\uacfc":76,"\uae30\ubc18\uc73c\ub85c\uc758":76,"\uae30\ubcf8":76,"\uae30\ucd08":[76,77,78],"\ub354":81,"\ubc84\ud2bc":80,"\uc0c8\ub85c\uc6b4":78,"\uc2ec\ud654":[79,80],"\uc5d0\ud544\ub85c\uadf8":82,"\uc65c":75,"\uc6c0\uc9c1\uc774\uae30":77,"\uc704\ud55c":77,"\uc774\ubca4\ud2b8":[76,78],"\uc785\ub825":78,"\uc785\ub825\uc740":78,"\uc785\ubb38":76,"\uc870\uac74":77,"\uc870\uae08":81,"\ucc98\ub9ac":[77,79],"\ucd9c\ub825":[76,80],"\ud29c\ud1a0\ub9ac\uc5bc":83,"\ud30c\uc774\uac8c\uc784":75,"\ud504\ub864\ub85c\uadf8":75,"\ud558\ud544":75,"\ud55c\uad6d\uc5b4":83,"\ud568\uc218\ud654":79,"\ud615\uc2dd\uacfc":76,"\ud654\uba74\uc774":77,"class":[2,3,8,11,36,50,58,64,87,88],"do":[62,84],"export":[1,2,3,4,5,6,7,8,9,11,12,13,17],"function":[10,14,59,62,65,71,86],"import":[57,58,60,65],"new":70,"switch":32,"while":58,A:[61,62,84,87,88],AND:63,Be:84,By:58,Into:68,It:62,NO:84,On:[58,62],One:62,The:[58,62,64,85,86,89],There:84,To:62,With:61,_freetyp:6,_sdl2:[47,55],access:[42,49,52],ad:62,advanc:[64,65,71,72],advic:84,all:[58,62,89],alpha:[73,84],an:[17,62],anim:69,api:[0,1,2,3,4,5,6,7,8,9,11,12,13],ar:[62,84],architectur:84,argument:14,arrai:[17,43,52],audio:[19,40],author:[67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82],avail:10,axi:32,back:62,background:[58,62],ball:[87,89],base:1,basic:[50,57,59,68,69,70,85],bat:[88,89],blit:[62,85],bother:84,buffer:17,bufferproxi:2,bufferproxypygam:17,button:72,c:[0,1,2,3,4,5,6,7,8,9,11,12],c_api:10,camera:[18,57],camerapygam:18,captur:57,cdrom:19,cdrompygam:19,center:58,chang:62,chimp:[58,66],clipboard:46,close:63,code:61,collis:[64,84],color:[3,20,21],colorkei:84,colorpygam:20,colorspac:57,com:[67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82],comfort:84,common:[32,64],comput:[29,57],con:32,connect:57,constant:34,contact:[67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82],contain:44,content:61,control:[19,23,32,40,47,57,88],controllerpygam:47,convert:84,coordin:[45,62],copi:43,creat:[58,62],cursor:22,cursorspygam:22,da:85,data:[10,49,52],decid:59,definit:62,design:84,detect:[64,84],direct:42,displai:[4,23,58,59],displaypygam:23,distract:84,divers:[87,88],document:15,don:84,draw:[24,30,58],drawpygam:24,driven:68,entir:58,epilog:74,event:[5,25,27,58,68,70,84,85,88],eventpygam:25,everyth:58,exampl:[26,58,59,65,66],examplesmodul:26,extend:64,fasteventpygam:27,file:14,finish:[58,89],first:[62,86],font:[28,29],fontpygam:28,freetypeenhanc:29,friend:84,from:[10,62],front:15,fundament:85,game:[50,58,61,63,84,85,87],gamepad:32,gener:43,get:84,gfxdrawpygam:30,gmail:[67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82],go:62,graduat:65,graphic:41,group:64,gui:[68,76],guid:84,handl:[58,62,86],help:62,here:62,hero:62,high:1,histori:[63,64],hit:89,how:[59,62],i:62,imag:[31,35,51,57,62],imagepygam:31,improv:62,inform:44,init:[57,60],initi:[58,60],input:[37,55,58,62,70],interact:[25,27,32,37],interfac:52,intro:63,introduct:[57,58,59,61,63,64,65],issu:84,joi:32,joystick:32,joystickpygam:32,just:62,keyboard:33,keypygam:33,kick:86,kim:[67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82],know:84,korean:[67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82],left:32,legaci:16,lesson:64,let:[62,89],level:[1,44],line:[58,86],list:[57,62],live:57,load:[28,29,38,58,86],localspygam:34,lock:65,logo:16,loop:[58,85],main:58,make:[10,61,62],manag:84,mani:64,map:[32,62],mask:[35,57],maskpygam:35,mathpygam:36,midi:37,midipygam:37,mix:64,mixer:[7,40],mixerpygam:38,mode:59,modul:[10,18,19,22,23,24,25,27,28,29,30,31,32,33,35,36,37,38,39,40,43,44,46,47,49,50,52,54,55,56,57,58,63,64,86],monitor:54,more:[62,65],mous:39,mousepygam:39,move:62,movement:62,multipl:62,musicpygam:40,mysteri:62,name:21,need:84,newbi:84,next:62,nintendo:32,note:61,numer:65,numpi:65,object:[17,20,41,42,45,50,51,58,85,87,88],obsolet:84,off:86,option:84,other:[10,65],outdat:84,output:[37,68,72],over:58,overlai:41,overlaypygam:41,overview:63,own:[62,64],packag:[44,53],page:[15,16],part:84,path:14,pattern:84,perfect:84,physic:87,pixel:[42,43,52,62,84],pixelarraypygam:42,pixelcopypygam:43,plai:38,playstat:32,plu:73,prepar:[58,62],pro:32,problem:64,process:[69,71],product:89,program:26,prolog:67,protocol:17,put:[58,62,89],py:[13,66],pygam:[0,1,2,3,4,5,6,7,8,9,11,12,13,15,16,17,18,19,20,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,84,85,88],pygameth:44,python:[63,65,84],pythoni:84,queue:[25,27],quick:15,quit:60,realli:84,recogn:84,rect:[8,84],rectangular:45,rectpygam:45,refer:15,render:[28,29,64],repres:51,represent:20,resourc:[22,58,86],revis:85,right:32,rule:84,rumia0601:[67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82],rwobject:9,s:62,sampl:49,scene:58,scrappygam:46,screen:[23,62],sdl2_video:48,set:[59,69],setup:58,shape:[24,30],side:[84,89],simpl:[87,88],singl:57,six:84,slot:10,smooth:62,sndarraypygam:49,so:62,softwar:84,some:62,sound:[38,49],sprite:[58,64,87],spritepygam:50,src_c:[1,2,3,4,5,6,7,8,9,11,12],src_py:13,start:15,step:62,store:45,stream:[40,57],style:61,subsystem:84,suit:53,support:46,surfac:[11,17,42,52,56,65,84],surfacepygam:51,surfarrai:65,surfarraypygam:52,surflock:12,t:84,ta:85,tabl:61,take:62,tast:63,templat:68,test:53,testspygam:53,text:58,them:64,thing:[84,86],threshold:57,through:17,time:[54,62],timepygam:54,togeth:[62,64,89],top:44,touch:55,touchpygam:55,trackbal:32,transfer:31,transform:56,transformpygam:56,transpar:65,tutori:[15,57,58,59,60,62,64,65],type:64,unit:53,updat:58,us:[18,52,57,84],user:[62,88],vector:[36,87],version:[13,44],versionsmal:44,video:41,vision:57,vs:84,wai:84,wari:84,what:[62,84],which:84,why:67,window:23,work:[33,39,47,55,84],x:32,xbox:32,you:[62,84],youngwook:[67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82],your:[62,64,84]}}) \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/tut/CameraIntro.html b/.venv/Lib/site-packages/pygame/docs/generated/tut/CameraIntro.html new file mode 100644 index 00000000..91c1f0aa --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/tut/CameraIntro.html @@ -0,0 +1,376 @@ + + + + + + + + + Pygame Tutorials - Camera Module Introduction — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

Camera Module Introduction

+
+
Author
+

by Nirav Patel

+
+
Contact
+

nrp@eclecti.cc

+
+
+

Pygame 1.9 comes with support for interfacing cameras, allowing you to capture +still images, watch live streams, and do some simple computer vision. This +tutorial will cover all of those use cases, providing code samples you can base +your app or game on. You can refer to the reference documentation +for the full API.

+
+

Note

+

As of Pygame 1.9, the camera module offers native support for cameras +that use v4l2 on Linux. There is support for other platforms via Videocapture +or OpenCV, but this guide will focus on the native module. Most of the code +will be valid for other platforms, but certain things like controls will not +work. The module is also marked as EXPERIMENTAL, meaning the API could +change in subsequent versions.

+
+
+

Import and Init

+
import pygame
+import pygame.camera
+from pygame.locals import *
+
+pygame.init()
+pygame.camera.init()
+
+
+

As the camera module is optional, it needs to be imported and initialized +manually as shown above.

+
+
+

Capturing a Single Image

+

Now we will go over the simplest case of opening a camera and capturing a frame +as a surface. In the below example, we assume that there is a camera at +/dev/video0 on the computer, and initialize it with a size of 640 by 480. +The surface called image is whatever the camera was seeing when get_image() was +called.

+
cam = pygame.camera.Camera("/dev/video0",(640,480))
+cam.start()
+image = cam.get_image()
+
+
+
+

Listing Connected Cameras

+

You may be wondering, what if we don't know the exact path of the camera? +We can ask the module to provide a list of cameras attached to the +computer and initialize the first camera in the list.

+
camlist = pygame.camera.list_cameras()
+if camlist:
+    cam = pygame.camera.Camera(camlist[0],(640,480))
+
+
+
+
+

Using Camera Controls

+

Most cameras support controls like flipping the image and changing brightness. +set_controls() and get_controls() can be used at any point after using start().

+
cam.set_controls(hflip = True, vflip = False)
+print camera.get_controls()
+
+
+
+
+
+

Capturing a Live Stream

+

The rest of this tutorial will be based around capturing a live stream of +images. For this, we will be using the class below. As described, it will +simply blit a constant stream of camera frames to the screen, effectively +showing live video. It is basically what you would expect, looping get_image(), +blitting to the display surface, and flipping it. For performance reasons, +we will be supplying the camera with the same surface to use each time.

+
class Capture:
+    def __init__(self):
+        self.size = (640,480)
+        # create a display surface. standard pygame stuff
+        self.display = pygame.display.set_mode(self.size, 0)
+
+        # this is the same as what we saw before
+        self.clist = pygame.camera.list_cameras()
+        if not self.clist:
+            raise ValueError("Sorry, no cameras detected.")
+        self.cam = pygame.camera.Camera(self.clist[0], self.size)
+        self.cam.start()
+
+        # create a surface to capture to.  for performance purposes
+        # bit depth is the same as that of the display surface.
+        self.snapshot = pygame.surface.Surface(self.size, 0, self.display)
+
+    def get_and_flip(self):
+        # if you don't want to tie the framerate to the camera, you can check
+        # if the camera has an image ready.  note that while this works
+        # on most cameras, some will never return true.
+        if self.cam.query_image():
+            self.snapshot = self.cam.get_image(self.snapshot)
+
+        # blit it to the display surface.  simple!
+        self.display.blit(self.snapshot, (0,0))
+        pygame.display.flip()
+
+    def main(self):
+        going = True
+        while going:
+            events = pygame.event.get()
+            for e in events:
+                if e.type == QUIT or (e.type == KEYDOWN and e.key == K_ESCAPE):
+                    # close the camera safely
+                    self.cam.stop()
+                    going = False
+
+            self.get_and_flip()
+
+
+

Since get_image() is a blocking call that could take quite a bit of time on a +slow camera, this example uses query_image() to see if the camera is ready. +This allows you to separate the framerate of your game from that of your camera. +It is also possible to have the camera capturing images in a separate thread, +for approximately the same performance gain, if you find that your camera does +not support the query_image() function correctly.

+
+
+

Basic Computer Vision

+

By using the camera, transform, and mask modules, pygame can do some basic +computer vision.

+
+

Colorspaces

+

When initializing a camera, colorspace is an optional parameter, with 'RGB', +'YUV', and 'HSV' as the possible choices. YUV and HSV are both generally more +useful for computer vision than RGB, and allow you to more easily threshold by +color, something we will look at later in the tutorial.

+
self.cam = pygame.camera.Camera(self.clist[0], self.size, "RGB")
+
+
+../_images/camera_rgb.jpg +
self.cam = pygame.camera.Camera(self.clist[0], self.size, "YUV")
+
+
+../_images/camera_yuv.jpg +
self.cam = pygame.camera.Camera(self.clist[0], self.size, "HSV")
+
+
+../_images/camera_hsv.jpg +
+
+

Thresholding

+

Using the threshold() function from the transform module, one can do simple +green screen like effects, or isolate specifically colored objects in a scene. +In the below example, we threshold out just the green tree and make the rest +of the image black. Check the reference documentation for details on the +threshold function.

+
self.thresholded = pygame.surface.Surface(self.size, 0, self.display)
+self.snapshot = self.cam.get_image(self.snapshot)
+pygame.transform.threshold(self.thresholded,self.snapshot,(0,255,0),(90,170,170),(0,0,0),2)
+
+
+../_images/camera_thresholded.jpg +

Of course, this is only useful if you already know the exact color of the object +you are looking for. To get around this and make thresholding usable in the +real world, we need to add a calibration stage where we identify the color of an +object and use it to threshold against. We will be using the average_color() +function of the transform module to do this. Below is an example calibration +function that you could loop until an event like a key press, and an image of +what it would look like. The color inside the box will be the one that is +used for the threshold. Note that we are using the HSV colorspace in the below +images.

+
def calibrate(self):
+    # capture the image
+    self.snapshot = self.cam.get_image(self.snapshot)
+    # blit it to the display surface
+    self.display.blit(self.snapshot, (0,0))
+    # make a rect in the middle of the screen
+    crect = pygame.draw.rect(self.display, (255,0,0), (145,105,30,30), 4)
+    # get the average color of the area inside the rect
+    self.ccolor = pygame.transform.average_color(self.snapshot, crect)
+    # fill the upper left corner with that color
+    self.display.fill(self.ccolor, (0,0,50,50))
+    pygame.display.flip()
+
+
+../_images/camera_average.jpg +
pygame.transform.threshold(self.thresholded,self.snapshot,self.ccolor,(30,30,30),(0,0,0),2)
+
+
+../_images/camera_thresh.jpg +

You can use the same idea to do a simple green screen/blue screen, by first +getting a background image and then thresholding against it. The below example +just has the camera pointed at a blank white wall in HSV colorspace.

+
def calibrate(self):
+    # capture a bunch of background images
+    bg = []
+    for i in range(0,5):
+      bg.append(self.cam.get_image(self.background))
+    # average them down to one to get rid of some noise
+    pygame.transform.average_surfaces(bg,self.background)
+    # blit it to the display surface
+    self.display.blit(self.background, (0,0))
+    pygame.display.flip()
+
+
+../_images/camera_background.jpg +
pygame.transform.threshold(self.thresholded,self.snapshot,(0,255,0),(30,30,30),(0,0,0),1,self.background)
+
+
+../_images/camera_green.jpg +
+
+

Using the Mask Module

+

The stuff above is great if you just want to display images, but with the +mask module, you can also use a camera as an +input device for a game. For example, going back to the example of +thresholding out a specific object, we can find the position of that object and +use it to control an on screen object.

+
def get_and_flip(self):
+    self.snapshot = self.cam.get_image(self.snapshot)
+    # threshold against the color we got before
+    mask = pygame.mask.from_threshold(self.snapshot, self.ccolor, (30, 30, 30))
+    self.display.blit(self.snapshot,(0,0))
+    # keep only the largest blob of that color
+    connected = mask.connected_component()
+    # make sure the blob is big enough that it isn't just noise
+    if mask.count() > 100:
+        # find the center of the blob
+        coord = mask.centroid()
+        # draw a circle with size variable on the size of the blob
+        pygame.draw.circle(self.display, (0,255,0), coord, max(min(50,mask.count()/400),5))
+    pygame.display.flip()
+
+
+../_images/camera_mask.jpg +

This is just the most basic example. You can track multiple different colored +blobs, find the outlines of objects, have collision detection between real life +and in game objects, get the angle of an object to allow for even finer control, +and more. Have fun!

+
+
+
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/tut/ChimpLineByLine.html b/.venv/Lib/site-packages/pygame/docs/generated/tut/ChimpLineByLine.html new file mode 100644 index 00000000..af31a527 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/tut/ChimpLineByLine.html @@ -0,0 +1,595 @@ + + + + + + + + + Pygame Tutorials - Line By Line Chimp Example — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

Line By Line Chimp

+
+
Author
+

Pete Shinners

+
+
Contact
+

pete@shinners.org

+
+
+
+
+
+

Introduction

+

In the pygame examples there is a simple example named "chimp". +This example simulates a punchable monkey moving around the screen with +promises of riches and reward. The example itself is very simple, and a +bit thin on error-checking code. This example program demonstrates many of +pygame's abilities, like creating a window, loading images and sounds, +rendering text, and basic event and mouse handling.

+

The program and images can be found inside the standard source distribution +of pygame. You can run it by running python -m pygame.examples.chimp in +your terminal.

+

This tutorial will go through the code block by block. Explaining how +the code works. There will also be mention of how the code could be improved +and what error checking could help out.

+

This is an excellent tutorial for people getting their first look at +the pygame code. Once pygame is fully installed, you can find +and run the chimp demo for yourself in the examples directory.

+
+

(no, this is not a banner ad, it's the screenshot)

+chimp game banner +

Full Source

+
+
+
+

Import Modules

+

This is the code that imports all the needed modules into your program. +It also checks for the availability of some of the optional pygame modules.

+
# Import Modules
+import os
+import pygame as pg
+
+if not pg.font:
+    print("Warning, fonts disabled")
+if not pg.mixer:
+    print("Warning, sound disabled")
+
+main_dir = os.path.split(os.path.abspath(__file__))[0]
+data_dir = os.path.join(main_dir, "data")
+
+
+

First, we import the standard "os" python module. This allow +us to do things like create platform independent file paths.

+

In the next line, we import the pygame package. In our case, we import +pygame as pg, so that all of the functionality of pygame is able to +be referenced from the namespace pg.

+

Some pygame modules are optional, and if they aren't found, +they evaluate to False. Because of that, we decide to print +a nice warning message if the font or +mixer modules in pygame are not available. +(Although they will only be unavailable in very uncommon situations).

+

Lastly, we prepare two paths for the rest of the code to use. +main_dir uses the os.path module and the __file__ variable provided +by Python to locate the game's python file, and extract the folder from +that path. It then prepares the variable data_dir to tell the +loading functions exactly where to look.

+
+
+

Loading Resources

+

Here we have two functions we can use to load images and sounds. We will +look at each function individually in this section.

+
def load_image(name, colorkey=None, scale=1):
+    fullname = os.path.join(data_dir, name)
+    image = pg.image.load(fullname)
+
+    size = image.get_size()
+    size = (size[0] * scale, size[1] * scale)
+    image = pg.transform.scale(image, size)
+
+    image = image.convert()
+    if colorkey is not None:
+        if colorkey == -1:
+            colorkey = image.get_at((0, 0))
+        image.set_colorkey(colorkey, pg.RLEACCEL)
+    return image, image.get_rect()
+
+
+

This function takes the name of an image to load. It also optionally +takes an argument it can use to set a colorkey for the image, and an argument +to scale the image. A colorkey is used in graphics to represent a color of the +image that is transparent.

+

The first thing this function does is create a full pathname to the file. +In this example all the resources are in a "data" subdirectory. By using +the os.path.join function, a pathname will be created that works for whatever +platform the game is running on.

+

Next we load the image using the pygame.image.load()load new image from a file (or file-like object) function. +After the image is loaded, we make an important +call to the convert() function. This makes a new copy of a Surface and converts +its color format and depth to match the display. This means blitting the +image to the screen will happen as quickly as possible.

+

We then scale the image, using the pygame.transform.scale()resize to new resolution function. +This function takes a Surface and the size it should be scaled to. To scale +by a scalar, we can get the size and scale the x and y by the scalar.

+

Last, we set the colorkey for the image. If the user supplied an argument +for the colorkey argument we use that value as the colorkey for the image. +This would usually just be a color RGB value, like (255, 255, 255) for +white. You can also pass a value of -1 as the colorkey. In this case the +function will lookup the color at the topleft pixel of the image, and use +that color for the colorkey.

+
def load_sound(name):
+    class NoneSound:
+        def play(self):
+            pass
+
+    if not pg.mixer or not pg.mixer.get_init():
+        return NoneSound()
+
+    fullname = os.path.join(data_dir, name)
+    sound = pg.mixer.Sound(fullname)
+
+    return sound
+
+
+

Next is the function to load a sound file. The first thing this function +does is check to see if the pygame.mixerpygame module for loading and playing sounds module was imported correctly. +If not, it returns a small class instance that has a dummy play method. +This will act enough like a normal Sound object for this game to run without +any extra error checking.

+

This function is similar to the image loading function, but handles some +different problems. First we create a full path to the sound image, and +load the sound file. Then we simply return the loaded Sound object.

+
+
+

Game Object Classes

+

Here we create two classes to represent the objects in our game. Almost +all the logic for the game goes into these two classes. We will look over +them one at a time here.

+
class Fist(pg.sprite.Sprite):
+    """moves a clenched fist on the screen, following the mouse"""
+
+    def __init__(self):
+        pg.sprite.Sprite.__init__(self)  # call Sprite initializer
+        self.image, self.rect = load_image("fist.png", -1)
+        self.fist_offset = (-235, -80)
+        self.punching = False
+
+    def update(self):
+        """move the fist based on the mouse position"""
+        pos = pg.mouse.get_pos()
+        self.rect.topleft = pos
+        self.rect.move_ip(self.fist_offset)
+        if self.punching:
+            self.rect.move_ip(15, 25)
+
+    def punch(self, target):
+        """returns true if the fist collides with the target"""
+        if not self.punching:
+            self.punching = True
+            hitbox = self.rect.inflate(-5, -5)
+            return hitbox.colliderect(target.rect)
+
+    def unpunch(self):
+        """called to pull the fist back"""
+        self.punching = False
+
+
+

Here we create a class to represent the players fist. It is derived from +the Sprite class included in the pygame.spritepygame module with basic game object classes module. The __init__ function +is called when new instances of this class are created. The first thing +we do is be sure to call the __init__ function for our base class. This +allows the Sprite's __init__ function to prepare our object for use as a +sprite. This game uses one of the sprite drawing Group classes. These classes +can draw sprites that have an "image" and "rect" attribute. By simply changing +these two attributes, the renderer will draw the current image at the current +position.

+

All sprites have an update() method. This function is typically called +once per frame. It is where you should put code that moves and updates +the variables for the sprite. The update() method for the fist moves the +fist to the location of the mouse pointer. It also offsets the fist position +slightly if the fist is in the "punching" state.

+

The following two functions punch() and unpunch() change the punching +state for the fist. The punch() method also returns a true value if the fist +is colliding with the given target sprite.

+
class Chimp(pg.sprite.Sprite):
+    """moves a monkey critter across the screen. it can spin the
+    monkey when it is punched."""
+
+    def __init__(self):
+        pg.sprite.Sprite.__init__(self)  # call Sprite initializer
+        self.image, self.rect = load_image("chimp.png", -1, 4)
+        screen = pg.display.get_surface()
+        self.area = screen.get_rect()
+        self.rect.topleft = 10, 90
+        self.move = 18
+        self.dizzy = False
+
+    def update(self):
+        """walk or spin, depending on the monkeys state"""
+        if self.dizzy:
+            self._spin()
+        else:
+            self._walk()
+
+    def _walk(self):
+        """move the monkey across the screen, and turn at the ends"""
+        newpos = self.rect.move((self.move, 0))
+        if not self.area.contains(newpos):
+            if self.rect.left < self.area.left or self.rect.right > self.area.right:
+                self.move = -self.move
+                newpos = self.rect.move((self.move, 0))
+                self.image = pg.transform.flip(self.image, True, False)
+        self.rect = newpos
+
+    def _spin(self):
+        """spin the monkey image"""
+        center = self.rect.center
+        self.dizzy = self.dizzy + 12
+        if self.dizzy >= 360:
+            self.dizzy = False
+            self.image = self.original
+        else:
+            rotate = pg.transform.rotate
+            self.image = rotate(self.original, self.dizzy)
+        self.rect = self.image.get_rect(center=center)
+
+    def punched(self):
+        """this will cause the monkey to start spinning"""
+        if not self.dizzy:
+            self.dizzy = True
+            self.original = self.image
+
+
+

The Chimp class is doing a little more work than the fist, but nothing +more complex. This class will move the chimp back and forth across the +screen. When the monkey is punched, he will spin around to exciting effect. +This class is also derived from the base Sprite +class, and is initialized the same as the fist. While initializing, the class +also sets the attribute "area" to be the size of the display screen.

+

The update function for the chimp simply looks at the current "dizzy" +state, which is true when the monkey is spinning from a punch. It calls either +the _spin or _walk method. These functions are prefixed with an underscore. +This is just a standard python idiom which suggests these methods should +only be used by the Chimp class. We could go so far as to give them a double +underscore, which would tell python to really try to make them private +methods, but we don't need such protection. :)

+

The _walk method creates a new position for the monkey by moving the current +rect by a given offset. If this new position crosses outside the display +area of the screen, it reverses the movement offset. It also mirrors the +image using the pygame.transform.flip()flip vertically and horizontally function. This is a crude effect +that makes the monkey look like he's turning the direction he is moving.

+

The _spin method is called when the monkey is currently "dizzy". The dizzy +attribute is used to store the current amount of rotation. When the monkey +has rotated all the way around (360 degrees) it resets the monkey image +back to the original, non-rotated version. Before calling the +pygame.transform.rotate()rotate an image function, you'll see the code makes a local +reference to the function simply named "rotate". There is no need to do that +for this example, it is just done here to keep the following line's length a +little shorter. Note that when calling the rotate function, we are always +rotating from the original monkey image. When rotating, there is a slight loss +of quality. Repeatedly rotating the same image and the quality would get worse +each time. Also, when rotating an image, the size of the image will actually +change. This is because the corners of the image will be rotated out, making +the image bigger. We make sure the center of the new image matches the center +of the old image, so it rotates without moving.

+

The last method is punched() which tells the sprite to enter its dizzy +state. This will cause the image to start spinning. It also makes a copy +of the current image named "original".

+
+
+

Initialize Everything

+

Before we can do much with pygame, we need to make sure its modules +are initialized. In this case we will also open a simple graphics window. +Now we are in the main() function of the program, which actually runs everything.

+
pg.init()
+screen = pg.display.set_mode((1280, 480), pg.SCALED)
+pg.display.set_caption("Monkey Fever")
+pg.mouse.set_visible(False)
+
+
+

The first line to initialize pygame takes care of a bit of +work for us. It checks through the imported pygame modules and attempts +to initialize each one of them. It is possible to go back and check if modules +failed to initialize, but we won't bother here. It is also possible to +take a lot more control and initialize each specific module by hand. That +type of control is generally not needed, but is available if you desire.

+

Next we set up the display graphics mode. Note that the pygame.displaypygame module to control the display window and screen +module is used to control all the display settings. In this case we are +asking for a 1280 by 480 window, with the SCALED display flag. +This automatically scales up the window for displays much larger than the +window.

+

Last we set the window title and turn off the mouse cursor for our +window. Very basic to do, and now we have a small black window ready to +do our bidding. Usually the cursor defaults to visible, so there is no need +to really set the state unless we want to hide it.

+
+
+

Create The Background

+

Our program is going to have text message in the background. It would +be nice for us to create a single surface to represent the background and +repeatedly use that. The first step is to create the surface.

+
background = pg.Surface(screen.get_size())
+background = background.convert()
+background.fill((170, 238, 187))
+
+
+

This creates a new surface for us that is the same size as the display +window. Note the extra call to convert() after creating the Surface. The +convert with no arguments will make sure our background is the same format +as the display window, which will give us the fastest results.

+

We also fill the entire background with a certain green color. The fill() +function usually takes an RGB triplet as arguments, but supports many +input formats. See the pygame.Colorpygame object for color representations for all the color formats.

+
+
+

Put Text On The Background, Centered

+

Now that we have a background surface, lets get the text rendered to it. We +only do this if we see the pygame.fontpygame module for loading and rendering fonts module has imported properly. +If not, we just skip this section.

+
if pg.font:
+    font = pg.font.Font(None, 64)
+    text = font.render("Pummel The Chimp, And Win $$$", True, (10, 10, 10))
+    textpos = text.get_rect(centerx=background.get_width() / 2, y=10)
+    background.blit(text, textpos)
+
+
+

As you see, there are a couple steps to getting this done. First we +must create the font object and render it into a new surface. We then find +the center of that new surface and blit (paste) it onto the background.

+

The font is created with the font module's Font() constructor. Usually +you will pass the name of a TrueType font file to this function, but we +can also pass None, which will use a default font. The Font constructor +also needs to know the size of font we want to create.

+

We then render that font into a new surface. The render function creates +a new surface that is the appropriate size for our text. In this case +we are also telling render to create antialiased text (for a nice smooth +look) and to use a dark grey color.

+

Next we need to find the centered position of the text on our display. +We create a "Rect" object from the text dimensions, which allows us to +easily assign it to the screen center.

+

Finally we blit (blit is like a copy or paste) the text onto the background +image.

+
+
+

Display The Background While Setup Finishes

+

We still have a black window on the screen. Lets show our background +while we wait for the other resources to load.

+
screen.blit(background, (0, 0))
+pg.display.flip()
+
+
+

This will blit our entire background onto the display window. The +blit is self explanatory, but what about this flip routine?

+

In pygame, changes to the display surface are not immediately visible. +Normally, a display must be updated in areas that have changed for them +to be visible to the user. In this case the flip() function works nicely +because it simply handles the entire window area.

+
+
+

Prepare Game Object

+

Here we create all the objects that the game is going to need.

+
whiff_sound = load_sound("whiff.wav")
+punch_sound = load_sound("punch.wav")
+chimp = Chimp()
+fist = Fist()
+allsprites = pg.sprite.RenderPlain((chimp, fist))
+clock = pg.time.Clock()
+
+
+

First we load two sound effects using the load_sound function we defined +above. Then we create an instance of each of our sprite classes. And lastly +we create a sprite Group which will contain all +our sprites.

+

We actually use a special sprite group named RenderPlain. This sprite group can draw all the sprites it +contains to the screen. It is called RenderPlain because there are actually +more advanced Render groups. But for our game, we just need simple drawing. We +create the group named "allsprites" by passing a list with all the sprites that +should belong in the group. We could later on add or remove sprites from this +group, but in this game we won't need to.

+

The clock object we create will be used to help control our game's framerate. +we will use it in the main loop of our game to make sure it doesn't run too fast.

+
+
+

Main Loop

+

Nothing much here, just an infinite loop.

+
going = True
+while going:
+    clock.tick(60)
+
+
+

All games run in some sort of loop. The usual order of things is to +check on the state of the computer and user input, move and update the +state of all the objects, and then draw them to the screen. You'll see +that this example is no different.

+

We also make a call to our clock object, which will make sure our game +doesn't run faster than 60 frames per second.

+
+
+

Handle All Input Events

+

This is an extremely simple case of working the event queue.

+
for event in pg.event.get():
+    if event.type == pg.QUIT:
+        going = False
+    elif event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE:
+        going = False
+    elif event.type == pg.MOUSEBUTTONDOWN:
+        if fist.punch(chimp):
+            punch_sound.play()  # punch
+            chimp.punched()
+        else:
+            whiff_sound.play()  # miss
+    elif event.type == pg.MOUSEBUTTONUP:
+        fist.unpunch()
+
+
+

First we get all the available Events from pygame and loop through each +of them. The first two tests see if the user has quit our game, or pressed +the escape key. In these cases we just set going to False, allowing +us out of the infinite loop.

+

Next we just check to see if the mouse button was pressed or released. +If the button was pressed, we ask the fist object if it has collided with +the chimp. We play the appropriate sound effect, and if the monkey was hit, +we tell him to start spinning (by calling his punched() method).

+
+
+

Update the Sprites

+
allsprites.update()
+
+
+

Sprite groups have an update() method, which simply calls the update method +for all the sprites it contains. Each of the objects will move around, depending +on which state they are in. This is where the chimp will move one step side +to side, or spin a little farther if he was recently punched.

+
+
+

Draw The Entire Scene

+

Now that all the objects are in the right place, time to draw them.

+
screen.blit(background, (0, 0))
+allsprites.draw(screen)
+pg.display.flip()
+
+
+

The first blit call will draw the background onto the entire screen. This +erases everything we saw from the previous frame (slightly inefficient, but +good enough for this game). Next we call the draw() method of the sprite +container. Since this sprite container is really an instance of the "RenderPlain" +sprite group, it knows how to draw our sprites. Lastly, we flip() the contents +of pygame's software double buffer to the screen. This makes everything we've +drawn visible all at once.

+
+
+

Game Over

+

User has quit, time to clean up.

+
pg.quit()
+
+
+

Cleaning up the running game in pygame is extremely simple. +Since all variables are automatically destructed, we don't really have to do +anything, but calling pg.quit() explicitly cleans up pygame's internals.

+
+
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/tut/DisplayModes.html b/.venv/Lib/site-packages/pygame/docs/generated/tut/DisplayModes.html new file mode 100644 index 00000000..05385f03 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/tut/DisplayModes.html @@ -0,0 +1,312 @@ + + + + + + + + + Pygame Tutorials - Setting Display Modes — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

Setting Display Modes

+
+
Author
+

Pete Shinners

+
+
Contact
+

pete@shinners.org

+
+
+
+

Introduction

+

Setting the display mode in pygame creates a visible image surface +on the monitor. +This surface can either cover the full screen, or be windowed +on platforms that support a window manager. +The display surface is nothing more than a standard pygame surface object. +There are special functions needed in the pygame.displaypygame module to control the display window and screen +module to keep the image surface contents updated on the monitor.

+

Setting the display mode in pygame is an easier task than with most +graphic libraries. +The advantage is if your display mode is not available, +pygame will emulate the display mode that you asked for. +Pygame will select a display resolution and color depth that best matches +the settings you have requested, +then allow you to access the display with the format you have requested. +In reality, since the pygame.displaypygame module to control the display window and screen module is +a binding around the SDL library, SDL is really doing all this work.

+

There are advantages and disadvantages to setting the display mode in this +manner. +The advantage is that if your game requires a specific display mode, +your game will run on platforms that do not support your requirements. +It also makes life easier when you're getting something started, +it is always easy to go back later and make the mode selection a little more +particular. +The disadvantage is that what you request is not always what you will get. +There is also a performance penalty when the display mode must be emulated. +This tutorial will help you understand the different methods for querying +the platforms display capabilities, and setting the display mode for your game.

+
+
+

Setting Basics

+

The first thing to learn about is how to actually set the current display mode. +The display mode may be set at any time after the pygame.displaypygame module to control the display window and screen +module has been initialized. +If you have previously set the display mode, +setting it again will change the current mode. +Setting the display mode is handled with the function +pygame.display.set_mode((width, height), flags, depth)Initialize a window or screen for display. +The only required argument in this function is a sequence containing +the width and height of the new display mode. +The depth flag is the requested bits per pixel for the surface. +If the given depth is 8, pygame will create a color-mapped surface. +When given a higher bit depth, pygame will use a packed color mode. +Much more information about depths and color modes can be found in the +documentation for the display and surface modules. +The default value for depth is 0. +When given an argument of 0, pygame will select the best bit depth to use, +usually the same as the system's current bit depth. +The flags argument lets you control extra features for the display mode. +Again, more information about this is found in the pygame reference documents.

+
+
+

How to Decide

+

So how do you select a display mode that is going to work best with your +graphic resources and the platform your game is running on? +There are several methods for gathering information about the display device. +All of these methods must be called after the display module has been +initialized, but you likely want to call them before setting the display mode. +First, pygame.display.Info()Create a video display information object +will return a special object type of VidInfo, +which can tell you a lot about the graphics driver capabilities. +The function +pygame.display.list_modes(depth, flags)Get list of available fullscreen modes +can be used to find the supported graphic modes by the system. +pygame.display.mode_ok((width, height), flags, depth)Pick the best color depth for a display mode takes the same arguments as +set_mode(), +but returns the closest matching bit depth to the one you request. +Lastly, pygame.display.get_driver()Get the name of the pygame display backend +will return the name of the graphics driver selected by pygame.

+

Just remember the golden rule. +Pygame will work with pretty much any display mode you request. +Some display modes will need to be emulated, +which will slow your game down, +since pygame will need to convert every update you make to the +"real" display mode. The best bet is to always let pygame +choose the best bit depth, +and convert all your graphic resources to that format when they are loaded. +You let pygame choose its bit depth by calling +set_mode() +with no depth argument or a depth of 0, +or you can call +mode_ok() +to find a closest matching bit depth to what you need.

+

When your display mode is windowed, +you usually must match the same bit depth as the desktop. +When you are fullscreen, some platforms can switch to any bit depth that +best suits your needs. +You can find the depth of the current desktop if you get a VidInfo object +before ever setting your display mode.

+

After setting the display mode, +you can find out information about its settings by getting a VidInfo object, +or by calling any of the Surface.get* methods on the display surface.

+
+
+

Functions

+

These are the routines you can use to determine the most appropriate +display mode. +You can find more information about these functions in the display module +documentation.

+
+

pygame.display.mode_ok(size, flags, depth)Pick the best color depth for a display mode

+
+

This function takes the exact same arguments as pygame.display.set_mode(). +It returns the best available bit depth for the mode you have described. +If this returns zero, +then the desired display mode is not available without emulation.

+
+

pygame.display.list_modes(depth, flags)Get list of available fullscreen modes

+
+

Returns a list of supported display modes with the requested +depth and flags. +An empty list is returned when there are no modes. +The flags argument defaults to FULLSCREEN. +If you specify your own flags without FULLSCREEN, +you will likely get a return value of -1. +This means that any display size is fine, since the display will be windowed. +Note that the listed modes are sorted largest to smallest.

+
+

pygame.display.Info()Create a video display information object

+
+

This function returns an object with many members describing +the display device. +Printing the VidInfo object will quickly show you all the +members and values for this object.

+
>>> import pygame.display
+>>> pygame.display.init()
+>>> info = pygame.display.Info()
+>>> print(info)
+<VideoInfo(hw = 0, wm = 1,video_mem = 0
+        blit_hw = 0, blit_hw_CC = 0, blit_hw_A = 0,
+        blit_sw = 0, blit_sw_CC = 0, blit_sw_A = 0,
+        bitsize  = 32, bytesize = 4,
+        masks =  (16711680, 65280, 255, 0),
+        shifts = (16, 8, 0, 0),
+        losses =  (0, 0, 0, 8),
+        current_w = 1920, current_h = 1080
+>
+
+
+
+
+

You can test all these flags as simply members of the VidInfo object.

+
+
+

Examples

+

Here are some examples of different methods to init the graphics display. +They should help you get an idea of how to go about setting your display mode.

+
>>> # give me the best depth with a 640 x 480 windowed display
+>>> pygame.display.set_mode((640, 480))
+
+>>> # give me the biggest 16-bit display available
+>>> modes = pygame.display.list_modes(16)
+>>> if not modes:
+...     print('16-bit not supported')
+... else:
+...     print('Found Resolution:', modes[0])
+...     pygame.display.set_mode(modes[0], FULLSCREEN, 16)
+
+>>> # need an 8-bit surface, nothing else will do
+>>> if pygame.display.mode_ok((800, 600), 0, 8) != 8:
+...     print('Can only work with an 8-bit display, sorry')
+... else:
+...     pygame.display.set_mode((800, 600), 0, 8)
+
+
+
+
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/tut/ImportInit.html b/.venv/Lib/site-packages/pygame/docs/generated/tut/ImportInit.html new file mode 100644 index 00000000..a6d4f39f --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/tut/ImportInit.html @@ -0,0 +1,195 @@ + + + + + + + + + Pygame Tutorials - Import and Initialize — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

Import and Initialize

+
+
Author
+

Pete Shinners

+
+
Contact
+

pete@shinners.org

+
+
+

Getting pygame imported and initialized is a very simple process. It is also +flexible enough to give you control over what is happening. Pygame is a +collection of different modules in a single python package. Some of the +modules are written in C, and some are written in python. Some modules +are also optional, and might not always be present.

+

This is just a quick introduction on what is going on when you import pygame. +For a clearer explanation definitely see the pygame examples.

+
+

Import

+

First we must import the pygame package. Since pygame version 1.4 this +has been updated to be much easier. Most games will import all of pygame like this.

+
import pygame
+from pygame.locals import *
+
+
+

The first line here is the only necessary one. It imports all the available pygame +modules into the pygame package. The second line is optional, and puts a limited +set of constants and functions into the global namespace of your script.

+

An important thing to keep in mind is that several pygame modules are optional. +For example, one of these is the font module. When you "import pygame", pygame +will check to see if the font module is available. If the font module is available +it will be imported as "pygame.font". If the module is not available, "pygame.font" +will be set to None. This makes it fairly easy to later on test if the font module is available.

+
+
+

Init

+

Before you can do much with pygame, you will need to initialize it. The most common +way to do this is just make one call.

+
pygame.init()
+
+
+

This will attempt to initialize all the pygame modules for you. Not all pygame modules +need to be initialized, but this will automatically initialize the ones that do. You can +also easily initialize each pygame module by hand. For example to only initialize the +font module you would just call.

+
pygame.font.init()
+
+
+

Note that if there is an error when you initialize with "pygame.init()", it will silently fail. +When hand initializing modules like this, any errors will raise an exception. Any +modules that must be initialized also have a "get_init()" function, which will return true +if the module has been initialized.

+

It is safe to call the init() function for any module more than once.

+
+
+

Quit

+

Modules that are initialized also usually have a quit() function that will clean up. +There is no need to explicitly call these, as pygame will cleanly quit all the +initialized modules when python finishes.

+
+
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/tut/MakeGames.html b/.venv/Lib/site-packages/pygame/docs/generated/tut/MakeGames.html new file mode 100644 index 00000000..bb778081 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/tut/MakeGames.html @@ -0,0 +1,235 @@ + + + + + + + + + Making Games With Pygame — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

Making Games With Pygame

+
+
+
+

Table of Contents

+

1. Introduction

+
+
+

2. Revision: Pygame fundamentals

+
+
+

3. Kicking things off

+
+
+

4. Game object classes

+
+
+

5. User-controllable objects

+
+
+

6. Putting it all together

+
+
+
+
+

1. Introduction

+

First of all, I will assume you have read the Line By Line Chimp +tutorial, which introduces the basics of Python and pygame. Give it a read before reading this +tutorial, as I won't bother repeating what that tutorial says (or at least not in as much detail). This tutorial is aimed at those +who understand how to make a ridiculously simple little "game", and who would like to make a relatively simple game like Pong. +It introduces you to some concepts of game design, some simple mathematics to work out ball physics, and some ways to keep your +game easy to maintain and expand.

+

All the code in this tutorial works toward implementing TomPong, +a game I've written. By the end of the tutorial, you should not only have a firmer grasp of pygame, but +you should also understand how TomPong works, and how to make your own version.

+

Now, for a brief recap of the basics of pygame. A common method of organising the code for a game is to divide it into the following +six sections:

+
+
    +
  • Load modules which are required in the game. Standard stuff, except that you should +remember to import the pygame local names as well as the pygame module itself

  • +
  • Resource handling classes; define some classes to handle your most basic resources, +which will be loading images and sounds, as well as connecting and disconnecting to and from networks, loading save game +files, and any other resources you might have.

  • +
  • Game object classes; define the classes for your game object. In the pong example, +these will be one for the player's bat (which you can initialise multiple times, one for each player in the game), and one +for the ball (which can again have multiple instances). If you're going to have a nice in-game menu, it's also a good idea to make a +menu class.

  • +
  • Any other game functions; define other necessary functions, such as scoreboards, menu +handling, etc. Any code that you could put into the main game logic, but that would make understanding said logic harder, should +be put into its own function. So as plotting a scoreboard isn't game logic, it should be moved into a function.

  • +
  • Initialise the game, including the pygame objects themselves, the background, the game +objects (initialising instances of the classes) and any other little bits of code you might want to add in.

  • +
  • The main loop, into which you put any input handling (i.e. watching for users hitting +keys/mouse buttons), the code for updating the game objects, and finally for updating the screen.

  • +
+
+

Every game you make will have some or all of those sections, possibly with more of your own. For the purposes of this tutorial, I will +write about how TomPong is laid out, and the ideas I write about can be transferred to almost any kind of game you might make. I will +also assume that you want to keep all of the code in a single file, but if you're making a reasonably large game, it's often a good +idea to source certain sections into module files. Putting the game object classes into a file called objects.py, for +example, can help you keep game logic separate from game objects. If you have a lot of resource handling code, it can also be handy +to put that into resources.py. You can then from objects,resources import * to import all of the +classes and functions.

+
+
+

1.1. A note on coding styles

+

The first thing to remember when approaching any programming project is to decide on a coding style, and stay consistent. Python +solves a lot of the problems because of its strict interpretation of whitespace and indentation, but you can still choose the size +of your indentations, whether you put each module import on a new line, how you comment code, etc. You'll see how I do all of this +in the code examples; you needn't use my style, but whatever style you adopt, use it all the way through the program code. Also try +to document all of your classes, and comment on any bits of code that seem obscure, though don't start commenting the obvious. I've +seen plenty of people do the following:

+
player1.score += scoreup        # Add scoreup to player1 score
+
+
+

The worst code is poorly laid out, with seemingly random changes in style, and poor documentation. Poor code is not only annoying +for other people, but it also makes it difficult for you to maintain.

+
+
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/tut/MoveIt.html b/.venv/Lib/site-packages/pygame/docs/generated/tut/MoveIt.html new file mode 100644 index 00000000..9ec10692 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/tut/MoveIt.html @@ -0,0 +1,655 @@ + + + + + + + + + Pygame Tutorials - Help! How Do I Move An Image? — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

Help! How Do I Move An Image?

+
+
Author
+

Pete Shinners

+
+
Contact
+

pete@shinners.org

+
+
+

Many people new to programming and graphics have a hard time figuring +out how to make an image move around the screen. Without understanding +all the concepts, it can be very confusing. You're not the first person +to be stuck here, I'll do my best to take things step by step. We'll even +try to end with methods of keeping your animations efficient.

+

Note that we won't be teaching you to program with python in this article, +just introduce you to some of the basics with pygame.

+
+

Just Pixels On The Screen

+

Pygame has a display Surface. This is basically an image that is visible +on the screen, and the image is made up of pixels. The main way you change +these pixels is by calling the blit() function. This copies the pixels +from one image onto another.

+

This is the first thing to understand. When you blit an image onto the +screen, you are simply changing the color of the pixels on the screen. +Pixels aren't added or moved, we just change the colors of the pixels already +on the screen. These images you blit to the screen are also Surfaces in +pygame, but they are in no way connected to the display Surface. When they +are blitted to the screen they are copied into the display, but you still +have a unique copy of the original.

+

With this brief description. Perhaps you can already understand what +is needed to "move" an image. We don't actually move anything at all. We +simply blit the image in a new position. But before we draw the image in +the new position, we'll need to "erase" the old one. Otherwise the image +will be visible in two places on the screen. By rapidly erasing the image +and redrawing it in a new place, we achieve the "illusion" of movement.

+

Through the rest of this tutorial we will break this process down into +simpler steps. Even explaining the best ways to have multiple images moving +around the screen. You probably already have questions. Like, how do we +"erase" the image before drawing it in a new position? Perhaps you're still +totally lost? Well hopefully the rest of this tutorial can straighten things +out for you.

+
+
+

Let's Go Back A Step

+

Perhaps the concept of pixels and images is still a little foreign to +you? Well good news, for the next few sections we are going to use code that +does everything we want, it just doesn't use pixels. We're going to create +a small python list of 6 numbers, and imagine it represents some fantastic +graphics we could see on the screen. It might actually be surprising how +closely this represents exactly what we'll later be doing with real graphics.

+

So let's begin by creating our screen list and fill it with a beautiful +landscape of 1s and 2s.

+
>>> screen = [1, 1, 2, 2, 2, 1]
+>>> print(screen)
+[1, 1, 2, 2, 2, 1]
+
+
+

Now we've created our background. It's not going to be very exciting +unless we also draw a player on the screen. We'll create a mighty hero +that looks like the number 8. Let's stick him near the middle of the map +and see what it looks like.

+
>>> screen[3] = 8
+>>> print(screen)
+[1, 1, 2, 8, 2, 1]
+
+
+

This might have been as far as you've gotten if you jumped right in doing +some graphics programming with pygame. You've got some nice looking stuff +on the screen, but it cannot move anywhere. Perhaps now that our screen +is just a list of numbers, it's easier to see how to move him?

+
+
+

Making The Hero Move

+

Before we can start moving the character. We need to keep track of some +sort of position for him. In the last section when we drew him, we just picked +an arbitrary position. Let's do it a little more officially this time.

+
>>> playerpos = 3
+>>> screen[playerpos] = 8
+>>> print(screen)
+[1, 1, 2, 8, 2, 1]
+
+
+

Now it is pretty easy to move him to a new position. We simply change +the value of playerpos, and draw him on the screen again.

+
>>> playerpos = playerpos - 1
+>>> screen[playerpos] = 8
+>>> print(screen)
+[1, 1, 8, 8, 2, 1]
+
+
+

Whoops. Now we can see two heroes. One in the old position, and one +in his new position. This is exactly the reason we need to "erase" the hero +in his old position before we draw him in the new position. To erase him, +we need to change that value in the list back to what it was before the hero +was there. That means we need to keep track of the values on the screen before +the hero replaced them. There's several ways you could do this, but the easiest +is usually to keep a separate copy of the screen background. This means +we need to make some changes to our little game.

+
+
+

Creating A Map

+

What we want to do is create a separate list we will call our background. +We will create the background so it looks like our original screen did, +with 1s and 2s. Then we will copy each item from the background to the screen. +After that we can finally draw our hero back onto the screen.

+
>>> background = [1, 1, 2, 2, 2, 1]
+>>> screen = [0]*6                         #a new blank screen
+>>> for i in range(6):
+...     screen[i] = background[i]
+>>> print(screen)
+[1, 1, 2, 2, 2, 1]
+>>> playerpos = 3
+>>> screen[playerpos] = 8
+>>> print(screen)
+[1, 1, 2, 8, 2, 1]
+
+
+

It may seem like a lot of extra work. We're no farther off than we were +before the last time we tried to make him move. But this time we have the +extra information we need to move him properly.

+
+
+

Making The Hero Move (Take 2)

+

This time it will be easy to move the hero around. First we will erase +the hero from his old position. We do this by copying the correct value +from the background onto the screen. Then we will draw the character in his +new position on the screen

+
>>> print(screen)
+[1, 1, 2, 8, 2, 1]
+>>> screen[playerpos] = background[playerpos]
+>>> playerpos = playerpos - 1
+>>> screen[playerpos] = 8
+>>> print(screen)
+[1, 1, 8, 2, 2, 1]
+
+
+

There it is. The hero has moved one space to the left. We can use this +same code to move him to the left again.

+
>>> screen[playerpos] = background[playerpos]
+>>> playerpos = playerpos - 1
+>>> screen[playerpos] = 8
+>>> print(screen)
+[1, 8, 2, 2, 2, 1]
+
+
+

Excellent! This isn't exactly what you'd call smooth animation. But with +a couple small changes, we'll make this work directly with graphics on +the screen.

+
+
+

Definition: "blit"

+

In the next sections we will transform our program from using lists to +using real graphics on the screen. When displaying the graphics we will +use the term blit frequently. If you are new to doing graphics +work, you are probably unfamiliar with this common term.

+

BLIT: Basically, blit means to copy graphics from one image +to another. A more formal definition is to copy an array of data +to a bitmapped array destination. You can think of blit as just +"assigning" pixels. Much like setting values in our screen-list +above, blitting assigns the color of pixels in our image.

+

Other graphics libraries will use the word bitblt, or just blt, +but they are talking about the same thing. It is basically copying +memory from one place to another. Actually, it is a bit more advanced than +straight copying of memory, since it needs to handle things like pixel +formats, clipping, and scanline pitches. Advanced blitters can also +handle things like transparency and other special effects.

+
+
+

Going From The List To The Screen

+

To take the code we see in the above to examples and make them work with +pygame is very straightforward. We'll pretend we have loaded some pretty +graphics and named them "terrain1", "terrain2", and "hero". Where before +we assigned numbers to a list, we now blit graphics to the screen. Another +big change, instead of using positions as a single index (0 through 5), we +now need a two dimensional coordinate. We'll pretend each of the graphics +in our game is 10 pixels wide.

+
>>> background = [terrain1, terrain1, terrain2, terrain2, terrain2, terrain1]
+>>> screen = create_graphics_screen()
+>>> for i in range(6):
+...     screen.blit(background[i], (i*10, 0))
+>>> playerpos = 3
+>>> screen.blit(playerimage, (playerpos*10, 0))
+
+
+

Hmm, that code should seem very familiar, and hopefully more importantly; +the code above should make a little sense. Hopefully my illustration of setting +simple values in a list shows the similarity of setting pixels on the screen +(with blit). The only part that's really extra work is converting the player position +into coordinates on the screen. For now we just use a crude (playerpos*10, 0) , +but we can certainly do better than that. Now let's move the player +image over a space. This code should have no surprises.

+
>>> screen.blit(background[playerpos], (playerpos*10, 0))
+>>> playerpos = playerpos - 1
+>>> screen.blit(playerimage, (playerpos*10, 0))
+
+
+

There you have it. With this code we've shown how to display a simple background +with a hero's image on it. Then we've properly moved that hero one space +to the left. So where do we go from here? Well for one the code is still +a little awkward. First thing we'll want to do is find a cleaner way to represent +the background and player position. Then perhaps a bit of smoother, real +animation.

+
+
+

Screen Coordinates

+

To position an object on the screen, we need to tell the blit() function +where to put the image. In pygame we always pass positions as an (X,Y) coordinate. +This represents the number of pixels to the right, and the number of pixels +down to place the image. The top-left corner of a Surface is coordinate (0, +0). Moving to the right a little would be (10, 0), and then moving down just +as much would be (10, 10). When blitting, the position argument represents +where the topleft corner of the source should be placed on the destination.

+

Pygame comes with a convenient container for these coordinates, it is a +Rect. The Rect basically represents a rectangular area in these coordinates. +It has topleft corner and a size. The Rect comes with a lot of convenient +methods which help you move and position them. In our next examples we will +represent the positions of our objects with the Rects.

+

Also know that many functions in pygame expect Rect arguments. All of these +functions can also accept a simple tuple of 4 elements (left, top, width, +height). You aren't always required to use these Rect objects, but you will +mainly want to. Also, the blit() function can accept a Rect as its position +argument, it simply uses the topleft corner of the Rect as the real position.

+
+
+

Changing The Background

+

In all our previous sections, we've been storing the background as a list +of different types of ground. That is a good way to create a tile-based game, +but we want smooth scrolling. To make that a little easier, we're going to +change the background into a single image that covers the whole screen. This +way, when we want to "erase" our objects (before redrawing them) we only need +to blit the section of the erased background onto the screen.

+

By passing an optional third Rect argument to blit, we tell blit to only +use that subsection of the source image. You'll see that in use below as we +erase the player image.

+

Also note, now when we finish drawing to the screen, we call pygame.display.update() +which will show everything we've drawn onto the screen.

+
+
+

Smooth Movement

+

To make something appear to move smoothly, we only want to move it a couple +pixels at a time. Here is the code to make an object move smoothly across +the screen. Based on what we already now know, this should look pretty simple.

+
>>> screen = create_screen()
+>>> clock = pygame.time.Clock()            #get a pygame clock object
+>>> player = load_player_image()
+>>> background = load_background_image()
+>>> screen.blit(background, (0, 0))        #draw the background
+>>> position = player.get_rect()
+>>> screen.blit(player, position)          #draw the player
+>>> pygame.display.update()                #and show it all
+>>> for x in range(100):                   #animate 100 frames
+...     screen.blit(background, position, position) #erase
+...     position = position.move(2, 0)     #move player
+...     screen.blit(player, position)      #draw new player
+...     pygame.display.update()            #and show it all
+...     clock.tick(60)                     #update 60 times per second
+
+
+

There you have it. This is all the code that is needed to smoothly animate +an object across the screen. We can even use a pretty background character. +Another benefit of doing the background this way, the image for the player +can have transparency or cutout sections and it will still draw correctly +over the background (a free bonus).

+

We also throw in a call to pygame.time.Clock() to grab the clock element. +With it, we can call clock.tick() to set the framerate in frames per second. +This slows down our program a little, otherwise it might run so fast you might +not see it.

+
+
+

So, What Next?

+

Well there we have it. Hopefully this article has done everything it promised +to do. But, at this point the code really isn't ready for the next best-selling +game. How do we easily have multiple moving objects? What exactly are those +mysterious functions like load_player_image()? We also need a way to get simple +user input, and loop for more than 100 frames. We'll take the example we +have here, and turn it into an object oriented creation that would make momma +proud.

+
+
+

First, The Mystery Functions

+

Full information on these types of functions can be found in other tutorials +and reference. The pygame.image module has a load() function which will do +what we want. The lines to load the images should become this.

+
>>> player = pygame.image.load('player.bmp').convert()
+>>> background = pygame.image.load('liquid.bmp').convert()
+
+
+

We can see that's pretty simple, the load function just takes a filename +and returns a new Surface with the loaded image. After loading we make a call +to the Surface method, convert(). Convert returns us a new Surface of the +image, but now converted to the same pixel format as our display. Since the +images will be the same format at the screen, they will blit very quickly. +If we did not convert, the blit() function is slower, since it has to convert +from one type of pixel to another as it goes.

+

You may also have noticed that both the load() and convert() return new +Surfaces. This means we're really creating two Surfaces on each of these +lines. In other programming languages, this results in a memory leak (not +a good thing). Fortunately Python is smart enough to handle this, and pygame +will properly clean up the Surface we end up not using.

+

The other mystery function we saw in the above example was create_screen(). +In pygame it is simple to create a new window for graphics. The code to create +a 640x480 surface is below. By passing no other arguments, pygame will just +pick the best color depth and pixel format for us.

+
>>> screen = pygame.display.set_mode((640, 480))
+
+
+
+
+

Handling Some Input

+

We desperately need to change the main loop to look for any user input, (like +when the user closes the window). We need to add "event handling" to our +program. All graphical programs use this Event Based design. The program +gets events like "keyboard pressed" or "mouse moved" from the computer. Then +the program responds to the different events. Here's what the code should +look like. Instead of looping for 100 frames, we'll keep looping until the +user asks us to stop.

+
>>> while True:
+...     for event in pygame.event.get():
+...         if event.type == pygame.QUIT:
+...             sys.exit()
+...     move_and_draw_all_game_objects()
+
+
+

What this code simply does is, first loop forever, then check if there are +any events from the user. We exit the program if the user presses the close +button on the window. After we've checked all the events we move and draw +our game objects. (We'll also erase them before they move, too)

+
+
+

Moving Multiple Images

+

Here's the part where we're really going to change things around. Let's +say we want 10 different images moving around on the screen. A good way to +handle this is to use python's classes. We'll create a class that represents +our game object. This object will have a function to move itself, and then +we can create as many as we like. The functions to draw and move the object +need to work in a way where they only move one frame (or one step) at a time. +Here's the python code to create our class.

+
>>> class GameObject:
+...     def __init__(self, image, height, speed):
+...         self.speed = speed
+...         self.image = image
+...         self.pos = image.get_rect().move(0, height)
+...     def move(self):
+...         self.pos = self.pos.move(self.speed, 0)
+...         if self.pos.right > 600:
+...             self.pos.left = 0
+
+
+

So we have two functions in our class. The init function constructs our object. +It positions the object and sets its speed. The move method moves the object +one step. If it's gone too far, it moves the object back to the left.

+
+
+

Putting It All Together

+

Now with our new object class, we can put together the entire game. Here +is what the main function for our program will look like.

+
>>> screen = pygame.display.set_mode((640, 480))
+>>> clock = pygame.time.Clock()            #get a pygame clock object
+>>> player = pygame.image.load('player.bmp').convert()
+>>> background = pygame.image.load('background.bmp').convert()
+>>> screen.blit(background, (0, 0))
+>>> objects = []
+>>> for x in range(10):                    #create 10 objects</i>
+...     o = GameObject(player, x*40, x)
+...     objects.append(o)
+>>> while True:
+...     for event in pygame.event.get():
+...         if event.type == pygame.QUIT:
+...             sys.exit()
+...     for o in objects:
+...         screen.blit(background, o.pos, o.pos)
+...     for o in objects:
+...         o.move()
+...         screen.blit(o.image, o.pos)
+...     pygame.display.update()
+...     clock.tick(60)
+
+
+

And there it is. This is the code we need to animate 10 objects on the screen. +The only point that might need explaining is the two loops we use to clear +all the objects and draw all the objects. In order to do things properly, +we need to erase all the objects before drawing any of them. In our sample +here it may not matter, but when objects are overlapping, using two loops +like this becomes important.

+
+
+

Preparing for Improved User Input

+

With all keyboard input terminating the program, that's not very interactive. +Let's add some extra user input!

+

First we should create a unique character that the player will control. We +can do that in much the same way we created the other movable entities. Let's +call the player object p. We can already move any object, but, a player should +have more input than simply moving right. To accommodate this, let's revamp +our move function under our GameObject class.

+
>>> def move(self, up=False, down=False, left=False, right=False):
+...   if right:
+...       self.pos.right += self.speed
+...   if left:
+...       self.pos.right -= self.speed
+...   if down:
+...       self.pos.top += self.speed
+...   if up:
+...       self.pos.top -= self.speed
+...   if self.pos.right > WIDTH:
+...       self.pos.left = 0
+...   if self.pos.top > HEIGHT-SPRITE_HEIGHT:
+...       self.pos.top = 0
+...   if self.pos.right < SPRITE_WIDTH:
+...       self.pos.right = WIDTH
+...   if self.pos.top < 0:
+...       self.pos.top = HEIGHT-SPRITE_HEIGHT
+
+
+

There's certainly a lot more going on here, so let's take it one step at a time. +First, we've added some default values into the move function, declared as up, +down, left, and right. These booleans will allow us to specifically select a +direction that the object is moving in. The first part, where we go through and +check True for each variable, is where we will add to the position of the object, +much like before. Right controls horizontal, and top controls vertical positions.

+

Additionally, we've removed the magic number present previously, and replaced it +with the constants WIDTH, HEIGHT, SPRITE_WIDTH, and SPRITE_HEIGHT. These values +represent the screen width and height, along with the width and height of the object +displayed on the screen.

+

The second part, where the position is being checked, ensures that the position +is within the confines of our screen. With this in place, we need to make sure that +when one of our other objects calls move, we set right to true.

+
+
+

Adding the User Input

+

We've already seen that pygame has event handling, and we know that KEYDOWN is +an event in this loop. We could, under KEYDOWN, assert the key press matches an +arrow key, where we would then call move. However, this movement will only occur +once every time a key is pressed, and it therefore will be extremely choppy and +unpleasant.

+

For this, we can use pygame.key.get_pressed(), which returns a list of all keys, +and whether or not they are currently pressed. Since we want these key presses +to be maintained whether an event is currently happening or not, we should put +it outside of the main event handling loop, but still within our game loop. +Our functionality will look like this.

+
>>> keys = pygame.key.get_pressed()
+>>> if keys[pygame.K_UP]:
+...     p.move(up=True)
+>>> if keys[pygame.K_DOWN]:
+...     p.move(down=True)
+>>> if keys[pygame.K_LEFT]:
+...     p.move(left=True)
+>>> if keys[pygame.K_RIGHT]:
+...     p.move(right=True)
+
+
+

We simply get our list of keys pressed, called keys. We can then check the index +at the key code position to see if it is held down. For more key codes, I recommend +checking out the documentation on pygame.key.

+

When up is held, we move our object, p, up. When down is held, we move down. Rinse and +repeat for all cases, and we're good to go!

+
+
+

Putting it all Together One More time

+

Now that we're finished with the player functionality, let's take one last look to make +sure we understand everything.

+
>>> screen = pygame.display.set_mode((640, 480))
+>>> clock = pygame.time.Clock()            #get a pygame clock object
+>>> player = pygame.image.load('player.bmp').convert()
+>>> entity = pygame.image.load('alien1.bmp').convert()
+>>> background = pygame.image.load('background.bmp').convert()
+>>> screen.blit(background, (0, 0))
+>>> objects = []
+>>> p = GameObject(player, 10, 3)          #create the player object
+>>> for x in range(10):                    #create 10 objects</i>
+...     o = GameObject(entity, x*40, x)
+...     objects.append(o)
+>>> while True:
+...     screen.blit(background, p.pos, p.pos)
+...     for o in objects:
+...         screen.blit(background, o.pos, o.pos)
+...     keys = pygame.key.get_pressed()
+...     if keys[pygame.K_UP]:
+...         p.move(up=True)
+...     if keys[pygame.K_DOWN]:
+...         p.move(down=True)
+...     if keys[pygame.K_LEFT]:
+...         p.move(left=True)
+...     if keys[pygame.K_RIGHT]:
+...         p.move(right=True)
+...     for event in pygame.event.get():
+...         if event.type == pygame.QUIT:
+...             sys.exit()
+...     screen.blit(p.image, p.pos)
+...     for o in objects:
+...         o.move()
+...         screen.blit(o.image, o.pos)
+...     pygame.display.update()
+...     clock.tick(60)
+
+
+

A few things not mentioned earlier: we load in a second image and call it entity, +and we use that for all objects that aren't the player, which uses the player +image defined earlier.

+

And that's all there is to it! Now we have a fully functional player object that +is controlled using the arrow keys!

+
+
+

You Are On Your Own From Here

+

So what would be next on your road to learning? Well first playing around +with this example a bit. The full running version of this example is available +in the pygame examples directory. It is the example named +moveit.py . +Take a look at the code and play with it, run it, learn it.

+

Things you may want to work on is maybe having more than one type of object. +Finding a way to cleanly "delete" objects when you don't want to show them +any more. Also updating the display.update() call to pass a list of the areas +on-screen that have changed.

+

There are also other tutorials and examples in pygame that cover these +issues. So when you're ready to keep learning, keep on reading. :-)

+

Lastly, you can feel free to come to the pygame mailing list or chatroom +with any questions on this stuff. There's always folks on hand who can help +you out with this sort of business.

+

Lastly, have fun, that's what games are for!

+
+
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/tut/PygameIntro.html b/.venv/Lib/site-packages/pygame/docs/generated/tut/PygameIntro.html new file mode 100644 index 00000000..8250ab57 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/tut/PygameIntro.html @@ -0,0 +1,421 @@ + + + + + + + + + Pygame Intro — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

Python Pygame Introduction

+
+
Author
+

Pete Shinners

+
+
Contact
+

pete@shinners.org

+
+
+

This article is an introduction to the pygame library +for Python programmers. +The original version appeared in the PyZine volume 1 issue 3. +This version contains minor revisions, to +create an all-around better article. Pygame is a Python extension +library that wraps the SDL library +and its helpers.

+
+

HISTORY

+

Pygame started in the summer of 2000. Being a C programmer of many +years, I discovered both Python and SDL at about the same time. You are +already familiar with Python, which was at version 1.5.2. You may need +an introduction to SDL, which is the Simple DirectMedia Layer. +Created by Sam Lantinga, SDL is a cross-platform C library for +controlling multimedia, comparable to DirectX. It has been used for +hundreds of commercial and open source games. I was impressed at how clean +and straightforward both projects were and it wasn't long before I +realized mixing Python and SDL was an interesting proposal.

+

I discovered a small project already under-way with exactly the same +idea, PySDL. Created by Mark Baker, PySDL was a straightforward +implementation of SDL as a Python extension. The interface was cleaner +than a generic SWIG wrapping, but I felt it forced a "C style" of code. +The sudden death of PySDL prompted me to take on a new project of my +own.

+

I wanted to put together a project that really took advantage of +Python. My goal was to make it easy to do the simple things, and +straightforward to do the difficult things. Pygame was started in +October, 2000. Six months later pygame version 1.0 was released.

+
+
+

TASTE

+

I find the best way to understand a new library is to jump straight +into an example. In the early days of pygame, I created a bouncing ball +animation with 7 lines of code. Let's take a look at a friendlier +version of that same thing. This should be simple enough to follow +along, and a complete breakdown follows.

+../_images/intro_ball.gif +
import sys, pygame
+pygame.init()
+
+size = width, height = 320, 240
+speed = [2, 2]
+black = 0, 0, 0
+
+screen = pygame.display.set_mode(size)
+
+ball = pygame.image.load("intro_ball.gif")
+ballrect = ball.get_rect()
+
+while True:
+    for event in pygame.event.get():
+        if event.type == pygame.QUIT: sys.exit()
+
+    ballrect = ballrect.move(speed)
+    if ballrect.left < 0 or ballrect.right > width:
+        speed[0] = -speed[0]
+    if ballrect.top < 0 or ballrect.bottom > height:
+        speed[1] = -speed[1]
+
+    screen.fill(black)
+    screen.blit(ball, ballrect)
+    pygame.display.flip()
+
+
+

This is as simple as you can get for a bouncing animation. +First we see importing and initializing pygame is nothing noteworthy. +The import pygame imports the package with all the available +pygame modules. +The call to pygame.init() initializes each of these modules. +Make sure the gif file of the bouncing ball is in the same folder +as the code block. +On line 4 we set the size of the display window, for best +results you can change these numbers to match your own monitor's +resolution. +On line 8 we create a +graphical window with the call to pygame.display.set_mode(). +Pygame and SDL make this easy by defaulting to the best graphics modes +for the graphics hardware. You can override the mode and SDL will +compensate for anything the hardware cannot do. Pygame represents +images as Surface objects. +The display.set_mode() function creates a new Surface +object that represents the actual displayed graphics. Any drawing you +do to this Surface will become visible on the monitor.

+

At line 10 we load +our ball image. Pygame supports a variety of image formats through the +SDL_image library, including BMP, JPG, PNG, TGA, and GIF. +The pygame.image.load() function +returns us a Surface with the ball data. The Surface will keep any +colorkey or alpha transparency from the file. After loading the ball +image we create a variable named ballrect. Pygame comes with a +convenient utility object type named Rect, +which represents a rectangular area. Later, in the animation part of +the code, we will see what the Rect objects can do.

+

At this point, line 13, +our program is initialized and ready to run. Inside an infinite loop we +check for user input, move the ball, and then draw the ball. If you are +familiar with GUI programming, you have had experience with events and +event loops. In pygame this is no different, +we check if a QUIT event has happened. If so we +simply exit the program, pygame will ensure everything is cleanly +shutdown.

+

It is time to update our position for the ball. +Lines 17 moves the ballrect variable by the current speed. +Lines 18 thru 21 reverse the speed if the ball has moved outside the screen. +Not exactly Newtonian physics, but it is all we need.

+

On line 23 we erase +the screen by filling it with a black RGB color. If you have never +worked with animations this may seem strange. You may be asking "Why do +we need to erase anything, why don't we just move the ball on the +screen?" That is not quite the way computer animation works. Animation +is nothing more than a series of single images, which when displayed in +sequence do a very good job of fooling the human eye into seeing +motion. The screen is just a single image that the user sees. If we did +not take the time to erase the ball from the screen, we would actually +see a "trail" of the ball as we continuously draw the ball in its new +positions.

+

On line 24 we draw the ball image onto the screen. +Drawing of images is handled by the +Surface.blit() method. +A blit basically means copying pixel colors from one image to another. +We pass the blit method a source Surface +to copy from, and a position to place the source onto the destination.

+

The last thing we need to do is actually update the visible display. +Pygame manages the display with a double buffer. When we are finished +drawing we call the pygame.display.flip()Update the full display Surface to the screen method. +This makes everything we have drawn on the screen Surface +become visible. This buffering makes sure we only see completely drawn +frames on the screen. Without it, the user would see the half completed +parts of the screen as they are being created.

+

That concludes this short introduction to pygame. Pygame also has +modules to do things like input handling for the keyboard, mouse, and +joystick. It can mix audio and decode streaming music. +With the Surfaces you can draw simple +shapes, rotate and scale the picture, and even manipulate the pixels of +an image in realtime as numpy arrays. +Pygame also has the ability to act as a +cross platform display layer for PyOpenGL. Most of the pygame modules +are written in C, few are actually done in Python.

+

The pygame website has full reference documentation for every pygame +function and tutorials for all ranges of users. The pygame source comes +with many examples of things like monkey punching and UFO shooting.

+
+
+

PYTHON AND GAMING

+

"Is Python suitable for gaming?" The answer is, "It depends on the +game."

+

Python is actually quite capable at running games. It will likely even +surprise you how much is possible in under 30 milliseconds. Still, it +is not hard to reach the ceiling once your game begins to get more +complex. Any game running in realtime will be making full use of the +computer.

+../_images/intro_blade.jpg +

Over the past several years there has been an interesting trend in game development, +the move towards higher level languages. Usually a game is split into +two major parts. The game engine, which must be as fast as possible, +and the game logic, which makes the engine actually do something. It +wasn't long ago when the engine of a game was written in assembly, with +portions written in C. Nowadays, C has moved to the game engine, while +often the game itself is written in higher level scripting languages. +Games like Quake3 and Unreal run these scripts as portable bytecode.

+

In early 2001, developer Rebel Act Studios finished their game, +Severance: Blade of Darkness. Using their own custom 3D engine, the +rest of the game is written with Python. The game is a bloody action +3rd person perspective fighter. You control medieval warriors into +intricate decapitating combination attacks while exploring dungeons and +castles. You can download third party add-ons for this game, and find +they are nothing more than Python source files.

+

More recently, Python has been used in a variety of games like Freedom +Force, and Humungous' Backyard Sports Series.

+../_images/intro_freedom.jpg +

Pygame and SDL serve as an excellent C engine for 2D games. +Games will still find the largest part of their runtime is spent +inside SDL handling the graphics. +SDL can take advantage of graphics hardware acceleration. +Enabling this can change a game from running around 40 frames per +second to over 200 frames per second. When you see your Python game +running at 200 frames per second, you realize that Python and games can +work together.

+

It is impressive how well both Python and SDL work on multiple +platforms. For example, in May of 2001 I released my own full pygame +project, SolarWolf, an arcade style action game. One thing that has +surprised me is that one year later there has been no need for any +patches, bug fixes, or updates. The game was developed entirely on +windows, but runs on Linux, Mac OSX, and many Unixes without any extra +work on my end.

+

Still, there are very clear limitations. The best way to manage +hardware accelerated graphics is not always the way to get fastest +results from software rendering. Hardware support is not available on +all platforms. When a game gets more complex, it often must commit to +one or the other. SDL has some other design limitations, things like +full screen scrolling graphics can quickly bring your game down to +unplayable speeds. While SDL is not suitable for all types of games, +remember companies like Loki have used SDL to run a wide variety of +retail quality titles.

+

Pygame is fairly low-level when it comes to writing games. You'll +quickly find yourself needing to wrap common functions into your own +game environment. The great thing about this is there is nothing inside +pygame to get in your way. Your program is in full control of +everything. The side effect of that is you will find yourself borrowing +a lot of code to get a more advanced framework put together. You'll +need a better understanding of what you are doing.

+
+
+

CLOSING

+

Developing games is very rewarding, there is something exciting about +being able to see and interact with the code you've written. Pygame +currently has almost 30 other projects using it. Several of them are +ready to play now. You may be surprised to visit the pygame website, +and see what other users have been able to do with Python.

+

One thing that has caught my attention is the amount of people coming +to Python for the first time to try game development. I can see why +games are a draw for new programmers, but it can be difficult since +creating games requires a firmer understanding of the language. I've +tried to support this group of users by writing many examples and +pygame tutorials for people new to these concepts.

+

In the end, my advice is to keep it simple. I cannot stress this +enough. If you are planning to create your first game, there is a +lot to learn. Even a simpler game will challenge your designs, and +complex games don't necessarily mean fun games. When you understand +Python, you can use pygame to create a simple game in only one or two +weeks. From there you'll need a surprising amount of time to add +the polish to make that into a full presentable game.

+
+

Pygame Modules Overview

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

cdrom

playback

cursors

load cursor images, includes standard cursors

display

control the display window or screen

draw

draw simple shapes onto a Surface

event

manage events and the event queue

font

create and render TrueType fonts

image

save and load images

joystick

manage joystick devices

key

manage the keyboard

mouse

manage the mouse

sndarray

manipulate sounds with numpy

surfarray

manipulate images with numpy

time

control timing

transform

scale, rotate, and flip images

+
+
+
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/tut/SpriteIntro.html b/.venv/Lib/site-packages/pygame/docs/generated/tut/SpriteIntro.html new file mode 100644 index 00000000..a681fe7c --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/tut/SpriteIntro.html @@ -0,0 +1,496 @@ + + + + + + + + + Pygame Tutorials - Sprite Module Introduction — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

Sprite Module Introduction

+
+
Author
+

Pete Shinners

+
+
Contact
+

pete@shinners.org

+
+
+

Pygame version 1.3 comes with a new module, pygame.sprite. This module is +written in Python and includes some higher-level classes to manage your game +objects. By using this module to its full potential, you can easily manage and +draw your game objects. The sprite classes are very optimized, so it's likely +your game will run faster with the sprite module than without.

+

The sprite module is also meant to be very generic. It turns out you can use it +with nearly any type of gameplay. All this flexibility comes with a slight +penalty, it needs a little understanding to properly use it. The +reference documentation for the sprite module can keep +you running, but you'll probably need a bit more explanation of how to use +pygame.sprite in your own game.

+

Several of the pygame examples (like "chimp" and "aliens") have been updated to +use the sprite module. You may want to look into those first to see what this +sprite module is all about. The chimp module even has its own line-by-line +tutorial, which may help get more an understanding of programming with python +and pygame.

+

Note that this introduction will assume you have a bit of experience +programming with python, and are somewhat familiar with the different parts of +creating a simple game. In this tutorial the word "reference" is occasionally +used. This represents a python variable. Variables in python are references, +so you can have several variables all pointing to the same object.

+
+

History Lesson

+

The term "sprite" is a holdover from older computer and game machines. These +older boxes were unable to draw and erase normal graphics fast enough for them +to work as games. These machines had special hardware to handle game like +objects that needed to animate very quickly. These objects were called +"sprites" and had special limitations, but could be drawn and updated very +fast. They usually existed in special overlay buffers in the video. These days +computers have become generally fast enough to handle sprite like objects +without dedicated hardware. The term sprite is still used to represent just +about anything in a 2D game that is animated.

+
+
+

The Classes

+

The sprite module comes with two main classes. The first is Sprite, which should be used as a base class for all your game +objects. This class doesn't really do anything on its own, it just includes +several functions to help manage the game object. The other type of class is +Group. The Group class is a container for +different Sprite objects. There are actually several different types of +group classes. Some of the Groups can draw all the elements they contain, +for example.

+

This is all there really is to it. We'll start with a description of what each +type of class does, and then discuss the proper ways to use these two classes.

+
+
+

The Sprite Class

+

As mentioned before, the Sprite class is designed to be a base class for all +your game objects. You cannot really use it on its own, as it only has several +methods to help it work with the different Group classes. The sprite keeps +track of which groups it belongs to. +The class constructor (__init__ method) takes an argument of a +Group (or list of Groups) the Sprite instance should belong to. +You can also change the Group membership for the Sprite with the +add() and +remove() methods. +There is also a groups() method, +which returns a list of the current groups containing the sprite.

+

When using the your Sprite classes it's best to think of them as "valid" or +"alive" when they are belonging to one or more Groups. When you remove the +instance from all groups pygame will clean up the object. (Unless you have your +own references to the instance somewhere else.) The kill() method removes the sprite from all groups it +belongs to. This will cleanly delete the sprite object. If you've put some +little games together, you'll know sometimes cleanly deleting a game object can +be tricky. The sprite also comes with an alive() method, which returns true if it is still a +member of any groups.

+
+
+

The Group Class

+

The Group class is just a simple container. Similar to the sprite, it has +an add() and remove() method which can change which sprites belong to +the group. You also can pass a sprite or list of sprites to the constructor +(__init__() method) to create a Group instance that contains some +initial sprites.

+

The Group has a few other methods like empty() to remove all sprites from the group and +copy() which will return a copy of the group +with all the same members. Also the has() +method will quickly check if the Group contains a sprite or list of +sprites.

+

The other function you will use frequently is the sprites() method. This returns an object that can be +looped on to access every sprite the group contains. Currently this is just a +list of the sprites, but in later version of python this will likely use +iterators for better performance.

+

As a shortcut, the Group also has an update() method, which will call an update() method on +every sprite in the group. Passing the same arguments to each one. Usually in a +game you need some function that updates the state of a game object. It's very +easy to call your own methods using the Group.sprites() method, but this is +a shortcut that's used enough to be included. Also note that the base +Sprite class has a "dummy" update() method that takes any sort of +arguments and does nothing.

+

Lastly, the Group has a couple other methods that allow you to use it with +the builtin len() function, getting the number of sprites it contains, and +the "truth" operator, which allows you to do "if mygroup:" to check if the +group has any sprites.

+
+
+

Mixing Them Together

+

At this point the two classes seem pretty basic. Not doing a lot more than you +can do with a simple list and your own class of game objects. But there are +some big advantages to using the Sprite and Group together. A sprite +can belong to as many groups as you want. Remember as soon as it belongs to no +groups, it will usually be cleared up (unless you have other "non-group" +references to that object).

+

The first big thing is a fast simple way to categorize sprites. For example, +say we had a Pacman-like game. We could make separate groups for the different +types of objects in the game. Ghosts, Pac, and Pellets. When Pac eats a power +pellet, we can change the state for all ghost objects by effecting everything +in the Ghost group. This is quicker and simpler than looping through a list +of all the game objects and checking which ones are ghosts.

+

Adding and removing groups and sprites from each other is a very fast +operation, quicker than using lists to store everything. Therefore you can very +efficiently change group memberships. Groups can be used to work like simple +attributes for each game object. Instead of tracking some attribute like +"close_to_player" for a bunch of enemy objects, you could add them to a +separate group. Then when you need to access all the enemies that are near the +player, you already have a list of them, instead of going through a list of all +the enemies, checking for the "close_to_player" flag. Later on your game could +add multiple players, and instead of adding more "close_to_player2", +"close_to_player3" attributes, you can easily add them to different groups for +each player.

+

Another important benefit of using the Sprites and Groups is that the groups +cleanly handle the deleting (or killing) of game objects. In a game where many +objects are referencing other objects, sometimes deleting an object can be the +hardest part, since it can't go away until it is not referenced by anyone. Say +we have an object that is "chasing" another object. The chaser can keep a +simple Group that references the object (or objects) it is chasing. If the +object being chased happens to be destroyed, we don't need to worry about +notifying the chaser to stop chasing. The chaser can see for itself that its +group is now empty, and perhaps find a new target.

+

Again, the thing to remember is that adding and removing sprites from groups is +a very cheap/fast operation. You may be best off by adding many groups to +contain and organize your game objects. Some could even be empty for large +portions of the game, there isn't any penalties for managing your game like +this.

+
+
+

The Many Group Types

+

The above examples and reasons to use Sprites and Groups are only a tip +of the iceberg. Another advantage is that the sprite module comes with several +different types of Groups. These groups all work just like a regular old +Group, but they also have added functionality (or slightly different +functionality). Here's a list of the Group classes included with the +sprite module.

+
+

Group

+
+

This is the standard "no frills" group mainly explained above. Most of the +other Groups are derived from this one, but not all.

+
+

GroupSingle

+
+

This works exactly like the regular Group class, but it only contains +the most recently added sprite. Therefore when you add a sprite to this group, +it "forgets" about any previous sprites it had. Therefore it always contains +only one or zero sprites.

+
+

RenderPlain

+
+

This is a standard group derived from Group. It has a draw() method +that draws all the sprites it contains to the screen (or any Surface). For +this to work, it requires all sprites it contains to have a "image" and "rect" +attributes. It uses these to know what to blit, and where to blit it.

+
+

RenderClear

+
+

This is derived from the RenderPlain group, and adds a method named +clear(). This will erase the previous position of all drawn sprites. It +uses a background image to fill in the areas where the sprite were. It is smart +enough to handle deleted sprites and properly clear them from the screen when +the clear() method is called.

+
+

RenderUpdates

+
+

This is the Cadillac of rendering Groups. It is inherited from +RenderClear, but changes the draw() method to also return a list of +pygame Rects, which represent all the areas on screen that have been +changed.

+
+
+

That is the list of different groups available We'll discuss more about these +rendering groups in the next section. There's nothing stopping you from +creating your own Group classes as well. They are just python code, so you can +inherit from one of these and add/change whatever you want. In the future I +hope we can add a couple more Groups to this list. A GroupMulti which +is like the GroupSingle, but can hold up to a given number of sprites (in +some sort of circular buffer?). Also a super-render group that can clear the +position of the old sprites without needing a background image to do it (by +grabbing a copy of the screen before blitting). Who knows really, but in the +future we can add more useful classes to this list.

+
+
+

The Rendering Groups

+

From above we can see there are three different rendering groups. We could +probably just get away with the RenderUpdates one, but it adds overhead not +really needed for something like a scrolling game. So we have a couple tools +here, pick the right one for the right job.

+

For a scrolling type game, where the background completely changes every frame, +we obviously don't need to worry about python's update rectangles in the call +to display.update(). You should definitely go with the RenderPlain +group here to manage your rendering.

+

For games where the background is more stationary, you definitely don't want +pygame updating the entire screen (since it doesn't need to). This type of game +usually involves erasing the old position of each object, then drawing it in a +new place for each frame. This way we are only changing what is necessary. +Most of the time you will just want to use the RenderUpdates class here. +Since you will also want to pass this list of changes to the +display.update() function.

+

The RenderUpdates class also does a good job at minimizing overlapping +areas in the list of updated rectangles. If the previous position and current +position of an object overlap, it will merge them into a single rectangle. +Combined with the fact that it properly handles deleted objects, this is +one powerful Group class. If you've written a game that manages the changed +rectangles for the objects in a game, you know this the cause for a lot of +messy code in your game. Especially once you start to throw in objects that can +be deleted at any time. All this work is reduced to a clear() and +draw() method with this monster class. Plus with the overlap checking, it +is likely faster than when you did it manually.

+

Also note that there's nothing stopping you from mixing and matching these +render groups in your game. You should definitely use multiple rendering groups +when you want to do layering with your sprites. Also if the screen is split +into multiple sections, perhaps each section of the screen should use an +appropriate render group?

+
+
+

Collision Detection

+

The sprite module also comes with two very generic collision detection +functions. For more complex games, these really won't work for you, but you +can easily grab the source code for them, and modify them as needed.

+

Here's a summary of what they are, and what they do.

+
+

spritecollide(sprite, group, dokill) -> list

+
+

This checks for collisions between a single sprite and the sprites in a group. +It requires a "rect" attribute for all the sprites used. It returns a list of +all the sprites that overlap with the first sprite. The "dokill" argument is a +boolean argument. If it is true, the function will call the kill() method +on all the sprites. This means the last reference to each sprite is probably in +the returned list. Once the list goes away so do the sprites. A quick example +of using this in a loop

+
>>> for bomb in sprite.spritecollide(player, bombs, 1):
+...     boom_sound.play()
+...     Explosion(bomb, 0)
+
+
+

This finds all the sprites in the "bomb" group that collide with the player. +Because of the "dokill" argument it deletes all the crashed bombs. For each +bomb that did collide, it plays a "boom" sound effect, and creates a new +Explosion where the bomb was. (Note, the Explosion class here knows to +add each instance to the appropriate class, so we don't need to store it in a +variable, that last line might feel a little "funny" to you python programmers.)

+
+

groupcollide(group1, group2, dokill1, dokill2) -> dictionary

+
+

This is similar to the spritecollide function, but a little more complex. +It checks for collisions for all the sprites in one group, to the sprites in +another. There is a dokill argument for the sprites in each list. When +dokill1 is true, the colliding sprites in group1 will be kill()``ed. +When ``dokill2 is true, we get the same results for group2. The +dictionary it returns works like this; each key in the dictionary is a sprite +from group1 that had a collision. The value for that key is a list of the +sprites that it collided with. Perhaps another quick code sample explains it +best

+
>>> for alien in sprite.groupcollide(aliens, shots, 1, 1).keys()
+...     boom_sound.play()
+...     Explosion(alien, 0)
+...     kills += 1
+
+
+

This code checks for the collisions between player bullets and all the aliens +they might intersect. In this case we only loop over the dictionary keys, but +we could loop over the values() or items() if we wanted to do something +to the specific shots that collided with aliens. If we did loop over the +values() we would be looping through lists that contain sprites. The same +sprite may even appear more than once in these different loops, since the same +"shot" could have collided against multiple "aliens".

+
+
+

Those are the basic collision functions that come with pygame. It should be +easy to roll your own that perhaps use something different than the "rect" +attribute. Or maybe try to fine-tweak your code a little more by directly +effecting the collision object, instead of building a list of the collision? +The code in the sprite collision functions is very optimized, but you could +speed it up slightly by taking out some functionality you don't need.

+
+
+

Common Problems

+

Currently there is one main problem that catches new users. When you derive +your new sprite class with the Sprite base, you must call the +Sprite.__init__() method from your own class __init__() method. If you +forget to call the Sprite.__init__() method, you get a cryptic error, like +this

+
AttributeError: 'mysprite' instance has no attribute '_Sprite__g'
+
+
+
+
+

Extending Your Own Classes (Advanced)

+

Because of speed concerns, the current Group classes try to only do exactly +what they need, and not handle a lot of general situations. If you decide you +need extra features, you may want to create your own Group class.

+

The Sprite and Group classes were designed to be extended, so feel free +to create your own Group classes to do specialized things. The best place +to start is probably the actual python source code for the sprite module. +Looking at the current Sprite groups should be enough example on how to +create your own.

+

For example, here is the source code for a rendering Group that calls a +render() method for each sprite, instead of just blitting an "image" +variable from it. Since we want it to also handle updated areas, we will start +with a copy of the original RenderUpdates group, here is the code:

+
class RenderUpdatesDraw(RenderClear):
+    """call sprite.draw(screen) to render sprites"""
+    def draw(self, surface):
+        dirty = self.lostsprites
+        self.lostsprites = []
+        for s, r in self.spritedict.items():
+            newrect = s.draw(screen) #Here's the big change
+            if r is 0:
+                dirty.append(newrect)
+            else:
+                dirty.append(newrect.union(r))
+            self.spritedict[s] = newrect
+        return dirty
+
+
+

Following is more information on how you could create your own Sprite and +Group objects from scratch.

+

The Sprite objects only "require" two methods. "add_internal()" and +"remove_internal()". These are called by the Group classes when they are +removing a sprite from themselves. The add_internal() and +remove_internal() have a single argument which is a group. Your Sprite +will need some way to also keep track of the Groups it belongs to. You will +likely want to try to match the other methods and arguments to the real +Sprite class, but if you're not going to use those methods, you sure don't +need them.

+

It is almost the same requirements for creating your own Group. In fact, if +you look at the source you'll see the GroupSingle isn't derived from the +Group class, it just implements the same methods so you can't really tell +the difference. Again you need an "add_internal()" and "remove_internal()" +method that the sprites call when they want to belong or remove themselves from +the group. The add_internal() and remove_internal() have a single +argument which is a sprite. The only other requirement for the Group +classes is they have a dummy attribute named "_spritegroup". It doesn't matter +what the value is, as long as the attribute is present. The Sprite classes can +look for this attribute to determine the difference between a "group" and any +ordinary python container. (This is important, because several sprite methods +can take an argument of a single group, or a sequence of groups. Since they +both look similar, this is the most flexible way to "see" the difference.)

+

You should go through the code for the sprite module. While the code is a bit +"tuned", it's got enough comments to help you follow along. There's even a +TODO section in the source if you feel like contributing.

+
+
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/tut/SurfarrayIntro.html b/.venv/Lib/site-packages/pygame/docs/generated/tut/SurfarrayIntro.html new file mode 100644 index 00000000..a270c63a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/tut/SurfarrayIntro.html @@ -0,0 +1,658 @@ + + + + + + + + + Pygame Tutorials - Surfarray Introduction — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

Surfarray Introduction

+
+
Author
+

Pete Shinners

+
+
Contact
+

pete@shinners.org

+
+
+
+

Introduction

+

This tutorial will attempt to introduce users to both NumPy and the pygame +surfarray module. To beginners, the code that uses surfarray can be quite +intimidating. But actually there are only a few concepts to understand and +you will be up and running. Using the surfarray module, it becomes possible +to perform pixel level operations from straight python code. The performance +can become quite close to the level of doing the code in C.

+

You may just want to jump down to the "Examples" section to get an +idea of what is possible with this module, then start at the beginning here +to work your way up.

+

Now I won't try to fool you into thinking everything is very easy. To get +more advanced effects by modifying pixel values is very tricky. Just mastering +Numeric Python (SciPy's original array package was Numeric, the predecessor of NumPy) +takes a lot of learning. In this tutorial I'll be sticking with +the basics and using a lot of examples in an attempt to plant seeds of wisdom. +After finishing the tutorial you should have a basic handle on how the surfarray +works.

+
+
+

Numeric Python

+

If you do not have the python NumPy package installed, +you will need to do that now, by following the +NumPy Installation Guide. +To make sure NumPy is working for you, +you should get something like this from the interactive python prompt.

+
>>> from numpy import *                    #import numeric
+>>> a = array((1,2,3,4,5))                 #create an array
+>>> a                                      #display the array
+array([1, 2, 3, 4, 5])
+>>> a[2]                                   #index into the array
+3
+>>> a*2                                    #new array with twiced values
+array([ 2,  4,  6,  8, 10])
+
+
+

As you can see, the NumPy module gives us a new data type, the array. +This object holds an array of fixed size, and all values inside are of the same +type. The arrays can also be multidimensional, which is how we will use them +with images. There's a bit more to it than this, but it is enough to get us +started.

+

If you look at the last command above, you'll see that mathematical operations +on NumPy arrays apply to all values in the array. This is called "element-wise +operations". These arrays can also be sliced like normal lists. The slicing +syntax is the same as used on standard python objects. +(so study up if you need to :] ). +Here are some more examples of working with arrays.

+
>>> len(a)                                 #get array size
+5
+>>> a[2:]                                  #elements 2 and up
+array([3, 4, 5])
+>>> a[:-2]                                 #all except last 2
+array([1, 2, 3])
+>>> a[2:] + a[:-2]                         #add first and last
+array([4, 6, 8])
+>>> array((1,2,3)) + array((3,4))          #add arrays of wrong sizes
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+ValueError: operands could not be broadcast together with shapes (3,) (2,)
+
+
+

We get an error on the last command, because we try add together two arrays +that are different sizes. In order for two arrays two operate with each other, +including comparisons and assignment, they must have the same dimensions. It is +very important to know that the new arrays created from slicing the original all +reference the same values. So changing the values in a slice also changes the +original values. It is important how this is done.

+
>>> a                                      #show our starting array
+array([1, 2, 3, 4, 5])
+>>> aa = a[1:3]                            #slice middle 2 elements
+>>> aa                                     #show the slice
+array([2, 3])
+>>> aa[1] = 13                             #chance value in slice
+>>> a                                      #show change in original
+array([ 1, 2, 13,  4,  5])
+>>> aaa = array(a)                         #make copy of array
+>>> aaa                                    #show copy
+array([ 1, 2, 13,  4,  5])
+>>> aaa[1:4] = 0                           #set middle values to 0
+>>> aaa                                    #show copy
+array([1, 0, 0, 0, 5])
+>>> a                                      #show original again
+array([ 1, 2, 13,  4,  5])
+
+
+

Now we will look at small arrays with two +dimensions. Don't be too worried, getting started it is the same as having a +two dimensional tuple (a tuple inside a tuple). Let's get started with +two dimensional arrays.

+
>>> row1 = (1,2,3)                         #create a tuple of vals
+>>> row2 = (3,4,5)                         #another tuple
+>>> (row1,row2)                            #show as a 2D tuple
+((1, 2, 3), (3, 4, 5))
+>>> b = array((row1, row2))                #create a 2D array
+>>> b                                      #show the array
+array([[1, 2, 3],
+       [3, 4, 5]])
+>>> array(((1,2),(3,4),(5,6)))             #show a new 2D array
+array([[1, 2],
+       [3, 4],
+       [5, 6]])
+
+
+

Now with this two +dimensional array (from now on as "2D") we can index specific values +and do slicing on both dimensions. Simply using a comma to separate the indices +allows us to lookup/slice in multiple dimensions. Just using ":" as an +index (or not supplying enough indices) gives us all the values in +that dimension. Let's see how this works.

+
>>> b                                      #show our array from above
+array([[1, 2, 3],
+       [3, 4, 5]])
+>>> b[0,1]                                 #index a single value
+2
+>>> b[1,:]                                 #slice second row
+array([3, 4, 5])
+>>> b[1]                                   #slice second row (same as above)
+array([3, 4, 5])
+>>> b[:,2]                                 #slice last column
+array([3, 5])
+>>> b[:,:2]                                #slice into a 2x2 array
+array([[1, 2],
+       [3, 4]])
+
+
+

Ok, stay with me here, this is about as hard as it gets. When using NumPy +there is one more feature to slicing. Slicing arrays also allow you to specify +a slice increment. The syntax for a slice with increment is +start_index : end_index : increment.

+
>>> c = arange(10)                         #like range, but makes an array
+>>> c                                      #show the array
+array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
+>>> c[1:6:2]                               #slice odd values from 1 to 6
+array([1, 3, 5])
+>>> c[4::4]                                #slice every 4th val starting at 4
+array([4, 8])
+>>> c[8:1:-1]                              #slice 1 to 8, reversed
+array([8, 7, 6, 5, 4, 3, 2])
+
+
+

Well that is it. There's enough information there to get you started using +NumPy with the surfarray module. There's certainly a lot more to NumPy, but +this is only an introduction. Besides, we want to get on to the fun stuff, +correct?

+
+
+

Import Surfarray

+

In order to use the surfarray module we need to import it. Since both surfarray +and NumPy are optional components for pygame, it is nice to make sure they +import correctly before using them. In these examples I'm going to import +NumPy into a variable named N. This will let you know which functions +I'm using are from the NumPy package. +(and is a lot shorter than typing NumPy before each function)

+
try:
+    import numpy as N
+    import pygame.surfarray as surfarray
+except ImportError:
+    raise ImportError, "NumPy and Surfarray are required."
+
+
+
+
+

Surfarray Introduction

+

There are two main types of functions in surfarray. One set of functions for +creating an array that is a copy of a surface pixel data. The other functions +create a referenced copy of the array pixel data, so that changes to the array +directly affect the original surface. There are other functions that allow you +to access any per-pixel alpha values as arrays along with a few other helpful +functions. We will look at these other functions later on.

+

When working with these surface arrays, there are two ways of representing the +pixel values. First, they can be represented as mapped integers. This type of +array is a simple 2D array with a single integer representing the surface's +mapped color value. This type of array is good for moving parts of an image +around. The other type of array uses three RGB values to represent each pixel +color. This type of array makes it extremely simple to do types of effects that +change the color of each pixel. This type of array is also a little trickier to +deal with, since it is essentially a 3D numeric array. Still, once you get your +mind into the right mode, it is not much harder than using the normal 2D arrays.

+

The NumPy module uses a machine's natural number types to represent the data +values, so a NumPy array can consist of integers that are 8-bits, 16-bits, and 32-bits. +(the arrays can also use other types like floats and doubles, but for our image +manipulation we mainly need to worry about the integer types). +Because of this limitation of integer sizes, you must take a little extra care +that the type of arrays that reference pixel data can be properly mapped to a +proper type of data. The functions create these arrays from surfaces are:

+
+
+surfarray.pixels2d(surface)
+

Creates a 2D array (integer pixel values) that reference the original surface data. +This will work for all surface formats except 24-bit.

+
+ +
+
+surfarray.array2d(surface)
+

Creates a 2D array (integer pixel values) that is copied from any type of surface.

+
+ +
+
+surfarray.pixels3d(surface)
+

Creates a 3D array (RGB pixel values) that reference the original surface data. +This will only work on 24-bit and 32-bit surfaces that have RGB or BGR formatting.

+
+ +
+
+surfarray.array3d(surface)
+

Creates a 3D array (RGB pixel values) that is copied from any type of surface.

+
+ +

Here is a small chart that might better illustrate what types of functions +should be used on which surfaces. As you can see, both the arrayXD functions +will work with any type of surface.

+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

32-bit

24-bit

16-bit

8-bit(c-map)

pixel2d

yes

yes

yes

array2d

yes

yes

yes

yes

pixel3d

yes

yes

array3d

yes

yes

yes

yes

+
+
+

Examples

+

With this information, we are equipped to start trying things with surface +arrays. The following are short little demonstrations that create a NumPy +array and display them in pygame. These different tests are found in the +arraydemo.py example. There is a simple function named surfdemo_show +that displays an array on the screen.

+
+
+allblack +
allblack = N.zeros((128, 128))
+surfdemo_show(allblack, 'allblack')
+
+
+

Our first example creates an all black array. Whenever you need +to create a new numeric array of a specific size, it is best to use the +zeros function. Here we create a 2D array of all zeros and display +it.

+
+
+
+
+striped +
striped = N.zeros((128, 128, 3))
+striped[:] = (255, 0, 0)
+striped[:,::3] = (0, 255, 255)
+surfdemo_show(striped, 'striped')
+
+
+

Here we are dealing with a 3D array. We start by creating an all red image. +Then we slice out every third row and assign it to a blue/green color. As you +can see, we can treat the 3D arrays almost exactly the same as 2D arrays, just +be sure to assign them 3 values instead of a single mapped integer.

+
+
+
+
+rgbarray +
imgsurface = pygame.image.load('surfarray.png')
+rgbarray = surfarray.array3d(imgsurface)
+surfdemo_show(rgbarray, 'rgbarray')
+
+
+

Here we load an image with the image module, then convert it to a 3D +array of integer RGB color elements. An RGB copy of a surface always +has the colors arranged as a[r,c,0] for the red component, +a[r,c,1] for the green component, and a[r,c,2] for blue. This can then +be used without caring how the pixels of the actual surface are configured, +unlike a 2D array which is a copy of the mapped +(raw) surface pixels. We will use this image in the rest of the samples.

+
+
+
+
+flipped +
flipped = rgbarray[:,::-1]
+surfdemo_show(flipped, 'flipped')
+
+
+

Here we flip the image vertically. All we need to do is take the original +image array and slice it using a negative increment.

+
+
+
+
+scaledown +
scaledown = rgbarray[::2,::2]
+surfdemo_show(scaledown, 'scaledown')
+
+
+

Based on the last example, scaling an image down is pretty logical. We just +slice out all the pixels using an increment of 2 vertically and horizontally.

+
+
+
+
+scaleup +
shape = rgbarray.shape
+scaleup = N.zeros((shape[0]*2, shape[1]*2, shape[2]))
+scaleup[::2,::2,:] = rgbarray
+scaleup[1::2,::2,:] = rgbarray
+scaleup[:,1::2] = scaleup[:,::2]
+surfdemo_show(scaleup, 'scaleup')
+
+
+

Scaling the image up is a little more work, but is similar to the previous +scaling down, we do it all with slicing. First we create an array that is +double the size of our original. First we copy the original array into every +other pixel of the new array. Then we do it again for every other pixel doing +the odd columns. At this point we have the image scaled properly going across, +but every other row is black, so we simply need to copy each row to the one +underneath it. Then we have an image doubled in size.

+
+
+
+
+redimg +
redimg = N.array(rgbarray)
+redimg[:,:,1:] = 0
+surfdemo_show(redimg, 'redimg')
+
+
+

Now we are using 3D arrays to change the colors. Here we +set all the values in green and blue to zero. +This leaves us with just the red channel.

+
+
+
+
+soften +
factor = N.array((8,), N.int32)
+soften = N.array(rgbarray, N.int32)
+soften[1:,:]  += rgbarray[:-1,:] * factor
+soften[:-1,:] += rgbarray[1:,:] * factor
+soften[:,1:]  += rgbarray[:,:-1] * factor
+soften[:,:-1] += rgbarray[:,1:] * factor
+soften //= 33
+surfdemo_show(soften, 'soften')
+
+
+

Here we perform a 3x3 convolution filter that will soften our image. +It looks like a lot of steps here, but what we are doing is shifting +the image 1 pixel in each direction and adding them all together (with some +multiplication for weighting). Then average all the values. It's no Gaussian, +but it's fast. One point with NumPy arrays, the precision of arithmetic +operations is determined by the array with the largest data type. +So if factor was not declared as a 1 element array of type numpy.int32, +the multiplications would be performed using numpy.int8, the 8 bit integer +type of each rgbarray element. This will cause value truncation. The soften +array must also be declared to have a larger integer size than rgbarray to +avoid truncation.

+
+
+
+
+xfade +
src = N.array(rgbarray)
+dest = N.zeros(rgbarray.shape)
+dest[:] = 20, 50, 100
+diff = (dest - src) * 0.50
+xfade = src + diff.astype(N.uint)
+surfdemo_show(xfade, 'xfade')
+
+
+

Lastly, we are cross fading between the original image and a solid bluish +image. Not exciting, but the dest image could be anything, and changing the 0.50 +multiplier will let you choose any step in a linear crossfade between two images.

+
+
+
+
+

Hopefully by this point you are starting to see how surfarray can be used to +perform special effects and transformations that are only possible at the pixel +level. At the very least, you can use the surfarray to do a lot of Surface.set_at() +Surface.get_at() type operations very quickly. But don't think you are finished +yet, there is still much to learn.

+
+
+

Surface Locking

+

Like the rest of pygame, surfarray will lock any Surfaces it needs to +automatically when accessing pixel data. There is one extra thing to be aware +of though. When creating the pixel arrays, the original surface will +be locked during the lifetime of that pixel array. This is important to remember. +Be sure to "del" the pixel array or let it go out of scope +(ie, when the function returns, etc).

+

Also be aware that you really don't want to be doing much (if any) +direct pixel access on hardware surfaces (HWSURFACE). This is because +the actual surface data lives on the graphics card, and transferring pixel +changes over the PCI/AGP bus is not fast.

+
+
+

Transparency

+

The surfarray module has several methods for accessing a Surface's alpha/colorkey +values. None of the alpha functions are affected by overall transparency of a +Surface, just the pixel alpha values. Here's the list of those functions.

+
+
+surfarray.pixels_alpha(surface)
+

Creates a 2D array (integer pixel values) that references the original +surface alpha data. +This will only work on 32-bit images with an 8-bit alpha component.

+
+ +
+
+surfarray.array_alpha(surface)
+

Creates a 2D array (integer pixel values) that is copied from any +type of surface. +If the surface has no alpha values, +the array will be fully opaque values (255).

+
+ +
+
+surfarray.array_colorkey(surface)
+

Creates a 2D array (integer pixel values) that is set to transparent +(0) wherever that pixel color matches the Surface colorkey.

+
+ +
+
+

Other Surfarray Functions

+

There are only a few other functions available in surfarray. You can get a better +list with more documentation on the +surfarray reference page. +There is one very useful function though.

+
+
+surfarray.blit_array(surface, array)
+

This will transfer any type of 2D or 3D surface array onto a Surface +of the same dimensions. +This surfarray blit will generally be faster than assigning an array to a +referenced pixel array. +Still, it should not be as fast as normal Surface blitting, +since those are very optimized.

+
+ +
+
+

More Advanced NumPy

+

There's a couple last things you should know about NumPy arrays. When dealing +with very large arrays, like the kind that are 640x480 big, there are some extra +things you should be careful about. Mainly, while using the operators like + and +* on the arrays makes them easy to use, it is also very expensive on big arrays. +These operators must make new temporary copies of the array, that are then +usually copied into another array. This can get very time consuming. Fortunately, +all the NumPy operators come with special functions that can perform the +operation "in place". For example, you would want to replace +screen[:] = screen + brightmap with the much faster +add(screen, brightmap, screen). +Anyway, you'll want to read up on the NumPy UFunc +documentation for more about this. +It is important when dealing with the arrays.

+

Another thing to be aware of when working with NumPy arrays is the datatype +of the array. Some of the arrays (especially the mapped pixel type) often return +arrays with an unsigned 8-bit value. These arrays will easily overflow if you are +not careful. NumPy will use the same coercion that you find in C programs, so +mixing an operation with 8-bit numbers and 32-bit numbers will give a result as +32-bit numbers. You can convert the datatype of an array, but definitely be +aware of what types of arrays you have, if NumPy gets in a situation where +precision would be ruined, it will raise an exception.

+

Lastly, be aware that when assigning values into the 3D arrays, they must be +between 0 and 255, or you will get some undefined truncating.

+
+
+

Graduation

+

Well there you have it. My quick primer on Numeric Python and surfarray. +Hopefully now you see what is possible, and even if you never use them for +yourself, you do not have to be afraid when you see code that does. Look into +the vgrade example for more numeric array action. There are also some "flame" +demos floating around that use surfarray to create a realtime fire effect.

+

Best of all, try some things on your own. Take it slow at first and build up, +I've seen some great things with surfarray already like radial gradients and +more. Good Luck.

+
+
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/tut/chimp.py.html b/.venv/Lib/site-packages/pygame/docs/generated/tut/chimp.py.html new file mode 100644 index 00000000..44245eb1 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/tut/chimp.py.html @@ -0,0 +1,340 @@ + + + + + + + + + pygame/examples/chimp.py — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
#!/usr/bin/env python
+""" pygame.examples.chimp
+
+This simple example is used for the line-by-line tutorial
+that comes with pygame. It is based on a 'popular' web banner.
+Note there are comments here, but for the full explanation,
+follow along in the tutorial.
+"""
+
+
+# Import Modules
+import os
+import pygame as pg
+
+if not pg.font:
+    print("Warning, fonts disabled")
+if not pg.mixer:
+    print("Warning, sound disabled")
+
+main_dir = os.path.split(os.path.abspath(__file__))[0]
+data_dir = os.path.join(main_dir, "data")
+
+
+# functions to create our resources
+def load_image(name, colorkey=None, scale=1):
+    fullname = os.path.join(data_dir, name)
+    image = pg.image.load(fullname)
+    image = image.convert()
+
+    size = image.get_size()
+    size = (size[0] * scale, size[1] * scale)
+    image = pg.transform.scale(image, size)
+
+    if colorkey is not None:
+        if colorkey == -1:
+            colorkey = image.get_at((0, 0))
+        image.set_colorkey(colorkey, pg.RLEACCEL)
+    return image, image.get_rect()
+
+
+def load_sound(name):
+    class NoneSound:
+        def play(self):
+            pass
+
+    if not pg.mixer or not pg.mixer.get_init():
+        return NoneSound()
+
+    fullname = os.path.join(data_dir, name)
+    sound = pg.mixer.Sound(fullname)
+
+    return sound
+
+
+# classes for our game objects
+class Fist(pg.sprite.Sprite):
+    """moves a clenched fist on the screen, following the mouse"""
+
+    def __init__(self):
+        pg.sprite.Sprite.__init__(self)  # call Sprite initializer
+        self.image, self.rect = load_image("fist.png", -1)
+        self.fist_offset = (-235, -80)
+        self.punching = False
+
+    def update(self):
+        """move the fist based on the mouse position"""
+        pos = pg.mouse.get_pos()
+        self.rect.topleft = pos
+        self.rect.move_ip(self.fist_offset)
+        if self.punching:
+            self.rect.move_ip(15, 25)
+
+    def punch(self, target):
+        """returns true if the fist collides with the target"""
+        if not self.punching:
+            self.punching = True
+            hitbox = self.rect.inflate(-5, -5)
+            return hitbox.colliderect(target.rect)
+
+    def unpunch(self):
+        """called to pull the fist back"""
+        self.punching = False
+
+
+class Chimp(pg.sprite.Sprite):
+    """moves a monkey critter across the screen. it can spin the
+    monkey when it is punched."""
+
+    def __init__(self):
+        pg.sprite.Sprite.__init__(self)  # call Sprite initializer
+        self.image, self.rect = load_image("chimp.png", -1, 4)
+        screen = pg.display.get_surface()
+        self.area = screen.get_rect()
+        self.rect.topleft = 10, 90
+        self.move = 18
+        self.dizzy = False
+
+    def update(self):
+        """walk or spin, depending on the monkeys state"""
+        if self.dizzy:
+            self._spin()
+        else:
+            self._walk()
+
+    def _walk(self):
+        """move the monkey across the screen, and turn at the ends"""
+        newpos = self.rect.move((self.move, 0))
+        if not self.area.contains(newpos):
+            if self.rect.left < self.area.left or self.rect.right > self.area.right:
+                self.move = -self.move
+                newpos = self.rect.move((self.move, 0))
+                self.image = pg.transform.flip(self.image, True, False)
+        self.rect = newpos
+
+    def _spin(self):
+        """spin the monkey image"""
+        center = self.rect.center
+        self.dizzy = self.dizzy + 12
+        if self.dizzy >= 360:
+            self.dizzy = False
+            self.image = self.original
+        else:
+            rotate = pg.transform.rotate
+            self.image = rotate(self.original, self.dizzy)
+        self.rect = self.image.get_rect(center=center)
+
+    def punched(self):
+        """this will cause the monkey to start spinning"""
+        if not self.dizzy:
+            self.dizzy = True
+            self.original = self.image
+
+
+def main():
+    """this function is called when the program starts.
+    it initializes everything it needs, then runs in
+    a loop until the function returns."""
+    # Initialize Everything
+    pg.init()
+    screen = pg.display.set_mode((1280, 480), pg.SCALED)
+    pg.display.set_caption("Monkey Fever")
+    pg.mouse.set_visible(False)
+
+    # Create The Background
+    background = pg.Surface(screen.get_size())
+    background = background.convert()
+    background.fill((170, 238, 187))
+
+    # Put Text On The Background, Centered
+    if pg.font:
+        font = pg.font.Font(None, 64)
+        text = font.render("Pummel The Chimp, And Win $$$", True, (10, 10, 10))
+        textpos = text.get_rect(centerx=background.get_width() / 2, y=10)
+        background.blit(text, textpos)
+
+    # Display The Background
+    screen.blit(background, (0, 0))
+    pg.display.flip()
+
+    # Prepare Game Objects
+    whiff_sound = load_sound("whiff.wav")
+    punch_sound = load_sound("punch.wav")
+    chimp = Chimp()
+    fist = Fist()
+    allsprites = pg.sprite.RenderPlain((chimp, fist))
+    clock = pg.time.Clock()
+
+    # Main Loop
+    going = True
+    while going:
+        clock.tick(60)
+
+        # Handle Input Events
+        for event in pg.event.get():
+            if event.type == pg.QUIT:
+                going = False
+            elif event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE:
+                going = False
+            elif event.type == pg.MOUSEBUTTONDOWN:
+                if fist.punch(chimp):
+                    punch_sound.play()  # punch
+                    chimp.punched()
+                else:
+                    whiff_sound.play()  # miss
+            elif event.type == pg.MOUSEBUTTONUP:
+                fist.unpunch()
+
+        allsprites.update()
+
+        # Draw Everything
+        screen.blit(background, (0, 0))
+        allsprites.draw(screen)
+        pg.display.flip()
+
+    pg.quit()
+
+
+# Game Over
+
+
+# this calls the 'main' function when this script is executed
+if __name__ == "__main__":
+    main()
+
+
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/tut/newbieguide.html b/.venv/Lib/site-packages/pygame/docs/generated/tut/newbieguide.html new file mode 100644 index 00000000..5b296b34 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/tut/newbieguide.html @@ -0,0 +1,544 @@ + + + + + + + + + A Newbie Guide to pygame — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

A Newbie Guide to pygame

+

or Things I learned by trial and error so you don't have to,

+

or How I learned to stop worrying and love the blit.

+

Pygame is a python wrapper for SDL, written by Pete Shinners. What this +means is that, using pygame, you can write games or other multimedia +applications in Python that will run unaltered on any of SDL's supported +platforms (Windows, Linux, Mac, and others).

+

Pygame may be easy to learn, but the world of graphics programming can be +pretty confusing to the newcomer. I wrote this to try to distill the practical +knowledge I've gained over the past year or so of working with pygame, and its +predecessor, PySDL. I've tried to rank these suggestions in order of +importance, but how relevant any particular hint is will depend on your own +background and the details of your project.

+
+

Get comfortable working in Python.

+

The most important thing is to feel confident using python. Learning something +as potentially complicated as graphics programming will be a real chore if +you're also unfamiliar with the language you're using. Write a few sizable +non-graphical programs in python -- parse some text files, write a guessing +game or a journal-entry program or something. Get comfortable with string and +list manipulation -- know how to split, slice and combine strings and lists. +Know how import works -- try writing a program that is spread across +several source files. Write your own functions, and practice manipulating +numbers and characters; know how to convert between the two. Get to the point +where the syntax for using lists and dictionaries is second-nature -- you don't +want to have to run to the documentation every time you need to slice a list or +sort a set of keys. Get comfortable using file paths -- this will come in handy +later when you start loading assets and creating save files.

+

Resist the temptation to ask for direct help online when +you run into trouble. Instead, fire up the interpreter and play with the +problem for a few hours, or use print statements and debugging tools to find out +what's going wrong in your code. Get into the habit of looking things up in the +official Python documentation, and Googling error messages to figure out what +they mean.

+

This may sound incredibly dull, but the confidence you'll gain through your +familiarity with python will work wonders when it comes time to write your +game. The time you spend making python code second-nature will be nothing +compared to the time you'll save when you're writing real code.

+
+
+

Recognize which parts of pygame you really need.

+

Looking at the jumble of classes at the top of the pygame documentation index +may be confusing. The important thing is to realize that you can do a great +deal with only a tiny subset of functions. Many classes you'll probably never +use -- in a year, I haven't touched the Channel, Joystick, cursors, +surfarray or version functions.

+
+
+

Know what a surface is.

+

The most important part of pygame is the surface. Just think of a surface as a +blank piece of paper. You can do a lot of things with a surface -- you can +draw lines on it, fill parts of it with color, copy images to and from it, and +set or read individual pixel colors on it. A surface can be any size (within +reason) and you can have as many of them as you like (again, within reason). +One surface is special -- the one you create with +pygame.display.set_mode()Initialize a window or screen for display. This 'display surface' represents the screen; +whatever you do to it will appear on the user's screen.

+

So how do you create surfaces? As mentioned above, you create the special +'display surface' with pygame.display.set_mode(). You can create a surface +that contains an image by using pygame.image.load()load new image from a file (or file-like object), or you can make a surface +that contains text with pygame.font.Font.render()draw text on a new Surface. You can even create a surface that +contains nothing at all with pygame.Surface()pygame object for representing images.

+

Most of the surface functions are not critical. Just learn Surface.blit(), +Surface.fill(), Surface.set_at() and Surface.get_at(), and you'll be fine.

+
+
+

Use Surface.convert().

+

When I first read the documentation for Surface.convert(), I didn't think +it was something I had to worry about. 'I only use PNGs, therefore everything I +do will be in the same format. So I don't need convert()';. It turns out I +was very, very wrong.

+

The 'format' that convert() refers to isn't the file format (i.e. PNG, +JPEG, GIF), it's what's called the 'pixel format'. This refers to the +particular way that a surface records individual colors in a specific pixel. +If the surface format isn't the same as the display format, SDL will have to +convert it on-the-fly for every blit -- a fairly time-consuming process. Don't +worry too much about the explanation; just note that convert() is necessary +if you want to get any kind of speed out of your blits.

+

How do you use convert? Just call it after creating a surface with the +image.load() function. Instead of just doing:

+
surface = pygame.image.load('foo.png')
+
+
+

Do:

+
surface = pygame.image.load('foo.png').convert()
+
+
+

It's that easy. You just need to call it once per surface, when you load an +image off the disk. You'll be pleased with the results; I see about a 6x +increase in blitting speed by calling convert().

+

The only times you don't want to use convert() is when you really need to +have absolute control over an image's internal format -- say you were writing +an image conversion program or something, and you needed to ensure that the +output file had the same pixel format as the input file. If you're writing a +game, you need speed. Use convert().

+
+
+

Be wary of outdated, obsolete, and optional advice.

+

Pygame has been around since the early 2000s, and a lot has changed since then -- +both within the framework itself and within the broader computing landscape as a +whole. Make sure to check the dates on materials you read (including this guide!), +and take older advice with a grain of salt. Here are some common things that +stick out to me:

+

Dirty Rects & performance 'tricks'

+

When you read older bits of pygame documentation or guides online, you may see +some emphasis on only updating portions of the screen that are dirty, for the +sake of performance (in this context, "dirty" means the region has changed since +the previous frame was drawn).

+

Generally this entails calling pygame.display.update()Update portions of the screen for software displays (with a list of +rects) instead of pygame.display.flip()Update the full display Surface to the screen, not having scrolling backgrounds, +or even not filling the screen with a background color every frame because pygame +supposedly can't handle it. Some of pygame's API is designed to support this +paradigm as well (e.g. pygame.sprite.RenderUpdates()Group sub-class that tracks dirty updates.), which made a lot of +sense in the early years of pygame.

+

In the present day (2022) though, most modest desktop computers are powerful enough to +refresh the entire display once per frame at 60 FPS and beyond. You can have a moving +camera, or dynamic backgrounds and your game should run totally fine at 60 FPS. CPUs are +more powerful nowadays, and you can use display.flip() without fear.

+

That being said there are still some times when this old technique is still useful +for squeezing out a few extra FPS. For example, with a single screen game like +an Asteroids or Space Invaders. Here is the rough process for how it works:

+

Instead of updating the whole screen every frame, only the parts that changed since +the last frame are updated. You do this by keeping track of those rectangles in a list, +then calling update(the_dirty_rectangles) at the end of the frame. In detail +for a moving sprite:

+
+
    +
  • Blit a piece of the background over the sprite's current location, erasing it.

  • +
  • Append the sprite's current location rectangle to a list called dirty_rects.

  • +
  • Move the sprite.

  • +
  • Draw the sprite at its new location.

  • +
  • Append the sprite's new location to my dirty_rects list.

  • +
  • Call display.update(dirty_rects)

  • +
+
+

Even though this technique is not required for making performant 2D games with +modern CPUs, it is still useful to be aware of. There are also still plenty of other ways +to accidentally tank your game's performance with poorly optimized rendering logic. +For example, even on modern hardware it's probably too slow to call set_at once per pixel +on the display surface. Being mindful of performance is still something you'll have to +do.

+

There just aren't that many 'one neat trick to fix your code performance' tips. Every game +is different and there are different problems and different algorithms to solve them +efficiently in each type of game. Pretty much every time your 2D game code is failing to hit a +reasonable frame rate the underlying cause turns out to be bad algorithm or a misunderstanding +of fundamental game design patterns.

+

If you are having performance problems, first make sure you aren't loading files repeatedly in your +game loop, then use one of the many options for profiling your code to find out what is taking up the +most time. Once you are armed with at least some knowledge on why your game is slow, try asking the +internet (via google), or the pygame community if they've got some better algorithms to help you out.

+

HWSURFACE and DOUBLEBUF

+

The HWSURFACE display.set_mode() flag does nothing in pygame versions 2.0.0 and +later (you can check the docs if you don't believe me)! There's no reason to +use it anymore. Even in pygame 1, its effect is pretty nuanced and +generally misunderstood by most pygame users. It was never a magic speed-up +flag, unfortunately.

+

DOUBLEBUF still has some use, but is also not a magic speed up flag.

+

The Sprite class

+

You don't need to use the built-in Sprite or Group classes +if you don't want to. In a lot of tutorials, it may seem like Sprite is the +fundamental "GameObject" of pygame, from which all other objects must derive, +but in reality it's pretty much just a wrapper around a Rect and a +Surface, with some additional convenience methods. You may find it +more intuitive (and fun) to write your game's core logic and classes from +scratch.

+
+
+

There is NO rule six.

+
+
+

Don't get distracted by side issues.

+

Sometimes, new game programmers spend too much time worrying about issues that +aren't really critical to their game's success. The desire to get secondary +issues 'right' is understandable, but early in the process of creating a game, +you cannot even know what the important questions are, let alone what answers +you should choose. The result can be a lot of needless prevarication.

+

For example, consider the question of how to organize your graphics files. +Should each frame have its own graphics file, or each sprite? Perhaps all the +graphics should be zipped up into one archive? A great deal of time has been +wasted on a lot of projects, asking these questions on mailing lists, debating +the answers, profiling, etc, etc. This is a secondary issue; any time spent +discussing it should have been spent coding the actual game.

+

The insight here is that it is far better to have a 'pretty good' solution that +was actually implemented, than a perfect solution that you never got around to +writing.

+
+
+

Rects are your friends.

+

Pete Shinners' wrapper may have cool alpha effects and fast blitting speeds, +but I have to admit my favorite part of pygame is the lowly Rect class. +A rect is simply a rectangle -- defined only by the position of its top left +corner, its width, and its height. Many pygame functions take rects as +arguments, and they also take 'rectstyles', a sequence that has the same values +as a rect. So if I need a rectangle that defines the area between 10, 20 and +40, 50, I can do any of the following:

+
rect = pygame.Rect(10, 20, 30, 30)
+rect = pygame.Rect((10, 20, 30, 30))
+rect = pygame.Rect((10, 20), (30, 30))
+rect = (10, 20, 30, 30)
+rect = ((10, 20, 30, 30))
+
+
+

If you use any of the first three versions, however, you get access to Rect's +utility functions. These include functions to move, shrink and inflate rects, +find the union of two rects, and a variety of collision-detection functions.

+

For example, suppose I'd like to get a list of all the sprites that contain a +point (x, y) -- maybe the player clicked there, or maybe that's the current +location of a bullet. It's simple if each sprite has a .rect member -- I just +do:

+
sprites_clicked = [sprite for sprite in all_my_sprites_list if sprite.rect.collidepoint(x, y)]
+
+
+

Rects have no other relation to surfaces or graphics functions, other than the +fact that you can use them as arguments. You can also use them in places that +have nothing to do with graphics, but still need to be defined as rectangles. +Every project I discover a few new places to use rects where I never thought +I'd need them.

+
+
+

Don't bother with pixel-perfect collision detection.

+

So you've got your sprites moving around, and you need to know whether or not +they're bumping into one another. It's tempting to write something like the +following:

+
+
    +
  • Check to see if the rects are in collision. If they aren't, ignore them.

  • +
  • For each pixel in the overlapping area, see if the corresponding pixels from both sprites are opaque. If so, there's a collision.

  • +
+
+

There are other ways to do this, with ANDing sprite masks and so on, but any +way you do it in pygame, it's probably going to be too slow. For most games, +it's probably better just to do 'sub-rect collision' -- create a rect for each +sprite that's a little smaller than the actual image, and use that for +collisions instead. It will be much faster, and in most cases the player won't +notice the imprecision.

+
+
+

Managing the event subsystem.

+

Pygame's event system is kind of tricky. There are actually two different ways +to find out what an input device (keyboard, mouse or joystick) is doing.

+

The first is by directly checking the state of the device. You do this by +calling, say, pygame.mouse.get_pos()get the mouse cursor position or pygame.key.get_pressed()get the state of all keyboard buttons. +This will tell you the state of that device at the moment you call the +function.

+

The second method uses the SDL event queue. This queue is a list of events -- +events are added to the list as they're detected, and they're deleted from the +queue as they're read off.

+

There are advantages and disadvantages to each system. State-checking (system +1) gives you precision -- you know exactly when a given input was made -- if +mouse.get_pressed([0]) is 1, that means that the left mouse button is +down right at this moment. The event queue merely reports that the +mouse was down at some time in the past; if you check the queue fairly often, +that can be ok, but if you're delayed from checking it by other code, input +latency can grow. Another advantage of the state-checking system is that it +detects "chording" easily; that is, several states at the same time. If you +want to know whether the t and f keys are down at the same time, just +check:

+
if key.get_pressed[K_t] and key.get_pressed[K_f]:
+    print("Yup!")
+
+
+

In the queue system, however, each keypress arrives in the queue as a +completely separate event, so you'd need to remember that the t key was +down, and hadn't come up yet, while checking for the f key. A little more +complicated.

+

The state system has one great weakness, however. It only reports what the +state of the device is at the moment it's called; if the user hits a mouse +button then releases it just before a call to mouse.get_pressed(), the +mouse button will return 0 -- get_pressed() missed the mouse button press +completely. The two events, MOUSEBUTTONDOWN and MOUSEBUTTONUP, will +still be sitting in the event queue, however, waiting to be retrieved and +processed.

+

The lesson is: choose the system that meets your requirements. If you don't +have much going on in your loop -- say you're just sitting in a while True +loop, waiting for input, use get_pressed() or another state function; the +latency will be lower. On the other hand, if every keypress is crucial, but +latency isn't as important -- say your user is typing something in an editbox, +use the event queue. Some key presses may be slightly late, but at least you'll +get them all.

+

A note about event.poll() vs. wait() -- poll() may seem better, +since it doesn't block your program from doing anything while it's waiting for +input -- wait() suspends the program until an event is received. +However, poll() will consume 100% of available CPU time while it runs, +and it will fill the event queue with NOEVENTS. Use set_blocked() to +select just those event types you're interested in -- your queue will be much +more manageable.

+

Another note about the event queue -- even if you don't want to use it, you must +still clear it periodically because it's still going to be filling up with events +in the background as the user presses keys and mouses over the window. On Windows, +if your game goes too long without clearing the queue, the operating system will +think it has frozen and show a "The application is not responding" message. +Iterating over event.get() or simply calling event.clear() once per frame +will avoid this.

+
+
+

Colorkey vs. Alpha.

+

There's a lot of confusion around these two techniques, and much of it comes +from the terminology used.

+

'Colorkey blitting' involves telling pygame that all pixels of a certain color +in a certain image are transparent instead of whatever color they happen to be. +These transparent pixels are not blitted when the rest of the image is blitted, +and so don't obscure the background. This is how we make sprites that aren't +rectangular in shape. Simply call Surface.set_colorkey(), and pass in +an RGB tuple -- say (0,0,0). This would make every pixel in the source image +transparent instead of black.

+

'Alpha' is different, and it comes in two flavors. 'Image alpha' applies to the +whole image, and is probably what you want. Properly known as 'translucency', +alpha causes each pixel in the source image to be only partially opaque. +For example, if you set a surface's alpha to 192 and then blitted it onto a +background, 3/4 of each pixel's color would come from the source image, and 1/4 +from the background. Alpha is measured from 255 to 0, where 0 is completely +transparent, and 255 is completely opaque. Note that colorkey and alpha +blitting can be combined -- this produces an image that is fully transparent in +some spots, and semi-transparent in others.

+

'Per-pixel alpha' is the other flavor of alpha, and it's more complicated. +Basically, each pixel in the source image has its own alpha value, from 0 to +255. Each pixel, therefore, can have a different opacity when blitted onto a +background. This type of alpha can't be mixed with colorkey blitting, +and it overrides per-image alpha. Per-pixel alpha is rarely used in +games, and to use it you have to save your source image in a graphic +editor with a special alpha channel. It's complicated -- don't use it +yet.

+
+
+

Software architecture, design patterns, and games.

+

You may reach a point where you're comfortable writing code, you're able to solve +complex problems without assistance, you understand how to use most of pygame's +modules, and yet, as you work on larger projects they always seem to get messier +and harder to maintain as time goes on. This can manifest in many ways -- for +example, fixing bugs in one place might always seem to create new bugs elsewhere, +figuring out where code should go might become a challenge, adding new +things might frequently require you to rewrite many other things, and so on. +Finally, you decide to cut your losses and start fresh on something new.

+

This is a common issue and it can be frustrating -- on the one hand, your +programming skills are improving, and yet you aren't able to finish the games +you start due to somewhat nebulous organizational problems.

+

This brings us to the concept of software architecture and design patterns. You +may be familiar with pygame's "standard" base template (there are many equivalent +variations of this, so don't stress about the small details too much):

+
import pygame
+
+pygame.init()
+
+screen = pygame.display.set_mode((1280,720))
+
+clock = pygame.time.Clock()
+
+while True:
+    # Process player inputs.
+    for event in pygame.event.get():
+        if event.type == pygame.QUIT:
+            pygame.quit()
+            raise SystemExit
+
+    # Do logical updates here.
+    # ...
+
+    screen.fill("purple")  # Fill the display with a solid color
+
+    # Render the graphics here.
+    # ...
+
+    pygame.display.flip()  # Refresh on-screen display
+    clock.tick(60)         # wait until next frame (at 60 FPS)
+
+
+

It does some initial setup, starts a loop, and then proceeds to repeatedly +collect input, handle the game's logic, and draw the current frame forever until +the program ends. The update, render, wait loop shown here is actually a design +pattern that serves as the skeleton of most games -- it's prolific because it's +clean, it's organized, and it works. (There's also an important but easy-to-miss +design feature here in the form of a strict division between the game's logic +and rendering routines. This decision alone prevents a whole category of potential +bugs related to objects updating and rendering concurrently, which is nice).

+

It turns out that there are many design patterns like this that are used frequently +in games and in software development at large. For a great resource on this +specifically for games, I highly recommend Game Programming Patterns, a short +free, e-book on the topic. It covers a bunch of useful patterns and concrete situations +where you might want to employ them. It won't instantly make you a better coder, +but learning some theory about software architecture can go a long way towards +helping you escape plateaus and tackle larger projects more confidently.

+
+
+

Do things the pythony way.

+

A final note (this isn't the least important one; it just comes at the end). +Pygame is a pretty lightweight wrapper around SDL, which is in turn a pretty +lightweight wrapper around your native OS graphics calls. Chances are pretty +good that if your code is still slow, and you've done the things I've mentioned +above, then the problem lies in the way you're addressing your data in python. +Certain idioms are just going to be slow in python no matter what you do. +Luckily, python is a very clear language -- if a piece of code looks awkward or +unwieldy, chances are its speed can be improved, too. Read over Why Pygame is +Slow for some deeper insight into why pygame might be considered slower than +other frameworks/engines, and what that actually means in practice. +And if you're truly stumped by performance problems, profilers like cProfile +(or SnakeViz, a visualizer for cProfile) can help identify bottlenecks (they'll +tell you which parts of the code are taking the longest to execute). That said, +premature optimisation is the root of all evil; if it's already fast enough, +don't torture the code trying to make it faster. If it's fast enough, let it +be :)

+

There you go. Now you know practically everything I know about using pygame. +Now, go write that game!

+
+

David Clark is an avid pygame user and the editor of the Pygame Code +Repository, a showcase for community-submitted python game code. He is also +the author of Twitch, an entirely average pygame arcade game.

+

This guide was substantially updated in 2022.

+
+
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/tut/tom_games2.html b/.venv/Lib/site-packages/pygame/docs/generated/tut/tom_games2.html new file mode 100644 index 00000000..47aa3122 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/tut/tom_games2.html @@ -0,0 +1,238 @@ + + + + + + + + + Revision: Pygame fundamentals — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

2. Revision: Pygame fundamentals

+
+

2.1. The basic Pygame game

+

For the sake of revision, and to ensure that you are familiar with the basic structure of a Pygame program, I'll briefly run through +a basic Pygame program, which will display no more than a window with some text in it, that should, by the end, look something like +this (though of course the window decoration will probably be different on your system):

+../_images/tom_basic.png +

The full code for this example looks like this:

+
#!/usr/bin/python
+
+import pygame
+from pygame.locals import *
+
+def main():
+    # Initialise screen
+    pygame.init()
+    screen = pygame.display.set_mode((150, 50))
+    pygame.display.set_caption('Basic Pygame program')
+
+    # Fill background
+    background = pygame.Surface(screen.get_size())
+    background = background.convert()
+    background.fill((250, 250, 250))
+
+    # Display some text
+    font = pygame.font.Font(None, 36)
+    text = font.render("Hello There", 1, (10, 10, 10))
+    textpos = text.get_rect()
+    textpos.centerx = background.get_rect().centerx
+    background.blit(text, textpos)
+
+    # Blit everything to the screen
+    screen.blit(background, (0, 0))
+    pygame.display.flip()
+
+    # Event loop
+    while True:
+        for event in pygame.event.get():
+            if event.type == QUIT:
+                return
+
+        screen.blit(background, (0, 0))
+        pygame.display.flip()
+
+
+if __name__ == '__main__': main()
+
+
+
+
+

2.2. Basic Pygame objects

+

As you can see, the code consists of three main objects: the screen, the background, and the text. Each of these objects is created +by first calling an instance of an in-built Pygame object, and then modifying it to fit our needs. The screen is a slightly special +case, because we still modify the display through Pygame calls, rather than calling methods belonging to the screen object. But for +all other Pygame objects, we first create the object as a copy of a Pygame object, giving it some attributes, and build our game +objects from them.

+

With the background, we first create a Pygame Surface object, and make it the size of the screen. We then perform the convert() +operation to convert the Surface to a single pixel format. This is more obviously necessary when we have several images and surfaces, +all of different pixel formats, which makes rendering them quite slow. By converting all the surfaces, we can drastically speed up +rendering times. Finally, we fill the background surface with white (255, 255, 255). These values are RGB (Red Green +Blue), and can be worked out from any good paint program.

+

With the text, we require more than one object. First, we create a font object, which defines which font to use, and the size of the +font. Then we create a text object, by using the render method that belongs to our font object, supplying three arguments: +the text to be rendered, whether or not it should be anti-aliased (1=yes, 0=no), and the color of the text (again in RGB format). Next +we create a third text object, which gets the rectangle for the text. The easiest way to understand this is to imagine drawing a +rectangle that will surround all of the text; you can then use this rectangle to get/set the position of the text on the screen. So +in this example we get the rectangle, set its centerx attribute to be the centerx attribute of the +background (so the text's center will be the same as the background's center, i.e. the text will be centered on the screen on the x +axis). We could also set the y coordinate, but it's not any different so I left the text at the top of the screen. As the screen is +small anyway, it didn't seem necessary.

+
+
+

2.3. Blitting

+

Now we have created our game objects, we need to actually render them. If we didn't and we ran the program, we'd just see a +blank window, and the objects would remain invisible. The term used for rendering objects is blitting, which is where +you copy the pixels belonging to said object onto the destination object. So to render the background object, you blit it onto the +screen. In this example, to make things simple, we blit the text onto the background (so the background will now have a copy of the +text on it), and then blit the background onto the screen.

+

Blitting is one of the slowest operations in any game, so you need to be careful not to blit too much onto the screen in every frame. +If you have a background image, and a ball flying around the screen, then you could blit the background and then the ball in every +frame, which would cover up the ball's previous position and render the new ball, but this would be pretty slow. A better solution is +to blit the background onto the area that the ball previously occupied, which can be found by the ball's previous rectangle, and then +blitting the ball, so that you are only blitting two small areas.

+
+
+

2.4. The event loop

+

Once you've set the game up, you need to put it into a loop so that it will continuously run until the user signals that he/she wants +to exit. So you start an open while loop, and then for each iteration of the loop, which will be each frame of the game, +update the game. The first thing is to check for any Pygame events, which will be the user hitting the keyboard, clicking a mouse +button, moving a joystick, resizing the window, or trying to close it. In this case, we simply want to watch out for for user trying +to quit the game by closing the window, in which case the game should return, which will end the while loop. +Then we simply need to re-blit the background, and flip (update) the display to have everything drawn. OK, as nothing moves or happens +in this example, we don't strictly speaking need to re-blit the background in every iteration, but I put it in because when things are +moving around on the screen, you will need to do all your blitting here.

+
+
+

2.5. Ta-da!

+

And that's it - your most basic Pygame game! All games will take a form similar to this, but with lots more code for the actual game +functions themselves, which are more to do your with programming, and less guided in structure by the workings of Pygame. This is what +this tutorial is really about, and will now go onto.

+
+
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/tut/tom_games3.html b/.venv/Lib/site-packages/pygame/docs/generated/tut/tom_games3.html new file mode 100644 index 00000000..feec1e21 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/tut/tom_games3.html @@ -0,0 +1,218 @@ + + + + + + + + + Kicking things off — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

3. Kicking things off

+

The first sections of code are relatively simple, and, once written, can usually be reused in every game you consequently make. They +will do all of the boring, generic tasks like loading modules, loading images, opening networking connections, playing music, and so +on. They will also include some simple but effective error handling, and any customisation you wish to provide on top of functions +provided by modules like sys and pygame.

+
+

3.1. The first lines, and loading modules

+

First off, you need to start off your game and load up your modules. It's always a good idea to set a few things straight at the top of +the main source file, such as the name of the file, what it contains, the license it is under, and any other helpful info you might +want to give those who will be looking at it. Then you can load modules, with some error checking so that Python doesn't print out +a nasty traceback, which non-programmers won't understand. The code is fairly simple, so I won't bother explaining any of it:

+
#!/usr/bin/env python
+#
+# Tom's Pong
+# A simple pong game with realistic physics and AI
+# http://tomchance.org.uk/projects/pong
+#
+# Released under the GNU General Public License
+
+VERSION = "0.4"
+
+try:
+    import sys
+    import random
+    import math
+    import os
+    import getopt
+    import pygame
+    from socket import *
+    from pygame.locals import *
+except ImportError, err:
+    print(f"couldn't load module. {err}")
+    sys.exit(2)
+
+
+
+
+

3.2. Resource handling functions

+

In the Line By Line Chimp example, the first code to be written was for loading images and sounds. As these +were totally independent of any game logic or game objects, they were written as separate functions, and were written first so +that later code could make use of them. I generally put all my code of this nature first, in their own, classless functions; these +will, generally speaking, be resource handling functions. You can of course create classes for these, so that you can group them +together, and maybe have an object with which you can control all of your resources. As with any good programming environment, it's up +to you to develop your own best practice and style.

+

It's always a good idea to write your own resource handling functions, +because although Pygame has methods for opening images and sounds, and other modules will have their methods of opening other +resources, those methods can take up more than one line, they can require consistent modification by yourself, and they often don't +provide satisfactory error handling. Writing resource handling functions gives you sophisticated, reusable code, and gives you more +control over your resources. Take this example of an image loading function:

+
def load_png(name):
+    """ Load image and return image object"""
+    fullname = os.path.join("data", name)
+    try:
+        image = pygame.image.load(fullname)
+        if image.get_alpha() is None:
+            image = image.convert()
+        else:
+            image = image.convert_alpha()
+    except FileNotFoundError:
+        print(f"Cannot load image: {fullname}")
+        raise SystemExit
+    return image, image.get_rect()
+
+
+

Here we make a more sophisticated image loading function than the one provided by pygame.image.load()load new image from a file (or file-like object). Note that +the first line of the function is a documentation string describing what the function does, and what object(s) it returns. The +function assumes that all of your images are in a directory called data, and so it takes the filename and creates the full pathname, +for example data/ball.png, using the os module to ensure cross-platform compatibility. Then it +tries to load the image, and convert any alpha regions so you can achieve transparency, and it returns a more human-readable error +if there's a problem. Finally it returns the image object, and its rect.

+

You can make similar functions for loading any other resources, such as loading sounds. You can also make resource handling classes, +to give you more flexibility with more complex resources. For example, you could make a music class, with an __init__ +function that loads the sound (perhaps borrowing from a load_sound() function), a function to pause the music, and a +function to restart. Another handy resource handling class is for network connections. Functions to open sockets, pass data with +suitable security and error checking, close sockets, finger addresses, and other network tasks, can make writing a game with network +capabilities relatively painless.

+

Remember the chief task of these functions/classes is to ensure that by the time you get around to writing game object classes, +and the main loop, there's almost nothing left to do. Class inheritance can make these basic classes especially handy. Don't go +overboard though; functions which will only be used by one class should be written as part of that class, not as a global +function.

+
+
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/tut/tom_games4.html b/.venv/Lib/site-packages/pygame/docs/generated/tut/tom_games4.html new file mode 100644 index 00000000..a9adc0ad --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/tut/tom_games4.html @@ -0,0 +1,247 @@ + + + + + + + + + Game object classes — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

4. Game object classes

+

Once you've loaded your modules, and written your resource handling functions, you'll want to get on to writing some game objects. +The way this is done is fairly simple, though it can seem complex at first. You write a class for each type of object in the game, +and then create an instance of those classes for the objects. You can then use those classes' methods to manipulate the objects, +giving objects some motion and interactive capabilities. So your game, in pseudo-code, will look like this:

+
#!/usr/bin/python
+
+# [load modules here]
+
+# [resource handling functions here]
+
+class Ball:
+    # [ball functions (methods) here]
+    # [e.g. a function to calculate new position]
+    # [and a function to check if it hits the side]
+
+def main:
+    # [initiate game environment here]
+
+    # [create new object as instance of ball class]
+    ball = Ball()
+
+    while True:
+        # [check for user input]
+
+        # [call ball's update function]
+        ball.update()
+
+
+

This is, of course, a very simple example, and you'd need to put in all the code, instead of those little bracketed comments. But +you should get the basic idea. You create a class, into which you put all the functions for a ball, including __init__, +which would create all the ball's attributes, and update, which would move the ball to its new position, before blitting +it onto the screen in this position.

+

You can then create more classes for all of your other game objects, and then create instances of them so that you can handle them +easily in the main function and the main program loop. Contrast this with initiating the ball in the main +function, and then having lots of classless functions to manipulate a set ball object, and you'll hopefully see why using classes is +an advantage: It allows you to put all of the code for each object in one place; it makes using objects easier; it makes adding new +objects, and manipulating them, more flexible. Rather than adding more code for each new ball object, you could simply create new +instances of the Ball class for each new ball object. Magic!

+
+

4.1. A simple ball class

+

Here is a simple class with the functions necessary for creating a ball object that will, if the update function is called +in the main loop, move across the screen:

+
class Ball(pygame.sprite.Sprite):
+    """A ball that will move across the screen
+    Returns: ball object
+    Functions: update, calcnewpos
+    Attributes: area, vector"""
+
+    def __init__(self, vector):
+        pygame.sprite.Sprite.__init__(self)
+        self.image, self.rect = load_png('ball.png')
+        screen = pygame.display.get_surface()
+        self.area = screen.get_rect()
+        self.vector = vector
+
+    def update(self):
+        newpos = self.calcnewpos(self.rect,self.vector)
+        self.rect = newpos
+
+    def calcnewpos(self,rect,vector):
+        (angle,z) = vector
+        (dx,dy) = (z*math.cos(angle),z*math.sin(angle))
+        return rect.move(dx,dy)
+
+
+

Here we have the Ball class, with an __init__ function that sets the ball up, an update +function that changes the ball's rectangle to be in the new position, and a calcnewpos function to calculate the ball's +new position based on its current position, and the vector by which it is moving. I'll explain the physics in a moment. The one other +thing to note is the documentation string, which is a little bit longer this time, and explains the basics of the class. These strings +are handy not only to yourself and other programmers looking at the code, but also for tools to parse your code and document it. They +won't make much of a difference in small programs, but with large ones they're invaluable, so it's a good habit to get into.

+
+

4.1.1. Diversion 1: Sprites

+

The other reason for creating a class for each object is sprites. Each image you render in your game will be a sprite object, and so +to begin with, the class for each object should inherit the Sprite class. +This is a really nice feature of Python - class +inheritance. Now the Ball class has all of the functions that come with the Sprite class, and any object +instances of the Ball class will be registered by Pygame as sprites. Whereas with text and the background, which don't +move, it's OK to blit the object onto the background, Pygame handles sprite objects in a different manner, which you'll see when we +look at the whole program's code.

+

Basically, you create both a ball object, and a sprite object for that ball, and you then call the ball's update function on the +sprite object, thus updating the sprite. Sprites also give you sophisticated ways of determining if two objects have collided. +Normally you might just check in the main loop to see if their rectangles overlap, but that would involve a lot of code, which would +be a waste because the Sprite class provides two functions (spritecollide and groupcollide) +to do this for you.

+
+
+

4.1.2. Diversion 2: Vector physics

+

Other than the structure of the Ball class, the notable thing about this code is the vector physics, used to calculate +the ball's movement. With any game involving angular movement, you won't get very far unless you're comfortable with trigonometry, so +I'll just introduce the basics you need to know to make sense of the calcnewpos function.

+

To begin with, you'll notice that the ball has an attribute vector, which is made up of angle and z. +The angle is measured in radians, and will give you the direction in which the ball is moving. Z is the speed at which the ball +moves. So by using this vector, we can determine the direction and speed of the ball, and therefore how much it will move on the x and +y axes:

+../_images/tom_radians.png +

The diagram above illustrates the basic maths behind vectors. In the left hand diagram, you can see the ball's projected movement +represented by the blue line. The length of that line (z) represents its speed, and the angle is the direction in which +it will move. The angle for the ball's movement will always be taken from the x axis on the right, and it is measured clockwise from +that line, as shown in the diagram.

+

From the angle and speed of the ball, we can then work out how much it has moved along the x and y axes. We need to do this because +Pygame doesn't support vectors itself, and we can only move the ball by moving its rectangle along the two axes. So we need to +resolve the angle and speed into its movement on the x axis (dx) and on the y axis (dy). This is a simple matter of +trigonometry, and can be done with the formulae shown in the diagram.

+

If you've studied elementary trigonometry before, none of this should be news to you. But just in case you're forgetful, here are some +useful formulae to remember, that will help you visualise the angles (I find it easier to visualise angles in degrees than in radians!)

+../_images/tom_formulae.png +
+
+
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/tut/tom_games5.html b/.venv/Lib/site-packages/pygame/docs/generated/tut/tom_games5.html new file mode 100644 index 00000000..815b18df --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/tut/tom_games5.html @@ -0,0 +1,236 @@ + + + + + + + + + User-controllable objects — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

5. User-controllable objects

+

So far you can create a Pygame window, and render a ball that will fly across the screen. The next step is to make some bats which +the user can control. This is potentially far more simple than the ball, because it requires no physics (unless your user-controlled +object will move in ways more complex than up and down, e.g. a platform character like Mario, in which case you'll need more physics). +User-controllable objects are pretty easy to create, thanks to Pygame's event queue system, as you'll see.

+
+

5.1. A simple bat class

+

The principle behind the bat class is similar to that of the ball class. You need an __init__ function to initialise the +ball (so you can create object instances for each bat), an update function to perform per-frame changes on the bat before +it is blitted the bat to the screen, and the functions that will define what this class will actually do. Here's some sample code:

+
class Bat(pygame.sprite.Sprite):
+    """Movable tennis 'bat' with which one hits the ball
+    Returns: bat object
+    Functions: reinit, update, moveup, movedown
+    Attributes: which, speed"""
+
+    def __init__(self, side):
+        pygame.sprite.Sprite.__init__(self)
+        self.image, self.rect = load_png("bat.png")
+        screen = pygame.display.get_surface()
+        self.area = screen.get_rect()
+        self.side = side
+        self.speed = 10
+        self.state = "still"
+        self.reinit()
+
+    def reinit(self):
+        self.state = "still"
+        self.movepos = [0,0]
+        if self.side == "left":
+            self.rect.midleft = self.area.midleft
+        elif self.side == "right":
+            self.rect.midright = self.area.midright
+
+    def update(self):
+        newpos = self.rect.move(self.movepos)
+        if self.area.contains(newpos):
+            self.rect = newpos
+        pygame.event.pump()
+
+    def moveup(self):
+        self.movepos[1] = self.movepos[1] - (self.speed)
+        self.state = "moveup"
+
+    def movedown(self):
+        self.movepos[1] = self.movepos[1] + (self.speed)
+        self.state = "movedown"
+
+
+

As you can see, this class is very similar to the ball class in its structure. But there are differences in what each function does. +First of all, there is a reinit function, which is used when a round ends, and the bat needs to be set back in its starting place, +with any attributes set back to their necessary values. Next, the way in which the bat is moved is a little more complex than with the +ball, because here its movement is simple (up/down), but it relies on the user telling it to move, unlike the ball which just keeps +moving in every frame. To make sense of how the bat moves, it is helpful to look at a quick diagram to show the sequence of events:

+../_images/tom_event-flowchart.png +

What happens here is that the person controlling the bat pushes down on the key that moves the bat up. For each iteration of the main +game loop (for every frame), if the key is still held down, then the state attribute of that bat object will be set to +"moving", and the moveup function will be called, causing the ball's y position to be reduced by the value of the +speed attribute (in this example, 10). In other words, so long as the key is held down, the bat will move up the screen +by 10 pixels per frame. The state attribute isn't used here yet, but it's useful to know if you're dealing with spin, or +would like some useful debugging output.

+

As soon as the player lets go of that key, the second set of boxes is invoked, and the state attribute of the bat object +will be set back to "still", and the movepos attribute will be set back to [0,0], meaning that when the update function is called, it won't move the bat any more. So when the player lets go of the key, the bat stops moving. Simple!

+
+

5.1.1. Diversion 3: Pygame events

+

So how do we know when the player is pushing keys down, and then releasing them? With the Pygame event queue system, dummy! It's a +really easy system to use and understand, so this shouldn't take long :) You've already seen the event queue in action in the basic +Pygame program, where it was used to check if the user was quitting the application. The code for moving the bat is about as simple +as that:

+
for event in pygame.event.get():
+    if event.type == QUIT:
+        return
+    elif event.type == KEYDOWN:
+        if event.key == K_UP:
+            player.moveup()
+        if event.key == K_DOWN:
+            player.movedown()
+    elif event.type == KEYUP:
+        if event.key == K_UP or event.key == K_DOWN:
+            player.movepos = [0,0]
+            player.state = "still"
+
+
+

Here assume that you've already created an instance of a bat, and called the object player. You can see the familiar +layout of the for structure, which iterates through each event found in the Pygame event queue, which is retrieved with +the event.get() function. As the user hits keys, pushes mouse buttons and moves the joystick about, those actions are +pumped into the Pygame event queue, and left there until dealt with. So in each iteration of the main game loop, you go through +these events, checking if they're ones you want to deal with, and then dealing with them appropriately. The event.pump() +function that was in the Bat.update function is then called in every iteration to pump out old events, and keep the queue +current.

+

First we check if the user is quitting the program, and quit it if they are. Then we check if any keys are being pushed down, and if +they are, we check if they're the designated keys for moving the bat up and down. If they are, then we call the appropriate moving +function, and set the player state appropriately (though the states moveup and movedown and changed in the moveup() and +movedown() functions, which makes for neater code, and doesn't break encapsulation, which means that you +assign attributes to the object itself, without referring to the name of the instance of that object). Notice here we have three +states: still, moveup, and movedown. Again, these come in handy if you want to debug or calculate spin. We also check if any keys have +been "let go" (i.e. are no longer being held down), and again if they're the right keys, we stop the bat from moving.

+
+
+
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/docs/generated/tut/tom_games6.html b/.venv/Lib/site-packages/pygame/docs/generated/tut/tom_games6.html new file mode 100644 index 00000000..b88789af --- /dev/null +++ b/.venv/Lib/site-packages/pygame/docs/generated/tut/tom_games6.html @@ -0,0 +1,435 @@ + + + + + + + + + Putting it all together — pygame v2.5.2 documentation + + + + + + + + + + + + + +
+ +
+
+ + +
+
+ +
+
+ +
+
+

6. Putting it all together

+

So far you've learnt all the basics necessary to build a simple game. You should understand how to create Pygame objects, how Pygame +displays objects, how it handles events, and how you can use physics to introduce some motion into your game. Now I'll just show how +you can take all those chunks of code and put them together into a working game. What we need first is to let the ball hit the sides +of the screen, and for the bat to be able to hit the ball, otherwise there's not going to be much gameplay involved. We do this +using Pygame's collision methods.

+
+

6.1. Let the ball hit sides

+

The basic principle behind making it bounce of the sides is easy to grasp. You grab the coordinates of the four corners of the ball, +and check to see if they correspond with the x or y coordinate of the edge of the screen. So if the top right and top left corners both +have a y coordinate of zero, you know that the ball is currently on the top edge of the screen. We do all this in the update function, +after we've worked out the new position of the ball.

+
if not self.area.contains(newpos):
+      tl = not self.area.collidepoint(newpos.topleft)
+      tr = not self.area.collidepoint(newpos.topright)
+      bl = not self.area.collidepoint(newpos.bottomleft)
+      br = not self.area.collidepoint(newpos.bottomright)
+      if tr and tl or (br and bl):
+              angle = -angle
+      if tl and bl:
+              self.offcourt(player=2)
+      if tr and br:
+              self.offcourt(player=1)
+
+self.vector = (angle,z)
+
+
+

Here we check to see if the area +contains the new position of the ball (it always should, so we needn't have an else clause, +though in other circumstances you might want to consider it). We then check if the coordinates for the four corners +are colliding with the area's edges, and create objects for each result. If they are, the objects will have a value of 1, +or True. If they don't, then the value will be None, or False. We then see if it has hit the top or bottom, and if it +has we change the ball's direction. Handily, using radians we can do this by simply reversing its positive/negative value. +We also check to see if the ball has gone off the sides, and if it has we call the offcourt function. +This, in my game, resets the ball, adds 1 point to the score of the player specified when calling the function, and displays the new score.

+

Finally, we recompile the vector based on the new angle. And that is it. The ball will now merrily bounce off the walls and go +offcourt with good grace.

+
+
+

6.2. Let the ball hit bats

+

Making the ball hit the bats is very similar to making it hit the sides of the screen. We still use the collide method, but this time +we check to see if the rectangles for the ball and either bat collide. In this code I've also put in some extra code to avoid various +glitches. You'll find that you'll have to put all sorts of extra code in to avoid glitches and bugs, so it's good to get used to seeing +it.

+
else:
+    # Deflate the rectangles so you can't catch a ball behind the bat
+    player1.rect.inflate(-3, -3)
+    player2.rect.inflate(-3, -3)
+
+    # Do ball and bat collide?
+    # Note I put in an odd rule that sets self.hit to 1 when they collide, and unsets it in the next
+    # iteration. this is to stop odd ball behaviour where it finds a collision *inside* the
+    # bat, the ball reverses, and is still inside the bat, so bounces around inside.
+    # This way, the ball can always escape and bounce away cleanly
+    if self.rect.colliderect(player1.rect) == 1 and not self.hit:
+        angle = math.pi - angle
+        self.hit = not self.hit
+    elif self.rect.colliderect(player2.rect) == 1 and not self.hit:
+        angle = math.pi - angle
+        self.hit = not self.hit
+    elif self.hit:
+        self.hit = not self.hit
+self.vector = (angle,z)
+
+
+

We start this section with an else statement, because this carries on from the previous chunk of code to check if the ball +hits the sides. It makes sense that if it doesn't hit the sides, it might hit a bat, so we carry on the conditional statement. The +first glitch to fix is to shrink the players' rectangles by 3 pixels in both dimensions, to stop the bat catching a ball that goes +behind them (if you imagine you just move the bat so that as the ball travels behind it, the rectangles overlap, and so normally the +ball would then have been "hit" - this prevents that).

+

Next we check if the rectangles collide, with one more glitch fix. Notice that I've commented on these odd bits of code - it's always +good to explain bits of code that are abnormal, both for others who look at your code, and so you understand it when you come back to +it. The without the fix, the ball might hit a corner of the bat, change direction, and one frame later still find itself inside the +bat. Then it would again think it has been hit, and change its direction. This can happen several times, making the ball's motion +completely unrealistic. So we have a variable, self.hit, which we set to True when it has been hit, and False one frame +later. When we check if the rectangles have collided, we also check if self.hit is True/False, to stop internal bouncing.

+

The important code here is pretty easy to understand. All rectangles have a colliderect +function, into which you feed the rectangle of another object, which returns True if the rectangles do overlap, and False if not. +If they do, we can change the direction by subtracting the current angle from pi (again, a handy trick you can do with radians, +which will adjust the angle by 90 degrees and send it off in the right direction; you might find at this point that a thorough +understanding of radians is in order!). Just to finish the glitch checking, we switch self.hit back to False if it's the frame +after they were hit.

+

We also then recompile the vector. You would of course want to remove the same line in the previous chunk of code, so that you only do +this once after the if-else conditional statement. And that's it! The combined code will now allow the ball to hit sides and bats.

+
+
+

6.3. The Finished product

+

The final product, with all the bits of code thrown together, as well as some other bits ofcode to glue it all together, will look +like this:

+
#
+# Tom's Pong
+# A simple pong game with realistic physics and AI
+# http://tomchance.org.uk/projects/pong
+#
+# Released under the GNU General Public License
+
+VERSION = "0.4"
+
+try:
+    import sys
+    import random
+    import math
+    import os
+    import getopt
+    import pygame
+    from socket import *
+    from pygame.locals import *
+except ImportError, err:
+    print(f"couldn't load module. {err}")
+    sys.exit(2)
+
+def load_png(name):
+    """ Load image and return image object"""
+    fullname = os.path.join("data", name)
+    try:
+        image = pygame.image.load(fullname)
+        if image.get_alpha is None:
+            image = image.convert()
+        else:
+            image = image.convert_alpha()
+    except FileNotFoundError:
+        print(f"Cannot load image: {fullname}")
+        raise SystemExit
+    return image, image.get_rect()
+
+class Ball(pygame.sprite.Sprite):
+    """A ball that will move across the screen
+    Returns: ball object
+    Functions: update, calcnewpos
+    Attributes: area, vector"""
+
+    def __init__(self, (xy), vector):
+        pygame.sprite.Sprite.__init__(self)
+        self.image, self.rect = load_png("ball.png")
+        screen = pygame.display.get_surface()
+        self.area = screen.get_rect()
+        self.vector = vector
+        self.hit = 0
+
+    def update(self):
+        newpos = self.calcnewpos(self.rect,self.vector)
+        self.rect = newpos
+        (angle,z) = self.vector
+
+        if not self.area.contains(newpos):
+            tl = not self.area.collidepoint(newpos.topleft)
+            tr = not self.area.collidepoint(newpos.topright)
+            bl = not self.area.collidepoint(newpos.bottomleft)
+            br = not self.area.collidepoint(newpos.bottomright)
+            if tr and tl or (br and bl):
+                angle = -angle
+            if tl and bl:
+                #self.offcourt()
+                angle = math.pi - angle
+            if tr and br:
+                angle = math.pi - angle
+                #self.offcourt()
+        else:
+            # Deflate the rectangles so you can't catch a ball behind the bat
+            player1.rect.inflate(-3, -3)
+            player2.rect.inflate(-3, -3)
+
+            # Do ball and bat collide?
+            # Note I put in an odd rule that sets self.hit to 1 when they collide, and unsets it in the next
+            # iteration. this is to stop odd ball behaviour where it finds a collision *inside* the
+            # bat, the ball reverses, and is still inside the bat, so bounces around inside.
+            # This way, the ball can always escape and bounce away cleanly
+            if self.rect.colliderect(player1.rect) == 1 and not self.hit:
+                angle = math.pi - angle
+                self.hit = not self.hit
+            elif self.rect.colliderect(player2.rect) == 1 and not self.hit:
+                angle = math.pi - angle
+                self.hit = not self.hit
+            elif self.hit:
+                self.hit = not self.hit
+        self.vector = (angle,z)
+
+    def calcnewpos(self,rect,vector):
+        (angle,z) = vector
+        (dx,dy) = (z*math.cos(angle),z*math.sin(angle))
+        return rect.move(dx,dy)
+
+class Bat(pygame.sprite.Sprite):
+    """Movable tennis 'bat' with which one hits the ball
+    Returns: bat object
+    Functions: reinit, update, moveup, movedown
+    Attributes: which, speed"""
+
+    def __init__(self, side):
+        pygame.sprite.Sprite.__init__(self)
+        self.image, self.rect = load_png("bat.png")
+        screen = pygame.display.get_surface()
+        self.area = screen.get_rect()
+        self.side = side
+        self.speed = 10
+        self.state = "still"
+        self.reinit()
+
+    def reinit(self):
+        self.state = "still"
+        self.movepos = [0,0]
+        if self.side == "left":
+            self.rect.midleft = self.area.midleft
+        elif self.side == "right":
+            self.rect.midright = self.area.midright
+
+    def update(self):
+        newpos = self.rect.move(self.movepos)
+        if self.area.contains(newpos):
+            self.rect = newpos
+        pygame.event.pump()
+
+    def moveup(self):
+        self.movepos[1] = self.movepos[1] - (self.speed)
+        self.state = "moveup"
+
+    def movedown(self):
+        self.movepos[1] = self.movepos[1] + (self.speed)
+        self.state = "movedown"
+
+
+def main():
+    # Initialise screen
+    pygame.init()
+    screen = pygame.display.set_mode((640, 480))
+    pygame.display.set_caption("Basic Pong")
+
+    # Fill background
+    background = pygame.Surface(screen.get_size())
+    background = background.convert()
+    background.fill((0, 0, 0))
+
+    # Initialise players
+    global player1
+    global player2
+    player1 = Bat("left")
+    player2 = Bat("right")
+
+    # Initialise ball
+    speed = 13
+    rand = ((0.1 * (random.randint(5,8))))
+    ball = Ball((0,0),(0.47,speed))
+
+    # Initialise sprites
+    playersprites = pygame.sprite.RenderPlain((player1, player2))
+    ballsprite = pygame.sprite.RenderPlain(ball)
+
+    # Blit everything to the screen
+    screen.blit(background, (0, 0))
+    pygame.display.flip()
+
+    # Initialise clock
+    clock = pygame.time.Clock()
+
+    # Event loop
+    while True:
+        # Make sure game doesn't run at more than 60 frames per second
+        clock.tick(60)
+
+        for event in pygame.event.get():
+            if event.type == QUIT:
+                return
+            elif event.type == KEYDOWN:
+                if event.key == K_a:
+                    player1.moveup()
+                if event.key == K_z:
+                    player1.movedown()
+                if event.key == K_UP:
+                    player2.moveup()
+                if event.key == K_DOWN:
+                    player2.movedown()
+            elif event.type == KEYUP:
+                if event.key == K_a or event.key == K_z:
+                    player1.movepos = [0,0]
+                    player1.state = "still"
+                if event.key == K_UP or event.key == K_DOWN:
+                    player2.movepos = [0,0]
+                    player2.state = "still"
+
+        screen.blit(background, ball.rect, ball.rect)
+        screen.blit(background, player1.rect, player1.rect)
+        screen.blit(background, player2.rect, player2.rect)
+        ballsprite.update()
+        playersprites.update()
+        ballsprite.draw(screen)
+        playersprites.draw(screen)
+        pygame.display.flip()
+
+
+if __name__ == "__main__":
+    main()
+
+
+

As well as showing you the final product, I'll point you back to TomPong, upon which all of this is based. Download it, have a look +at the source code, and you'll see a full implementation of pong using all of the code you've seen in this tutorial, as well as lots of +other code I've added in various versions, such as some extra physics for spinning, and various other bug and glitch fixes.

+

Oh, find TomPong at http://tomchance.org.uk/projects/pong.

+
+
+
+ + +

+
+Edit on GitHub +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/draw.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/draw.cp311-win_amd64.pyd new file mode 100644 index 00000000..571e721a Binary files /dev/null and b/.venv/Lib/site-packages/pygame/draw.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/draw.pyi b/.venv/Lib/site-packages/pygame/draw.pyi new file mode 100644 index 00000000..d574ea33 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/draw.pyi @@ -0,0 +1,74 @@ +from typing import Optional, Sequence + +from pygame.rect import Rect +from pygame.surface import Surface + +from ._common import ColorValue, Coordinate, RectValue + +def rect( + surface: Surface, + color: ColorValue, + rect: RectValue, + width: int = 0, + border_radius: int = -1, + border_top_left_radius: int = -1, + border_top_right_radius: int = -1, + border_bottom_left_radius: int = -1, + border_bottom_right_radius: int = -1, +) -> Rect: ... +def polygon( + surface: Surface, + color: ColorValue, + points: Sequence[Coordinate], + width: int = 0, +) -> Rect: ... +def circle( + surface: Surface, + color: ColorValue, + center: Coordinate, + radius: float, + width: int = 0, + draw_top_right: bool = False, + draw_top_left: bool = False, + draw_bottom_left: bool = False, + draw_bottom_right: bool = False, +) -> Rect: ... +def ellipse( + surface: Surface, color: ColorValue, rect: RectValue, width: int = 0 +) -> Rect: ... +def arc( + surface: Surface, + color: ColorValue, + rect: RectValue, + start_angle: float, + stop_angle: float, + width: int = 1, +) -> Rect: ... +def line( + surface: Surface, + color: ColorValue, + start_pos: Coordinate, + end_pos: Coordinate, + width: int = 1, +) -> Rect: ... +def lines( + surface: Surface, + color: ColorValue, + closed: bool, + points: Sequence[Coordinate], + width: int = 1, +) -> Rect: ... +def aaline( + surface: Surface, + color: ColorValue, + start_pos: Coordinate, + end_pos: Coordinate, + blend: int = 1, +) -> Rect: ... +def aalines( + surface: Surface, + color: ColorValue, + closed: bool, + points: Sequence[Coordinate], + blend: int = 1, +) -> Rect: ... diff --git a/.venv/Lib/site-packages/pygame/draw_py.py b/.venv/Lib/site-packages/pygame/draw_py.py new file mode 100644 index 00000000..33f60923 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/draw_py.py @@ -0,0 +1,562 @@ +"""Pygame Drawing algorithms written in Python. (Work in Progress) + +Implement Pygame's Drawing Algorithms in a Python version for testing +and debugging. +""" + +from collections import namedtuple +from math import floor, ceil + + +# H E L P E R F U N C T I O N S # + +# fractional part of x + + +def frac(value): + """return fractional part of x""" + return value - floor(value) + + +def inv_frac(value): + """return inverse fractional part of x""" + return 1 - (value - floor(value)) # eg, 1 - frac(x) + + +BoundingBox = namedtuple("BoundingBox", ["left", "top", "right", "bottom"]) +Point = namedtuple("Point", ["x", "y"]) + + +# L O W L E V E L D R A W F U N C T I O N S # +# (They are too low-level to be translated into python, right?) + + +def set_at(surf, in_x, in_y, color): + """Set the color of a pixel in a surface""" + surf.set_at((in_x, in_y), color) + + +def draw_pixel(surf, pos, color, bright, blend=True): + """draw one blended pixel with given brightness.""" + try: + other_col = surf.get_at(pos) if blend else (0, 0, 0, 0) + except IndexError: # pixel outside the surface + return + new_color = tuple( + (bright * col + (1 - bright) * pix) for col, pix in zip(color, other_col) + ) + # FIXME what should happen if only one, color or surf_col, has alpha? + surf.set_at(pos, new_color) + + +def _drawhorzline(surf, color, x_from, in_y, x_to): + if x_from == x_to: + surf.set_at((x_from, in_y), color) + return + + start, end = (x_from, x_to) if x_from <= x_to else (x_to, x_from) + for line_x in range(start, end + 1): + surf.set_at((line_x, in_y), color) + + +def _drawvertline(surf, color, in_x, y_from, y_to): + if y_from == y_to: + surf.set_at((in_x, y_from), color) + return + + start, end = (y_from, y_to) if y_from <= y_to else (y_to, y_from) + for line_y in range(start, end + 1): + surf.set_at((in_x, line_y), color) + + +# I N T E R N A L D R A W L I N E F U N C T I O N S # + + +def _clip_and_draw_horizline(surf, color, x_from, in_y, x_to): + """draw clipped horizontal line.""" + # check Y inside surf + clip = surf.get_clip() + if in_y < clip.y or in_y >= clip.y + clip.h: + return + + x_from = max(x_from, clip.x) + x_to = min(x_to, clip.x + clip.w - 1) + + # check any x inside surf + if x_to < clip.x or x_from >= clip.x + clip.w: + return + + _drawhorzline(surf, color, x_from, in_y, x_to) + + +def _clip_and_draw_vertline(surf, color, in_x, y_from, y_to): + """draw clipped vertical line.""" + # check X inside surf + clip = surf.get_clip() + + if in_x < clip.x or in_x >= clip.x + clip.w: + return + + y_from = max(y_from, clip.y) + y_to = min(y_to, clip.y + clip.h - 1) + + # check any y inside surf + if y_to < clip.y or y_from >= clip.y + clip.h: + return + + _drawvertline(surf, color, in_x, y_from, y_to) + + +# These constants xxx_EDGE are "outside-the-bounding-box"-flags +LEFT_EDGE = 0x1 +RIGHT_EDGE = 0x2 +BOTTOM_EDGE = 0x4 +TOP_EDGE = 0x8 + + +def encode(pos, b_box): + """returns a code that defines position with respect to a bounding box""" + # we use the fact that python interprets booleans (the inequalities) + # as 0/1, and then multiply them with the xxx_EDGE flags + return ( + (pos[0] < b_box.left) * LEFT_EDGE + + (pos[0] > b_box.right) * RIGHT_EDGE + + (pos[1] < b_box.top) * TOP_EDGE + + (pos[1] > b_box.bottom) * BOTTOM_EDGE + ) + + +def clip_line(line, b_box, use_float=False): + """Algorithm to calculate the clipped line. + + We calculate the coordinates of the part of the line segment within the + bounding box (defined by left, top, right, bottom). The we write + the coordinates of the line segment into "line", much like the C-algorithm. + With `use_float` True, clip_line is usable for float-clipping. + + Returns: true if the line segment cuts the bounding box (false otherwise) + """ + + def inside(code): + return not code + + def accept(code_a, code_b): + return not (code_a or code_b) + + def reject(code_a, code_b): + return code_a and code_b + + assert isinstance(line, list) + x_1, y_1, x_2, y_2 = line + dtype = float if use_float else int + + while True: + # the coordinates are progressively modified with the codes, + # until they are either rejected or correspond to the final result. + code1 = encode((x_1, y_1), b_box) + code2 = encode((x_2, y_2), b_box) + + if accept(code1, code2): + # write coordinates into "line" ! + line[:] = x_1, y_1, x_2, y_2 + return True + if reject(code1, code2): + return False + + # We operate on the (x_1, y_1) point, + # and swap if it is inside the bbox: + if inside(code1): + x_1, x_2 = x_2, x_1 + y_1, y_2 = y_2, y_1 + code1, code2 = code2, code1 + slope = (y_2 - y_1) / float(x_2 - x_1) if (x_2 != x_1) else 1.0 + # Each case, if true, means that we are outside the border: + # calculate x_1 and y_1 to be the "first point" inside the bbox... + if code1 & LEFT_EDGE: + y_1 += dtype((b_box.left - x_1) * slope) + x_1 = b_box.left + elif code1 & RIGHT_EDGE: + y_1 += dtype((b_box.right - x_1) * slope) + x_1 = b_box.right + elif code1 & BOTTOM_EDGE: + if x_2 != x_1: + x_1 += dtype((b_box.bottom - y_1) / slope) + y_1 = b_box.bottom + elif code1 & TOP_EDGE: + if x_2 != x_1: + x_1 += dtype((b_box.top - y_1) / slope) + y_1 = b_box.top + + +def _draw_line(surf, color, start, end): + """draw a non-horizontal line (without anti-aliasing).""" + # Variant of https://en.wikipedia.org/wiki/Bresenham's_line_algorithm + # + # This strongly differs from craw.c implementation, because we use a + # "slope" variable (instead of delta_x and delta_y) and a "error" variable. + # And we can not do pointer-arithmetic with "BytesPerPixel", like in + # the C-algorithm. + if start.x == end.x: + # This case should not happen... + raise ValueError + + slope = abs((end.y - start.y) / (end.x - start.x)) + error = 0.0 + + if slope < 1: + # Here, it's a rather horizontal line + + # 1. check in which octants we are & set init values + if end.x < start.x: + start.x, end.x = end.x, start.x + start.y, end.y = end.y, start.y + line_y = start.y + dy_sign = 1 if (start.y < end.y) else -1 + + # 2. step along x coordinate + for line_x in range(start.x, end.x + 1): + set_at(surf, line_x, line_y, color) + error += slope + if error >= 0.5: + line_y += dy_sign + error -= 1 + else: + # Case of a rather vertical line + + # 1. check in which octants we are & set init values + if start.y > end.y: + start.x, end.x = end.x, start.x + start.y, end.y = end.y, start.y + line_x = start.x + slope = 1 / slope + dx_sign = 1 if (start.x < end.x) else -1 + + # 2. step along y coordinate + for line_y in range(start.y, end.y + 1): + set_at(surf, line_x, line_y, color) + error += slope + if error >= 0.5: + line_x += dx_sign + error -= 1 + + +def _draw_aaline(surf, color, start, end, blend): + """draw an anti-aliased line. + + The algorithm yields identical results with _draw_line for horizontal, + vertical or diagonal lines, and results changes smoothly when changing + any of the endpoint coordinates. + + Note that this yields strange results for very short lines, eg + a line from (0, 0) to (0, 1) will draw 2 pixels, and a line from + (0, 0) to (0, 1.1) will blend 10 % on the pixel (0, 2). + """ + # The different requirements that we have on an antialiasing algorithm + # implies to make some compromises: + # 1. We want smooth evolution wrt to the 4 endpoint coordinates + # (this means also that we want a smooth evolution when the angle + # passes +/- 45° + # 2. We want the same behavior when swapping the endpoints + # 3. We want understandable results for the endpoint values + # (eg we want to avoid half-integer values to draw a simple plain + # horizontal or vertical line between two integer l endpoints) + # + # This implies to somehow make the line artificially 1 pixel longer + # and to draw a full pixel when we have the endpoints are identical. + d_x = end.x - start.x + d_y = end.y - start.y + + if d_x == 0 and d_y == 0: + # For smoothness reasons, we could also do some blending here, + # but it seems overshoot... + set_at(surf, int(start.x), int(start.y), color) + return + + if start.x > end.x or start.y > end.y: + start.x, end.x = end.x, start.x + start.y, end.y = end.y, start.y + d_x = -d_x + d_y = -d_y + + if abs(d_x) >= abs(d_y): + slope = d_y / d_x + + def draw_two_pixel(in_x, float_y, factor): + flr_y = floor(float_y) + draw_pixel(surf, (in_x, flr_y), color, factor * inv_frac(float_y), blend) + draw_pixel(surf, (in_x, flr_y + 1), color, factor * frac(float_y), blend) + + _draw_aaline_dx(d_x, slope, end, start, draw_two_pixel) + else: + slope = d_x / d_y + + def draw_two_pixel(float_x, in_y, factor): + fl_x = floor(float_x) + draw_pixel(surf, (fl_x, in_y), color, factor * inv_frac(float_x), blend) + draw_pixel(surf, (fl_x + 1, in_y), color, factor * frac(float_x), blend) + + _draw_aaline_dy(d_y, slope, end, start, draw_two_pixel) + + +def _draw_aaline_dy(d_y, slope, end, start, draw_two_pixel): + g_y = ceil(start.y) + g_x = start.x + (g_y - start.y) * slope + # 1. Draw start of the segment + if start.y < g_y: + draw_two_pixel(g_x - slope, floor(start.y), inv_frac(start.y)) + # 2. Draw end of the segment + rest = frac(end.y) + s_y = ceil(end.y) + if rest > 0: + s_x = start.x + slope * (d_y + 1 - rest) + draw_two_pixel(s_x, s_y, rest) + else: + s_y += 1 + # 3. loop for other points + for line_y in range(g_y, s_y): + line_x = g_x + slope * (line_y - g_y) + draw_two_pixel(line_x, line_y, 1) + + +def _draw_aaline_dx(d_x, slope, end, start, draw_two_pixel): + # A and G are respectively left and right to the "from" point, but + # with integer-x-coordinate, (and only if from_x is not integer). + # Hence they appear in following order on the line in general case: + # A from-pt G . . . to-pt S + # |------*-------|--- . . . ---|-----*------|- + g_x = ceil(start.x) + g_y = start.y + (g_x - start.x) * slope + # 1. Draw start of the segment if we have a non-integer-part + if start.x < g_x: + # this corresponds to the point "A" + draw_two_pixel(floor(start.x), g_y - slope, inv_frac(start.x)) + # 2. Draw end of the segment: we add one pixel for homogeneity reasons + rest = frac(end.x) + s_x = ceil(end.x) + if rest > 0: + # Again we draw only if we have a non-integer-part + s_y = start.y + slope * (d_x + 1 - rest) + draw_two_pixel(s_x, s_y, rest) + else: + s_x += 1 + # 3. loop for other points + for line_x in range(g_x, s_x): + line_y = g_y + slope * (line_x - g_x) + draw_two_pixel(line_x, line_y, 1) + + +# C L I P A N D D R A W L I N E F U N C T I O N S # + + +def _clip_and_draw_line(surf, rect, color, pts): + """clip the line into the rectangle and draw if needed. + + Returns true if anything has been drawn, else false.""" + # "pts" is a list with the four coordinates of the two endpoints + # of the line to be drawn : pts = x1, y1, x2, y2. + # The data format is like that to stay closer to the C-algorithm. + if not clip_line( + pts, BoundingBox(rect.x, rect.y, rect.x + rect.w - 1, rect.y + rect.h - 1) + ): + # The line segment defined by "pts" is not crossing the rectangle + return 0 + if pts[1] == pts[3]: # eg y1 == y2 + _drawhorzline(surf, color, pts[0], pts[1], pts[2]) + elif pts[0] == pts[2]: # eg x1 == x2 + _drawvertline(surf, color, pts[0], pts[1], pts[3]) + else: + _draw_line(surf, color, Point(pts[0], pts[1]), Point(pts[2], pts[3])) + return 1 + + +def _clip_and_draw_line_width(surf, rect, color, line, width): + yinc = xinc = 0 + if abs(line[0] - line[2]) > abs(line[1] - line[3]): + yinc = 1 + else: + xinc = 1 + newpts = line[:] + if _clip_and_draw_line(surf, rect, color, newpts): + anydrawn = 1 + frame = newpts[:] + else: + anydrawn = 0 + frame = [10000, 10000, -10000, -10000] + + for loop in range(1, width // 2 + 1): + newpts[0] = line[0] + xinc * loop + newpts[1] = line[1] + yinc * loop + newpts[2] = line[2] + xinc * loop + newpts[3] = line[3] + yinc * loop + if _clip_and_draw_line(surf, rect, color, newpts): + anydrawn = 1 + frame[0] = min(newpts[0], frame[0]) + frame[1] = min(newpts[1], frame[1]) + frame[2] = max(newpts[2], frame[2]) + frame[3] = max(newpts[3], frame[3]) + + if loop * 2 < width: + newpts[0] = line[0] - xinc * loop + newpts[1] = line[1] - yinc * loop + newpts[2] = line[2] - xinc * loop + newpts[3] = line[3] - yinc * loop + if _clip_and_draw_line(surf, rect, color, newpts): + anydrawn = 1 + frame[0] = min(newpts[0], frame[0]) + frame[1] = min(newpts[1], frame[1]) + frame[2] = max(newpts[2], frame[2]) + frame[3] = max(newpts[3], frame[3]) + + return anydrawn + + +def _clip_and_draw_aaline(surf, rect, color, line, blend): + """draw anti-aliased line between two endpoints.""" + if not clip_line( + line, + BoundingBox(rect.x - 1, rect.y - 1, rect.x + rect.w, rect.y + rect.h), + use_float=True, + ): + return # TODO Rect(rect.x, rect.y, 0, 0) + _draw_aaline(surf, color, Point(line[0], line[1]), Point(line[2], line[3]), blend) + return # TODO Rect(-- affected area --) + + +# D R A W L I N E F U N C T I O N S # + + +def draw_aaline(surf, color, from_point, to_point, blend=True): + """draw anti-aliased line between two endpoints.""" + line = [from_point[0], from_point[1], to_point[0], to_point[1]] + return _clip_and_draw_aaline(surf, surf.get_clip(), color, line, blend) + + +def draw_line(surf, color, from_point, to_point, width=1): + """draw anti-aliased line between two endpoints.""" + line = [from_point[0], from_point[1], to_point[0], to_point[1]] + return _clip_and_draw_line_width(surf, surf.get_clip(), color, line, width) + + +# M U L T I L I N E F U N C T I O N S # + + +def _multi_lines( + surf, + color, + closed, # pylint: disable=too-many-arguments + points, + width=1, + blend=False, + aaline=False, +): + """draw several lines, either anti-aliased or not.""" + # The code for anti-aliased or not is almost identical, so it's factorized + if len(points) <= 2: + raise TypeError + line = [0] * 4 # store x1, y1 & x2, y2 of the lines to be drawn + + xlist = [pt[0] for pt in points] + ylist = [pt[1] for pt in points] + line[0] = xlist[0] + line[1] = ylist[0] + b_box = BoundingBox(left=xlist[0], right=xlist[0], top=ylist[0], bottom=ylist[0]) + + for line_x, line_y in points[1:]: + b_box.left = min(b_box.left, line_x) + b_box.right = max(b_box.right, line_x) + b_box.top = min(b_box.top, line_y) + b_box.bottom = max(b_box.bottom, line_y) + + rect = surf.get_clip() + for loop in range(1, len(points)): + line[0] = xlist[loop - 1] + line[1] = ylist[loop - 1] + line[2] = xlist[loop] + line[3] = ylist[loop] + if aaline: + _clip_and_draw_aaline(surf, rect, color, line, blend) + else: + _clip_and_draw_line_width(surf, rect, color, line, width) + + if closed: + line[0] = xlist[len(points) - 1] + line[1] = ylist[len(points) - 1] + line[2] = xlist[0] + line[3] = ylist[0] + if aaline: + _clip_and_draw_aaline(surf, rect, color, line, blend) + else: + _clip_and_draw_line_width(surf, rect, color, line, width) + + # TODO Rect(...) + + +def draw_lines(surf, color, closed, points, width=1): + """draw several lines connected through the points.""" + return _multi_lines(surf, color, closed, points, width, aaline=False) + + +def draw_aalines(surf, color, closed, points, blend=True): + """draw several anti-aliased lines connected through the points.""" + return _multi_lines(surf, color, closed, points, blend=blend, aaline=True) + + +def draw_polygon(surface, color, points, width): + """Draw a polygon""" + if width: + draw_lines(surface, color, 1, points, width) + return # TODO Rect(...) + num_points = len(points) + point_x = [x for x, y in points] + point_y = [y for x, y in points] + + miny = min(point_y) + maxy = max(point_y) + + if miny == maxy: + minx = min(point_x) + maxx = max(point_x) + _clip_and_draw_horizline(surface, color, minx, miny, maxx) + return # TODO Rect(...) + + for y_coord in range(miny, maxy + 1): + x_intersect = [] + for i in range(num_points): + _draw_polygon_inner_loop(i, point_x, point_y, y_coord, x_intersect) + + x_intersect.sort() + for i in range(0, len(x_intersect), 2): + _clip_and_draw_horizline( + surface, color, x_intersect[i], y_coord, x_intersect[i + 1] + ) + + # special case : horizontal border lines + for i in range(num_points): + i_prev = i - 1 if i else num_points - 1 + if miny < point_y[i] == point_y[i_prev] < maxy: + _clip_and_draw_horizline( + surface, color, point_x[i], point_y[i], point_x[i_prev] + ) + + return # TODO Rect(...) + + +def _draw_polygon_inner_loop(index, point_x, point_y, y_coord, x_intersect): + i_prev = index - 1 if index else len(point_x) - 1 + + y_1 = point_y[i_prev] + y_2 = point_y[index] + + if y_1 < y_2: + x_1 = point_x[i_prev] + x_2 = point_x[index] + elif y_1 > y_2: + y_2 = point_y[i_prev] + y_1 = point_y[index] + x_2 = point_x[i_prev] + x_1 = point_x[index] + else: # special case handled below + return + + if (y_2 > y_coord >= y_1) or ((y_coord == max(point_y)) and (y_coord <= y_2)): + x_intersect.append((y_coord - y_1) * (x_2 - x_1) // (y_2 - y_1) + x_1) diff --git a/.venv/Lib/site-packages/pygame/event.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/event.cp311-win_amd64.pyd new file mode 100644 index 00000000..d5aba595 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/event.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/event.pyi b/.venv/Lib/site-packages/pygame/event.pyi new file mode 100644 index 00000000..91759607 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/event.pyi @@ -0,0 +1,51 @@ +from typing import ( + Any, + Dict, + List, + Optional, + Sequence, + SupportsInt, + Tuple, + Union, + final, + overload, +) + +@final +class Event: + type: int + dict: Dict[str, Any] + __dict__: Dict[str, Any] + __hash__: None # type: ignore + def __init__( + self, type: int, dict: Dict[str, Any] = ..., **kwargs: Any + ) -> None: ... + def __getattribute__(self, name: str) -> Any: ... + def __setattr__(self, name: str, value: Any) -> None: ... + def __delattr__(self, name: str) -> None: ... + def __bool__(self) -> bool: ... + +_EventTypes = Union[SupportsInt, Tuple[SupportsInt, ...], Sequence[SupportsInt]] + +def pump() -> None: ... +def get( + eventtype: Optional[_EventTypes] = None, + pump: Any = True, + exclude: Optional[_EventTypes] = None, +) -> List[Event]: ... +def poll() -> Event: ... +def wait(timeout: int = 0) -> Event: ... +def peek(eventtype: Optional[_EventTypes] = None, pump: Any = True) -> bool: ... +def clear(eventtype: Optional[_EventTypes] = None, pump: Any = True) -> None: ... +def event_name(type: int) -> str: ... +def set_blocked(type: Optional[_EventTypes]) -> None: ... +def set_allowed(type: Optional[_EventTypes]) -> None: ... +def get_blocked(type: _EventTypes) -> bool: ... +def set_grab(grab: bool) -> None: ... +def get_grab() -> bool: ... +def set_keyboard_grab(grab: bool) -> None: ... +def get_keyboard_grab() -> bool: ... +def post(event: Event) -> bool: ... +def custom_type() -> int: ... + +EventType = Event diff --git a/.venv/Lib/site-packages/pygame/examples/README.rst b/.venv/Lib/site-packages/pygame/examples/README.rst new file mode 100644 index 00000000..68b75bba --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/README.rst @@ -0,0 +1,154 @@ +These examples are a good introduction to various Pygame modules and +techniques. They are beginner-friendly with source code in the public +domain that can be adapted for your projects. + + +aacircles.py + An example of using the gfxdraw module to drawing anti-aliased circles. + +aliens.py + An arcade-style space shooter game that showcases various common and + important Pygame modules and techniques. + +arraydemo.py + Showcases the use of Numpy with Pygame to perform efficient + pixel manipulation. + +audiocapture.py + Use the mixer module to record sound from a microphone, and + play back the recorded sound. + +blend_fill.py + Demonstrates how to perform surface blending and filling + with Pygame. + +blit_blends.py + Uses blit functions to showcase some of Pygame's different + blending modes. + +camera.py + Basic image capturing and display using pygame.camera + +chimp.py + A simple game featuring a chimp that showcases the use of + common and important Pygame modules and techniques. + +cursors.py + Demonstrates the creation of custom cursors with Pygame. + +dropevent.py + Drag and drop files using the following events: + DROPBEGIN, DROPCOMPLETE, DROPTEXT, DROPFILE + +eventlist.py + A utility for displaying and logging real-time Pygame events, + useful for debugging. + +font_viewer.py + Demonstrates how to display all available fonts in a + scrolling window. + +fonty.py + A simple application demonstrating the different ways + to render fonts with the font module + +freetype_misc.py + Shows how to use the freetype module to perform font + rendering and manipulation. + +glcube.py + Using PyOpenGL and Pygame, this creates a spinning 3D multicolored cube. + +go_over_there.py + Demonstrates the important Vector.move_towards() function. + +grid.py + A simple example of grid-based movement. + +headless_no_windows_needed.py + Shows how to run Pygame in scripts. + +joystick.py + Shows how to integrate joysticks or game controllers into Pygame. + +liquid.py + Demonstrates how to create a simple liquid effect in an image. + +mask.py + Showcases how to use masks for collision detection and sprite + interaction. + +midi.py + Demonstrates how to use MIDI I/O using the midi module. + +moveit.py + Illustrates how to accomplish sprite movement and animation. + +music_drop_fade.py + Showcases dropping music files into Pygame, and how to + apply a fade effect to music playback. + +pixelarray.py + Manipulation of individual pixels using the PixelArray module. + +playmus.py + Uses the mixer module to play music files with CLI. + +prevent_display_stretching.py + Illustrates how to maintain aspect ratio when resizing a window + in Pygame. + +resizing_new.py + Showcases various window resizing events and how to fit graphics + to new dimensions. + +scaletest.py + Showcases the scaling of Surfaces. + +scrap_clipboard.py + Shows how to implement clipboard interaction with Pygame's scrap module. + +scroll.py + An example that implements smooth scrolling backgrounds for side-scrolling + games or parallax effects. + +setmodescale.py + Handles mouse scaling and selection of a good sized window depending + on the display. + +sound.py + Illustrates how to implement sound effects and music using Pygame. + +sound_array_demos.py + Showcases echo, delay and other array based processing of sounds. + +sprite_texture.py + Demonstrates how to use textured sprites in Pygame. + +stars.py + A simple starfield implementation in which the perspective can be + changed by a mouse click. + +testsprite.py + Showcases the basics of sprite handling, namely collision + detection and animation. + +textinput.py + A little "console" where you can write in text. + Shows how to use the TEXTEDITING and TEXTINPUT events. + +vgrade.py + Shows how to apply vertical gradients to surfaces using Pygame. + +video.py + Showcases the movie module, including the display of playback + controls. + +data/ + Directory with the resources for the examples. + + +More examples can be found on the Pygame website and GitHub. +We're always looking for new examples and/or example requests. Examining +code such as this is a great way to get started with Python-based +game development. diff --git a/.venv/Lib/site-packages/pygame/examples/__init__.py b/.venv/Lib/site-packages/pygame/examples/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 00000000..a6394b80 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/aacircle.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/aacircle.cpython-311.pyc new file mode 100644 index 00000000..e830c1c9 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/aacircle.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/aliens.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/aliens.cpython-311.pyc new file mode 100644 index 00000000..a70761c5 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/aliens.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/arraydemo.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/arraydemo.cpython-311.pyc new file mode 100644 index 00000000..19f8e74f Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/arraydemo.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/audiocapture.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/audiocapture.cpython-311.pyc new file mode 100644 index 00000000..b1af8841 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/audiocapture.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/blend_fill.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/blend_fill.cpython-311.pyc new file mode 100644 index 00000000..2c8a77cb Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/blend_fill.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/blit_blends.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/blit_blends.cpython-311.pyc new file mode 100644 index 00000000..03837d9c Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/blit_blends.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/camera.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/camera.cpython-311.pyc new file mode 100644 index 00000000..96820ef5 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/camera.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/chimp.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/chimp.cpython-311.pyc new file mode 100644 index 00000000..862be35c Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/chimp.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/cursors.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/cursors.cpython-311.pyc new file mode 100644 index 00000000..b94afb16 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/cursors.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/dropevent.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/dropevent.cpython-311.pyc new file mode 100644 index 00000000..f9a6e06b Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/dropevent.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/eventlist.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/eventlist.cpython-311.pyc new file mode 100644 index 00000000..506b512b Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/eventlist.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/font_viewer.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/font_viewer.cpython-311.pyc new file mode 100644 index 00000000..94fff06f Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/font_viewer.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/fonty.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/fonty.cpython-311.pyc new file mode 100644 index 00000000..2846ec78 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/fonty.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/freetype_misc.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/freetype_misc.cpython-311.pyc new file mode 100644 index 00000000..ed4b55ad Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/freetype_misc.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/glcube.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/glcube.cpython-311.pyc new file mode 100644 index 00000000..ed203662 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/glcube.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/go_over_there.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/go_over_there.cpython-311.pyc new file mode 100644 index 00000000..1aa50e91 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/go_over_there.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/grid.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/grid.cpython-311.pyc new file mode 100644 index 00000000..73aba71f Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/grid.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/headless_no_windows_needed.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/headless_no_windows_needed.cpython-311.pyc new file mode 100644 index 00000000..9f1581e6 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/headless_no_windows_needed.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/joystick.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/joystick.cpython-311.pyc new file mode 100644 index 00000000..396d6bb4 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/joystick.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/liquid.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/liquid.cpython-311.pyc new file mode 100644 index 00000000..20f46277 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/liquid.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/mask.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/mask.cpython-311.pyc new file mode 100644 index 00000000..0bd25465 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/mask.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/midi.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/midi.cpython-311.pyc new file mode 100644 index 00000000..99d9118d Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/midi.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/moveit.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/moveit.cpython-311.pyc new file mode 100644 index 00000000..9474284f Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/moveit.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/music_drop_fade.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/music_drop_fade.cpython-311.pyc new file mode 100644 index 00000000..6477f114 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/music_drop_fade.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/pixelarray.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/pixelarray.cpython-311.pyc new file mode 100644 index 00000000..bf274713 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/pixelarray.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/playmus.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/playmus.cpython-311.pyc new file mode 100644 index 00000000..1a366f19 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/playmus.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/resizing_new.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/resizing_new.cpython-311.pyc new file mode 100644 index 00000000..6c65b85f Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/resizing_new.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/scaletest.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/scaletest.cpython-311.pyc new file mode 100644 index 00000000..976b0fb2 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/scaletest.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/scrap_clipboard.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/scrap_clipboard.cpython-311.pyc new file mode 100644 index 00000000..7be685e9 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/scrap_clipboard.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/scroll.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/scroll.cpython-311.pyc new file mode 100644 index 00000000..aa170bae Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/scroll.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/setmodescale.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/setmodescale.cpython-311.pyc new file mode 100644 index 00000000..14478a2c Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/setmodescale.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/sound.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/sound.cpython-311.pyc new file mode 100644 index 00000000..fbb52645 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/sound.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/sound_array_demos.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/sound_array_demos.cpython-311.pyc new file mode 100644 index 00000000..1ff866c4 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/sound_array_demos.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/sprite_texture.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/sprite_texture.cpython-311.pyc new file mode 100644 index 00000000..d677398a Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/sprite_texture.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/stars.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/stars.cpython-311.pyc new file mode 100644 index 00000000..6f673484 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/stars.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/testsprite.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/testsprite.cpython-311.pyc new file mode 100644 index 00000000..72c14a7c Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/testsprite.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/textinput.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/textinput.cpython-311.pyc new file mode 100644 index 00000000..34e41f17 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/textinput.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/vgrade.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/vgrade.cpython-311.pyc new file mode 100644 index 00000000..4a695617 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/vgrade.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/__pycache__/video.cpython-311.pyc b/.venv/Lib/site-packages/pygame/examples/__pycache__/video.cpython-311.pyc new file mode 100644 index 00000000..22316bc0 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/__pycache__/video.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/examples/aacircle.py b/.venv/Lib/site-packages/pygame/examples/aacircle.py new file mode 100644 index 00000000..651df18b --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/aacircle.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +"""Proof of concept gfxdraw example""" + +import pygame +import pygame.gfxdraw + + +def main(): + pygame.init() + screen = pygame.display.set_mode((500, 500)) + screen.fill((255, 0, 0)) + s = pygame.Surface(screen.get_size(), pygame.SRCALPHA, 32) + pygame.draw.line(s, (0, 0, 0), (250, 250), (250 + 200, 250)) + + width = 1 + for a_radius in range(width): + radius = 200 + pygame.gfxdraw.aacircle(s, 250, 250, radius - a_radius, (0, 0, 0)) + + screen.blit(s, (0, 0)) + + pygame.draw.circle(screen, "green", (50, 100), 10) + pygame.draw.circle(screen, "black", (50, 100), 10, 1) + + pygame.display.flip() + try: + while True: + event = pygame.event.wait() + if event.type == pygame.QUIT: + break + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_ESCAPE or event.unicode == "q": + break + pygame.display.flip() + finally: + pygame.quit() + + +if __name__ == "__main__": + main() diff --git a/.venv/Lib/site-packages/pygame/examples/aliens.py b/.venv/Lib/site-packages/pygame/examples/aliens.py new file mode 100644 index 00000000..e0f5ed6a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/aliens.py @@ -0,0 +1,395 @@ +#!/usr/bin/env python +""" pygame.examples.aliens + +Shows a mini game where you have to defend against aliens. + +What does it show you about pygame? + +* pg.sprite, the difference between Sprite and Group. +* dirty rectangle optimization for processing for speed. +* music with pg.mixer.music, including fadeout +* sound effects with pg.Sound +* event processing, keyboard handling, QUIT handling. +* a main loop frame limited with a game clock from pg.time.Clock +* fullscreen switching. + + +Controls +-------- + +* Left and right arrows to move. +* Space bar to shoot +* f key to toggle between fullscreen. + +""" + +import os +import random +from typing import List + +# import basic pygame modules +import pygame as pg + +# see if we can load more than standard BMP +if not pg.image.get_extended(): + raise SystemExit("Sorry, extended image module required") + + +# game constants +MAX_SHOTS = 2 # most player bullets onscreen +ALIEN_ODDS = 22 # chances a new alien appears +BOMB_ODDS = 60 # chances a new bomb will drop +ALIEN_RELOAD = 12 # frames between new aliens +SCREENRECT = pg.Rect(0, 0, 640, 480) +SCORE = 0 + +main_dir = os.path.split(os.path.abspath(__file__))[0] + + +def load_image(file): + """loads an image, prepares it for play""" + file = os.path.join(main_dir, "data", file) + try: + surface = pg.image.load(file) + except pg.error: + raise SystemExit(f'Could not load image "{file}" {pg.get_error()}') + return surface.convert() + + +def load_sound(file): + """because pygame can be compiled without mixer.""" + if not pg.mixer: + return None + file = os.path.join(main_dir, "data", file) + try: + sound = pg.mixer.Sound(file) + return sound + except pg.error: + print(f"Warning, unable to load, {file}") + return None + + +# Each type of game object gets an init and an update function. +# The update function is called once per frame, and it is when each object should +# change its current position and state. +# +# The Player object actually gets a "move" function instead of update, +# since it is passed extra information about the keyboard. + + +class Player(pg.sprite.Sprite): + """Representing the player as a moon buggy type car.""" + + speed = 10 + bounce = 24 + gun_offset = -11 + images: List[pg.Surface] = [] + + def __init__(self, *groups): + pg.sprite.Sprite.__init__(self, *groups) + self.image = self.images[0] + self.rect = self.image.get_rect(midbottom=SCREENRECT.midbottom) + self.reloading = 0 + self.origtop = self.rect.top + self.facing = -1 + + def move(self, direction): + if direction: + self.facing = direction + self.rect.move_ip(direction * self.speed, 0) + self.rect = self.rect.clamp(SCREENRECT) + if direction < 0: + self.image = self.images[0] + elif direction > 0: + self.image = self.images[1] + self.rect.top = self.origtop - (self.rect.left // self.bounce % 2) + + def gunpos(self): + pos = self.facing * self.gun_offset + self.rect.centerx + return pos, self.rect.top + + +class Alien(pg.sprite.Sprite): + """An alien space ship. That slowly moves down the screen.""" + + speed = 13 + animcycle = 12 + images: List[pg.Surface] = [] + + def __init__(self, *groups): + pg.sprite.Sprite.__init__(self, *groups) + self.image = self.images[0] + self.rect = self.image.get_rect() + self.facing = random.choice((-1, 1)) * Alien.speed + self.frame = 0 + if self.facing < 0: + self.rect.right = SCREENRECT.right + + def update(self): + self.rect.move_ip(self.facing, 0) + if not SCREENRECT.contains(self.rect): + self.facing = -self.facing + self.rect.top = self.rect.bottom + 1 + self.rect = self.rect.clamp(SCREENRECT) + self.frame = self.frame + 1 + self.image = self.images[self.frame // self.animcycle % 3] + + +class Explosion(pg.sprite.Sprite): + """An explosion. Hopefully the Alien and not the player!""" + + defaultlife = 12 + animcycle = 3 + images: List[pg.Surface] = [] + + def __init__(self, actor, *groups): + pg.sprite.Sprite.__init__(self, *groups) + self.image = self.images[0] + self.rect = self.image.get_rect(center=actor.rect.center) + self.life = self.defaultlife + + def update(self): + """called every time around the game loop. + + Show the explosion surface for 'defaultlife'. + Every game tick(update), we decrease the 'life'. + + Also we animate the explosion. + """ + self.life = self.life - 1 + self.image = self.images[self.life // self.animcycle % 2] + if self.life <= 0: + self.kill() + + +class Shot(pg.sprite.Sprite): + """a bullet the Player sprite fires.""" + + speed = -11 + images: List[pg.Surface] = [] + + def __init__(self, pos, *groups): + pg.sprite.Sprite.__init__(self, *groups) + self.image = self.images[0] + self.rect = self.image.get_rect(midbottom=pos) + + def update(self): + """called every time around the game loop. + + Every tick we move the shot upwards. + """ + self.rect.move_ip(0, self.speed) + if self.rect.top <= 0: + self.kill() + + +class Bomb(pg.sprite.Sprite): + """A bomb the aliens drop.""" + + speed = 9 + images: List[pg.Surface] = [] + + def __init__(self, alien, explosion_group, *groups): + pg.sprite.Sprite.__init__(self, *groups) + self.image = self.images[0] + self.rect = self.image.get_rect(midbottom=alien.rect.move(0, 5).midbottom) + self.explosion_group = explosion_group + + def update(self): + """called every time around the game loop. + + Every frame we move the sprite 'rect' down. + When it reaches the bottom we: + + - make an explosion. + - remove the Bomb. + """ + self.rect.move_ip(0, self.speed) + if self.rect.bottom >= 470: + Explosion(self, self.explosion_group) + self.kill() + + +class Score(pg.sprite.Sprite): + """to keep track of the score.""" + + def __init__(self, *groups): + pg.sprite.Sprite.__init__(self, *groups) + self.font = pg.font.Font(None, 20) + self.font.set_italic(1) + self.color = "white" + self.lastscore = -1 + self.update() + self.rect = self.image.get_rect().move(10, 450) + + def update(self): + """We only update the score in update() when it has changed.""" + if SCORE != self.lastscore: + self.lastscore = SCORE + msg = f"Score: {SCORE}" + self.image = self.font.render(msg, 0, self.color) + + +def main(winstyle=0): + # Initialize pygame + if pg.get_sdl_version()[0] == 2: + pg.mixer.pre_init(44100, 32, 2, 1024) + pg.init() + if pg.mixer and not pg.mixer.get_init(): + print("Warning, no sound") + pg.mixer = None + + fullscreen = False + # Set the display mode + winstyle = 0 # |FULLSCREEN + bestdepth = pg.display.mode_ok(SCREENRECT.size, winstyle, 32) + screen = pg.display.set_mode(SCREENRECT.size, winstyle, bestdepth) + + # Load images, assign to sprite classes + # (do this before the classes are used, after screen setup) + img = load_image("player1.gif") + Player.images = [img, pg.transform.flip(img, 1, 0)] + img = load_image("explosion1.gif") + Explosion.images = [img, pg.transform.flip(img, 1, 1)] + Alien.images = [load_image(im) for im in ("alien1.gif", "alien2.gif", "alien3.gif")] + Bomb.images = [load_image("bomb.gif")] + Shot.images = [load_image("shot.gif")] + + # decorate the game window + icon = pg.transform.scale(Alien.images[0], (32, 32)) + pg.display.set_icon(icon) + pg.display.set_caption("Pygame Aliens") + pg.mouse.set_visible(0) + + # create the background, tile the bgd image + bgdtile = load_image("background.gif") + background = pg.Surface(SCREENRECT.size) + for x in range(0, SCREENRECT.width, bgdtile.get_width()): + background.blit(bgdtile, (x, 0)) + screen.blit(background, (0, 0)) + pg.display.flip() + + # load the sound effects + boom_sound = load_sound("boom.wav") + shoot_sound = load_sound("car_door.wav") + if pg.mixer: + music = os.path.join(main_dir, "data", "house_lo.wav") + pg.mixer.music.load(music) + pg.mixer.music.play(-1) + + # Initialize Game Groups + aliens = pg.sprite.Group() + shots = pg.sprite.Group() + bombs = pg.sprite.Group() + all = pg.sprite.RenderUpdates() + lastalien = pg.sprite.GroupSingle() + + # Create Some Starting Values + alienreload = ALIEN_RELOAD + clock = pg.time.Clock() + + # initialize our starting sprites + global SCORE + player = Player(all) + Alien( + aliens, all, lastalien + ) # note, this 'lives' because it goes into a sprite group + if pg.font: + all.add(Score(all)) + + # Run our main loop whilst the player is alive. + while player.alive(): + # get input + for event in pg.event.get(): + if event.type == pg.QUIT: + return + if event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE: + return + if event.type == pg.KEYDOWN: + if event.key == pg.K_f: + if not fullscreen: + print("Changing to FULLSCREEN") + screen_backup = screen.copy() + screen = pg.display.set_mode( + SCREENRECT.size, winstyle | pg.FULLSCREEN, bestdepth + ) + screen.blit(screen_backup, (0, 0)) + else: + print("Changing to windowed mode") + screen_backup = screen.copy() + screen = pg.display.set_mode( + SCREENRECT.size, winstyle, bestdepth + ) + screen.blit(screen_backup, (0, 0)) + pg.display.flip() + fullscreen = not fullscreen + + keystate = pg.key.get_pressed() + + # clear/erase the last drawn sprites + all.clear(screen, background) + + # update all the sprites + all.update() + + # handle player input + direction = keystate[pg.K_RIGHT] - keystate[pg.K_LEFT] + player.move(direction) + firing = keystate[pg.K_SPACE] + if not player.reloading and firing and len(shots) < MAX_SHOTS: + Shot(player.gunpos(), shots, all) + if pg.mixer and shoot_sound is not None: + shoot_sound.play() + player.reloading = firing + + # Create new alien + if alienreload: + alienreload = alienreload - 1 + elif not int(random.random() * ALIEN_ODDS): + Alien(aliens, all, lastalien) + alienreload = ALIEN_RELOAD + + # Drop bombs + if lastalien and not int(random.random() * BOMB_ODDS): + Bomb(lastalien.sprite, all, bombs, all) + + # Detect collisions between aliens and players. + for alien in pg.sprite.spritecollide(player, aliens, 1): + if pg.mixer and boom_sound is not None: + boom_sound.play() + Explosion(alien, all) + Explosion(player, all) + SCORE = SCORE + 1 + player.kill() + + # See if shots hit the aliens. + for alien in pg.sprite.groupcollide(aliens, shots, 1, 1).keys(): + if pg.mixer and boom_sound is not None: + boom_sound.play() + Explosion(alien, all) + SCORE = SCORE + 1 + + # See if alien bombs hit the player. + for bomb in pg.sprite.spritecollide(player, bombs, 1): + if pg.mixer and boom_sound is not None: + boom_sound.play() + Explosion(player, all) + Explosion(bomb, all) + player.kill() + + # draw the scene + dirty = all.draw(screen) + pg.display.update(dirty) + + # cap the framerate at 40fps. Also called 40HZ or 40 times per second. + clock.tick(40) + + if pg.mixer: + pg.mixer.music.fadeout(1000) + pg.time.wait(1000) + + +# call the "main" function if running this script +if __name__ == "__main__": + main() + pg.quit() diff --git a/.venv/Lib/site-packages/pygame/examples/arraydemo.py b/.venv/Lib/site-packages/pygame/examples/arraydemo.py new file mode 100644 index 00000000..514722bf --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/arraydemo.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python +""" pygame.examples.arraydemo + +Welcome to the arraydemo! + +Use the numpy array package to manipulate pixels. + +This demo will show you a few things: + +* scale up, scale down, flip, +* cross fade +* soften +* put stripes on it! + +""" + +import os + +import pygame as pg +from pygame import surfarray + +main_dir = os.path.split(os.path.abspath(__file__))[0] + + +def surfdemo_show(array_img, name): + "displays a surface, waits for user to continue" + screen = pg.display.set_mode(array_img.shape[:2], 0, 32) + surfarray.blit_array(screen, array_img) + pg.display.flip() + pg.display.set_caption(name) + while True: + e = pg.event.wait() + # Force application to only advance when main button is released + if e.type == pg.MOUSEBUTTONUP and e.button == pg.BUTTON_LEFT: + break + elif e.type == pg.KEYDOWN and e.key == pg.K_s: + pg.image.save(screen, name + ".png") + elif e.type == pg.QUIT: + pg.quit() + raise SystemExit() + + +def main(): + """show various surfarray effects""" + import numpy as np + from numpy import int32, uint + + pg.init() + + print("Using Numpy") + print("Press the left mouse button to advance image.") + print('Press the "s" key to save the current image.') + + # allblack + allblack = np.zeros((128, 128), int32) + surfdemo_show(allblack, "allblack") + + # striped + # the element type is required for np.zeros in numpy else + # an array of float is returned. + striped = np.zeros((128, 128, 3), int32) + striped[:] = (255, 0, 0) + striped[:, ::3] = (0, 255, 255) + surfdemo_show(striped, "striped") + + # rgbarray + imagename = os.path.join(main_dir, "data", "arraydemo.bmp") + imgsurface = pg.image.load(imagename) + rgbarray = surfarray.array3d(imgsurface) + surfdemo_show(rgbarray, "rgbarray") + + # flipped + flipped = rgbarray[:, ::-1] + surfdemo_show(flipped, "flipped") + + # scaledown + scaledown = rgbarray[::2, ::2] + surfdemo_show(scaledown, "scaledown") + + # scaleup + # the element type is required for np.zeros in numpy else + # an #array of floats is returned. + shape = rgbarray.shape + scaleup = np.zeros((shape[0] * 2, shape[1] * 2, shape[2]), int32) + scaleup[::2, ::2, :] = rgbarray + scaleup[1::2, ::2, :] = rgbarray + scaleup[:, 1::2] = scaleup[:, ::2] + surfdemo_show(scaleup, "scaleup") + + # redimg + redimg = np.array(rgbarray) + redimg[:, :, 1:] = 0 + surfdemo_show(redimg, "redimg") + + # soften + # having factor as an array forces integer upgrade during multiplication + # of rgbarray, even for numpy. + factor = np.array((8,), int32) + soften = np.array(rgbarray, int32) + soften[1:, :] += rgbarray[:-1, :] * factor + soften[:-1, :] += rgbarray[1:, :] * factor + soften[:, 1:] += rgbarray[:, :-1] * factor + soften[:, :-1] += rgbarray[:, 1:] * factor + soften //= 33 + surfdemo_show(soften, "soften") + + # crossfade (50%) + src = np.array(rgbarray) + dest = np.zeros(rgbarray.shape) # dest is float64 by default. + dest[:] = 20, 50, 100 + diff = (dest - src) * 0.50 + xfade = src + diff.astype(uint) + surfdemo_show(xfade, "xfade") + + # all done + pg.quit() + + +if __name__ == "__main__": + main() diff --git a/.venv/Lib/site-packages/pygame/examples/audiocapture.py b/.venv/Lib/site-packages/pygame/examples/audiocapture.py new file mode 100644 index 00000000..714a8aa6 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/audiocapture.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +""" pygame.examples.audiocapture + +A pygame 2 experiment. + +* record sound from a microphone +* play back the recorded sound +""" +import pygame as pg +import time + +from pygame._sdl2 import ( + get_audio_device_names, + AudioDevice, + AUDIO_F32, + AUDIO_ALLOW_FORMAT_CHANGE, +) +from pygame._sdl2.mixer import set_post_mix + + +pg.mixer.pre_init(44100, 32, 2, 512) +pg.init() + +# init_subsystem(INIT_AUDIO) +names = get_audio_device_names(True) +print(names) + +sounds = [] +sound_chunks = [] + + +def callback(audiodevice, audiomemoryview): + """This is called in the sound thread. + + Note, that the frequency and such you request may not be what you get. + """ + # print(type(audiomemoryview), len(audiomemoryview)) + # print(audiodevice) + sound_chunks.append(bytes(audiomemoryview)) + + +def postmix_callback(postmix, audiomemoryview): + """This is called in the sound thread. + + At the end of mixing we get this data. + """ + print(type(audiomemoryview), len(audiomemoryview)) + print(postmix) + + +set_post_mix(postmix_callback) + +audio = AudioDevice( + devicename=names[0], + iscapture=True, + frequency=44100, + audioformat=AUDIO_F32, + numchannels=2, + chunksize=512, + allowed_changes=AUDIO_ALLOW_FORMAT_CHANGE, + callback=callback, +) +# start recording. +audio.pause(0) + +print(audio) + +print(f"recording with '{names[0]}'") +time.sleep(5) + + +print("Turning data into a pg.mixer.Sound") +sound = pg.mixer.Sound(buffer=b"".join(sound_chunks)) + +print("playing back recorded sound") +sound.play() +time.sleep(5) +pg.quit() diff --git a/.venv/Lib/site-packages/pygame/examples/blend_fill.py b/.venv/Lib/site-packages/pygame/examples/blend_fill.py new file mode 100644 index 00000000..473da040 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/blend_fill.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python +""" pygame.examples.blend_fill + +BLEND_ing colors in different ways with Surface.fill(). + +Keyboard Controls: + +* Press R, G, B to increase the color channel values, +* 1-9 to set the step range for the increment, +* A - ADD, S- SUB, M- MULT, - MIN, + MAX to change the blend modes + +""" +import os +import pygame as pg +from pygame import K_1, K_2, K_3, K_4, K_5, K_6, K_7, K_8, K_9 + + +def usage(): + print("Press R, G, B to increase the color channel values,") + print("1-9 to set the step range for the increment,") + print("A - ADD, S- SUB, M- MULT, - MIN, + MAX") + print(" to change the blend modes") + + +main_dir = os.path.split(os.path.abspath(__file__))[0] +data_dir = os.path.join(main_dir, "data") + + +def main(): + color = [0, 0, 0] + changed = False + blendtype = 0 + step = 5 + + pg.init() + screen = pg.display.set_mode((640, 480), 0, 32) + screen.fill((100, 100, 100)) + + image = pg.image.load(os.path.join(data_dir, "liquid.bmp")).convert() + blendimage = pg.image.load(os.path.join(data_dir, "liquid.bmp")).convert() + screen.blit(image, (10, 10)) + screen.blit(blendimage, (200, 10)) + + pg.display.flip() + pg.key.set_repeat(500, 30) + usage() + + going = True + while going: + for event in pg.event.get(): + if event.type == pg.QUIT: + going = False + + if event.type == pg.KEYDOWN: + usage() + + if event.key == pg.K_ESCAPE: + going = False + + if event.key == pg.K_r: + color[0] += step + if color[0] > 255: + color[0] = 0 + changed = True + + elif event.key == pg.K_g: + color[1] += step + if color[1] > 255: + color[1] = 0 + changed = True + + elif event.key == pg.K_b: + color[2] += step + if color[2] > 255: + color[2] = 0 + changed = True + + elif event.key == pg.K_a: + blendtype = pg.BLEND_ADD + changed = True + elif event.key == pg.K_s: + blendtype = pg.BLEND_SUB + changed = True + elif event.key == pg.K_m: + blendtype = pg.BLEND_MULT + changed = True + elif event.key == pg.K_PLUS: + blendtype = pg.BLEND_MAX + changed = True + elif event.key == pg.K_MINUS: + blendtype = pg.BLEND_MIN + changed = True + + elif event.key in (K_1, K_2, K_3, K_4, K_5, K_6, K_7, K_8, K_9): + step = int(event.unicode) + + if changed: + screen.fill((100, 100, 100)) + screen.blit(image, (10, 10)) + blendimage.blit(image, (0, 0)) + # blendimage.fill (color, (0, 0, 20, 20), blendtype) + blendimage.fill(color, None, blendtype) + screen.blit(blendimage, (200, 10)) + print( + f"Color: {tuple(color)}, Pixel (0,0): {[blendimage.get_at((0, 0))]}" + ) + changed = False + pg.display.flip() + + pg.quit() + + +if __name__ == "__main__": + main() diff --git a/.venv/Lib/site-packages/pygame/examples/blit_blends.py b/.venv/Lib/site-packages/pygame/examples/blit_blends.py new file mode 100644 index 00000000..e5f64a2c --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/blit_blends.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python +""" pygame.examples.blit_blends + +Blending colors in different ways with different blend modes. + +It also shows some tricks with the surfarray. +Including how to do additive blending. + + +Keyboard Controls +----------------- + +* R, G, B - add a bit of Red, Green, or Blue. +* A - Add blend mode +* S - Subtractive blend mode +* M - Multiply blend mode +* = key BLEND_MAX blend mode. +* - key BLEND_MIN blend mode. +* 1, 2, 3, 4 - use different images. + +""" +import os +import pygame as pg +import time + +main_dir = os.path.split(os.path.abspath(__file__))[0] +data_dir = os.path.join(main_dir, "data") + +try: + import pygame.surfarray + import numpy +except ImportError: + print("no surfarray for you! install numpy") + + +def main(): + pg.init() + pg.mixer.quit() # remove ALSA underflow messages for Debian squeeze + screen = pg.display.set_mode((640, 480)) + + im1 = pg.Surface(screen.get_size()) + # im1= im1.convert() + im1.fill((100, 0, 0)) + + im2 = pg.Surface(screen.get_size()) + im2.fill((0, 50, 0)) + # we make a srcalpha copy of it. + # im3= im2.convert(SRCALPHA) + im3 = im2 + im3.set_alpha(127) + + images = {} + images[pg.K_1] = im2 + images[pg.K_2] = pg.image.load(os.path.join(data_dir, "chimp.png")) + images[pg.K_3] = pg.image.load(os.path.join(data_dir, "alien3.gif")) + images[pg.K_4] = pg.image.load(os.path.join(data_dir, "liquid.bmp")) + img_to_blit = im2.convert() + iaa = img_to_blit.convert_alpha() + + blits = {} + blits[pg.K_a] = pg.BLEND_ADD + blits[pg.K_s] = pg.BLEND_SUB + blits[pg.K_m] = pg.BLEND_MULT + blits[pg.K_EQUALS] = pg.BLEND_MAX + blits[pg.K_MINUS] = pg.BLEND_MIN + + blitsn = {} + blitsn[pg.K_a] = "BLEND_ADD" + blitsn[pg.K_s] = "BLEND_SUB" + blitsn[pg.K_m] = "BLEND_MULT" + blitsn[pg.K_EQUALS] = "BLEND_MAX" + blitsn[pg.K_MINUS] = "BLEND_MIN" + + screen.blit(im1, (0, 0)) + pg.display.flip() + clock = pg.time.Clock() + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + + going = True + while going: + clock.tick(60) + + for event in pg.event.get(): + if event.type == pg.QUIT: + going = False + if event.type == pg.KEYDOWN: + usage() + + if event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE: + going = False + + elif event.type == pg.KEYDOWN and event.key in images.keys(): + img_to_blit = images[event.key] + iaa = img_to_blit.convert_alpha() + + elif event.type == pg.KEYDOWN and event.key in blits.keys(): + t1 = time.time() + # blits is a dict keyed with key -> blit flag. eg BLEND_ADD. + im1.blit(img_to_blit, (0, 0), None, blits[event.key]) + t2 = time.time() + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + print(f"time to do:{t2 - t1}:") + + elif event.type == pg.KEYDOWN and event.key in [pg.K_t]: + for bkey in blits.keys(): + t1 = time.time() + + for x in range(300): + im1.blit(img_to_blit, (0, 0), None, blits[bkey]) + + t2 = time.time() + + # show which key we're doing... + onedoing = blitsn[bkey] + print(f"time to do :{onedoing}: is :{t2 - t1}:") + + elif event.type == pg.KEYDOWN and event.key in [pg.K_o]: + t1 = time.time() + # blits is a dict keyed with key -> blit flag. eg BLEND_ADD. + im1.blit(iaa, (0, 0)) + t2 = time.time() + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + print(f"time to do:{t2 - t1}:") + + elif event.type == pg.KEYDOWN and event.key == pg.K_SPACE: + # this additive blend without clamp two surfaces. + # im1.set_alpha(127) + # im1.blit(im1, (0,0)) + # im1.set_alpha(255) + t1 = time.time() + + im1p = pygame.surfarray.pixels2d(im1) + im2p = pygame.surfarray.pixels2d(im2) + im1p += im2p + del im1p + del im2p + t2 = time.time() + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + print(f"time to do:{t2 - t1}:") + + elif event.type == pg.KEYDOWN and event.key in [pg.K_z]: + t1 = time.time() + im1p = pygame.surfarray.pixels3d(im1) + im2p = pygame.surfarray.pixels3d(im2) + im1p16 = im1p.astype(numpy.uint16) + im2p16 = im1p.astype(numpy.uint16) + im1p16 += im2p16 + im1p16 = numpy.minimum(im1p16, 255) + pygame.surfarray.blit_array(im1, im1p16) + + del im1p + del im2p + t2 = time.time() + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + print(f"time to do:{t2 - t1}:") + + elif event.type == pg.KEYDOWN and event.key in [pg.K_r, pg.K_g, pg.K_b]: + # this adds one to each pixel. + colmap = {} + colmap[pg.K_r] = 0x10000 + colmap[pg.K_g] = 0x00100 + colmap[pg.K_b] = 0x00001 + im1p = pygame.surfarray.pixels2d(im1) + im1p += colmap[event.key] + del im1p + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + + elif event.type == pg.KEYDOWN and event.key == pg.K_p: + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + + elif event.type == pg.KEYDOWN and event.key == pg.K_f: + # this additive blend without clamp two surfaces. + + t1 = time.time() + im1.set_alpha(127) + im1.blit(im2, (0, 0)) + im1.set_alpha(255) + + t2 = time.time() + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + print(f"time to do:{t2 - t1}:") + + screen.blit(im1, (0, 0)) + pg.display.flip() + + pg.quit() + + +def usage(): + print("press keys 1-5 to change image to blit.") + print("A - ADD, S- SUB, M- MULT, - MIN, + MAX") + print("T - timing test for special blend modes.") + + +if __name__ == "__main__": + usage() + main() diff --git a/.venv/Lib/site-packages/pygame/examples/camera.py b/.venv/Lib/site-packages/pygame/examples/camera.py new file mode 100644 index 00000000..cfb91d2b --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/camera.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +""" pygame.examples.camera + +Basic image capturing and display using pygame.camera + +Keyboard controls +----------------- + +- 0, start camera 0. +- 1, start camera 1. +- 9, start camera 9. +- 10, start camera... wait a minute! There's not 10 key! +""" +import pygame as pg +import pygame.camera + + +class VideoCapturePlayer: + size = (640, 480) + + def __init__(self, **argd): + self.__dict__.update(**argd) + super().__init__(**argd) + + # create a display surface. standard pygame stuff + self.display = pg.display.set_mode(self.size) + self.init_cams(0) + + def init_cams(self, which_cam_idx): + # gets a list of available cameras. + self.clist = pygame.camera.list_cameras() + + # ensure a camera exists + if not self.clist: + raise ValueError("Sorry, no cameras detected.") + + # check to see if the camera id exists. If not, default to the first one in the list. + try: + cam_id = self.clist[which_cam_idx] + except IndexError: + cam_id = self.clist[0] + + # creates the camera of the specified size and in RGB colorspace + self.camera = pygame.camera.Camera(cam_id, self.size, "RGB") + + # starts the camera + self.camera.start() + + self.clock = pg.time.Clock() + + # create a surface to capture to. for performance purposes, you want the + # bit depth to be the same as that of the display surface. + self.snapshot = pg.surface.Surface(self.size, 0, self.display) + # return the name of the camera being used, to be included in the window name + return cam_id + + def get_and_flip(self): + # if you don't want to tie the framerate to the camera, you can check and + # see if the camera has an image ready. note that while this works + # on most cameras, some will never return true. + + self.snapshot = self.camera.get_image(self.display) + + # if 0 and self.camera.query_image(): + # # capture an image + + # self.snapshot = self.camera.get_image(self.snapshot) + + # if 0: + # self.snapshot = self.camera.get_image(self.snapshot) + # # self.snapshot = self.camera.get_image() + + # # blit it to the display surface. simple! + # self.display.blit(self.snapshot, (0, 0)) + # else: + + # self.snapshot = self.camera.get_image(self.display) + # # self.display.blit(self.snapshot, (0,0)) + + pg.display.flip() + + def main(self): + # get the camera list. If there are no cameras, raise a value error. + clist = pygame.camera.list_cameras() + if not clist: + raise ValueError("Sorry, no cameras detected.") + # get the first camera, as this is the default. We want the display to contain the name of the camera. + camera = clist[0] + + # create a list of options for the user to easily understand. + print( + "\nPress the associated number for the desired camera to see that display!" + ) + print("(Selecting a camera that does not exist will default to camera 0)") + for index, cam in enumerate(clist): + print(f"[{index}]: {cam}") + + going = True + while going: + events = pg.event.get() + for e in events: + if e.type == pg.QUIT or (e.type == pg.KEYDOWN and e.key == pg.K_ESCAPE): + going = False + if e.type == pg.KEYDOWN: + if e.key in range(pg.K_0, pg.K_0 + 10): + camera = self.init_cams(e.key - pg.K_0) + + self.get_and_flip() + self.clock.tick() + pygame.display.set_caption(f"{camera} ({self.clock.get_fps():.2f} FPS)") + + +def main(): + pg.init() + pygame.camera.init() + VideoCapturePlayer().main() + pg.quit() + + +if __name__ == "__main__": + main() diff --git a/.venv/Lib/site-packages/pygame/examples/chimp.py b/.venv/Lib/site-packages/pygame/examples/chimp.py new file mode 100644 index 00000000..b3373ecd --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/chimp.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python +""" pygame.examples.chimp + +This simple example is used for the line-by-line tutorial +that comes with pygame. It is based on a 'popular' web banner. +Note there are comments here, but for the full explanation, +follow along in the tutorial. +""" + + +# Import Modules +import os +import pygame as pg + +if not pg.font: + print("Warning, fonts disabled") +if not pg.mixer: + print("Warning, sound disabled") + +main_dir = os.path.split(os.path.abspath(__file__))[0] +data_dir = os.path.join(main_dir, "data") + + +# functions to create our resources +def load_image(name, colorkey=None, scale=1): + fullname = os.path.join(data_dir, name) + image = pg.image.load(fullname) + image = image.convert() + + size = image.get_size() + size = (size[0] * scale, size[1] * scale) + image = pg.transform.scale(image, size) + + if colorkey is not None: + if colorkey == -1: + colorkey = image.get_at((0, 0)) + image.set_colorkey(colorkey, pg.RLEACCEL) + return image, image.get_rect() + + +def load_sound(name): + class NoneSound: + def play(self): + pass + + if not pg.mixer or not pg.mixer.get_init(): + return NoneSound() + + fullname = os.path.join(data_dir, name) + sound = pg.mixer.Sound(fullname) + + return sound + + +# classes for our game objects +class Fist(pg.sprite.Sprite): + """moves a clenched fist on the screen, following the mouse""" + + def __init__(self): + pg.sprite.Sprite.__init__(self) # call Sprite initializer + self.image, self.rect = load_image("fist.png", -1) + self.fist_offset = (-235, -80) + self.punching = False + + def update(self): + """move the fist based on the mouse position""" + pos = pg.mouse.get_pos() + self.rect.topleft = pos + self.rect.move_ip(self.fist_offset) + if self.punching: + self.rect.move_ip(15, 25) + + def punch(self, target): + """returns true if the fist collides with the target""" + if not self.punching: + self.punching = True + hitbox = self.rect.inflate(-5, -5) + return hitbox.colliderect(target.rect) + + def unpunch(self): + """called to pull the fist back""" + self.punching = False + + +class Chimp(pg.sprite.Sprite): + """moves a monkey critter across the screen. it can spin the + monkey when it is punched.""" + + def __init__(self): + pg.sprite.Sprite.__init__(self) # call Sprite initializer + self.image, self.rect = load_image("chimp.png", -1, 4) + screen = pg.display.get_surface() + self.area = screen.get_rect() + self.rect.topleft = 10, 90 + self.move = 18 + self.dizzy = False + + def update(self): + """walk or spin, depending on the monkeys state""" + if self.dizzy: + self._spin() + else: + self._walk() + + def _walk(self): + """move the monkey across the screen, and turn at the ends""" + newpos = self.rect.move((self.move, 0)) + if not self.area.contains(newpos): + if self.rect.left < self.area.left or self.rect.right > self.area.right: + self.move = -self.move + newpos = self.rect.move((self.move, 0)) + self.image = pg.transform.flip(self.image, True, False) + self.rect = newpos + + def _spin(self): + """spin the monkey image""" + center = self.rect.center + self.dizzy = self.dizzy + 12 + if self.dizzy >= 360: + self.dizzy = False + self.image = self.original + else: + rotate = pg.transform.rotate + self.image = rotate(self.original, self.dizzy) + self.rect = self.image.get_rect(center=center) + + def punched(self): + """this will cause the monkey to start spinning""" + if not self.dizzy: + self.dizzy = True + self.original = self.image + + +def main(): + """this function is called when the program starts. + it initializes everything it needs, then runs in + a loop until the function returns.""" + # Initialize Everything + pg.init() + screen = pg.display.set_mode((1280, 480), pg.SCALED) + pg.display.set_caption("Monkey Fever") + pg.mouse.set_visible(False) + + # Create The Background + background = pg.Surface(screen.get_size()) + background = background.convert() + background.fill((170, 238, 187)) + + # Put Text On The Background, Centered + if pg.font: + font = pg.font.Font(None, 64) + text = font.render("Pummel The Chimp, And Win $$$", True, (10, 10, 10)) + textpos = text.get_rect(centerx=background.get_width() / 2, y=10) + background.blit(text, textpos) + + # Display The Background + screen.blit(background, (0, 0)) + pg.display.flip() + + # Prepare Game Objects + whiff_sound = load_sound("whiff.wav") + punch_sound = load_sound("punch.wav") + chimp = Chimp() + fist = Fist() + allsprites = pg.sprite.RenderPlain((chimp, fist)) + clock = pg.time.Clock() + + # Main Loop + going = True + while going: + clock.tick(60) + + # Handle Input Events + for event in pg.event.get(): + if event.type == pg.QUIT: + going = False + elif event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE: + going = False + elif event.type == pg.MOUSEBUTTONDOWN: + if fist.punch(chimp): + punch_sound.play() # punch + chimp.punched() + else: + whiff_sound.play() # miss + elif event.type == pg.MOUSEBUTTONUP: + fist.unpunch() + + allsprites.update() + + # Draw Everything + screen.blit(background, (0, 0)) + allsprites.draw(screen) + pg.display.flip() + + pg.quit() + + +# Game Over + + +# this calls the 'main' function when this script is executed +if __name__ == "__main__": + main() diff --git a/.venv/Lib/site-packages/pygame/examples/cursors.py b/.venv/Lib/site-packages/pygame/examples/cursors.py new file mode 100644 index 00000000..5778c5bb --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/cursors.py @@ -0,0 +1,259 @@ +#!/usr/bin/env python +""" pygame.examples.cursors +Click a button and the cursor will change. +This example will show you: +*The different types of cursors that exist +*How to create a cursor +*How to set a cursor +*How to make a simple button +""" + +import pygame as pg +import os + + +# Create a system cursor + +system_cursor1 = pg.SYSTEM_CURSOR_CROSSHAIR +system_cursor2 = pg.SYSTEM_CURSOR_HAND +system_cursor3 = pg.SYSTEM_CURSOR_IBEAM + + +# Create a color cursor + +surf = pg.Surface((40, 40)) +surf.fill((120, 50, 50)) +color_cursor = pg.cursors.Cursor((20, 20), surf) + + +# Create a color cursor with an image surface + +main_dir = os.path.split(os.path.abspath(__file__))[0] +image_name = os.path.join(main_dir, "data", "cursor.png") +image = pg.image.load(image_name) +image_cursor = pg.cursors.Cursor( + (image.get_width() // 2, image.get_height() // 2), image +) + + +# Create a bitmap cursor from simple strings + +# sized 24x24 +thickarrow_strings = ( + "XX ", + "XXX ", + "XXXX ", + "XX.XX ", + "XX..XX ", + "XX...XX ", + "XX....XX ", + "XX.....XX ", + "XX......XX ", + "XX.......XX ", + "XX........XX ", + "XX........XXX ", + "XX......XXXXX ", + "XX.XXX..XX ", + "XXXX XX..XX ", + "XX XX..XX ", + " XX..XX ", + " XX..XX ", + " XX..XX ", + " XXXX ", + " XX ", + " ", + " ", + " ", +) + +bitmap_cursor1 = pg.cursors.Cursor( + (24, 24), + (0, 0), + *pg.cursors.compile(thickarrow_strings, black="X", white=".", xor="o"), +) + + +# Create a bitmap cursor from premade simple strings + +bitmap_cursor2 = pg.cursors.diamond + + +# Calculate if mouse position is inside circle +def check_circle(mouse_pos_x, mouse_pos_y, center_x, center_y, radius): + return (mouse_pos_x - center_x) ** 2 + (mouse_pos_y - center_y) ** 2 < radius**2 + + +def main(): + pg.init() + pg.display.set_caption("Cursors Example") + + pg.font.init() + font = pg.font.Font(None, 30) + font1 = pg.font.Font(None, 24) + + bg = pg.display.set_mode((500, 400)) + bg.fill((183, 201, 226)) + + # Initialize circles + radius1 = 40 + radius2 = 40 + radius3 = 40 + radius4 = 40 + radius5 = 40 + radius6 = 40 + radius7 = 40 + + pos_x1 = 82 + pos_x2 = 138 + pos_x3 = 194 + pos_x4 = 250 + pos_x5 = 306 + pos_x6 = 362 + pos_x7 = 418 + + pos_y1 = 140 + pos_y2 = 220 + pos_y3 = 140 + pos_y4 = 220 + pos_y5 = 140 + pos_y6 = 220 + pos_y7 = 140 + + circle1 = pg.draw.circle(bg, (255, 255, 255), (pos_x1, pos_y1), radius1) + circle2 = pg.draw.circle(bg, (255, 255, 255), (pos_x2, pos_y2), radius2) + circle3 = pg.draw.circle(bg, (255, 255, 255), (pos_x3, pos_y3), radius3) + circle4 = pg.draw.circle(bg, (255, 255, 255), (pos_x4, pos_y4), radius4) + circle5 = pg.draw.circle(bg, (255, 255, 255), (pos_x5, pos_y5), radius5) + circle6 = pg.draw.circle(bg, (255, 255, 255), (pos_x6, pos_y6), radius6) + circle7 = pg.draw.circle(bg, (255, 255, 255), (pos_x7, pos_y7), radius7) + + # Initialize button + button_text = font1.render("Click here to change cursor", True, (0, 0, 0)) + button = pg.draw.rect( + bg, + (180, 180, 180), + (139, 300, button_text.get_width() + 5, button_text.get_height() + 50), + ) + button_text_rect = button_text.get_rect(center=button.center) + bg.blit(button_text, button_text_rect) + + pg.display.update() + + cursors = [ + system_cursor1, + color_cursor, + system_cursor2, + image_cursor, + system_cursor3, + bitmap_cursor1, + bitmap_cursor2, + ] + + index = 0 + pg.mouse.set_cursor(cursors[index]) + + pressed = False + clock = pg.time.Clock() + + while True: + clock.tick(50) + + mouse_x, mouse_y = pg.mouse.get_pos() + + # Check if mouse is inside a circle to change its color + if check_circle(mouse_x, mouse_y, circle1.centerx, circle1.centery, radius1): + circle1 = pg.draw.circle(bg, (255, 0, 0), (pos_x1, pos_y1), radius1) + else: + circle1 = pg.draw.circle(bg, (255, 255, 255), (pos_x1, pos_y1), radius1) + + if check_circle(mouse_x, mouse_y, circle2.centerx, circle2.centery, radius2): + circle2 = pg.draw.circle(bg, (255, 127, 0), (pos_x2, pos_y2), radius2) + else: + circle2 = pg.draw.circle(bg, (255, 255, 255), (pos_x2, pos_y2), radius2) + + if check_circle(mouse_x, mouse_y, circle3.centerx, circle3.centery, radius3): + circle3 = pg.draw.circle(bg, (255, 255, 0), (pos_x3, pos_y3), radius3) + else: + circle3 = pg.draw.circle(bg, (255, 255, 255), (pos_x3, pos_y3), radius3) + + if check_circle(mouse_x, mouse_y, circle4.centerx, circle4.centery, radius3): + circle4 = pg.draw.circle(bg, (0, 255, 0), (pos_x4, pos_y4), radius4) + else: + circle4 = pg.draw.circle(bg, (255, 255, 255), (pos_x4, pos_y4), radius4) + + if check_circle(mouse_x, mouse_y, circle5.centerx, circle5.centery, radius4): + circle5 = pg.draw.circle(bg, (0, 0, 255), (pos_x5, pos_y5), radius5) + else: + circle5 = pg.draw.circle(bg, (255, 255, 255), (pos_x5, pos_y5), radius5) + + if check_circle(mouse_x, mouse_y, circle6.centerx, circle6.centery, radius6): + circle6 = pg.draw.circle(bg, (75, 0, 130), (pos_x6, pos_y6), radius6) + else: + circle6 = pg.draw.circle(bg, (255, 255, 255), (pos_x6, pos_y6), radius6) + + if check_circle(mouse_x, mouse_y, circle7.centerx, circle7.centery, radius7): + circle7 = pg.draw.circle(bg, (148, 0, 211), (pos_x7, pos_y7), radius7) + else: + circle7 = pg.draw.circle(bg, (255, 255, 255), (pos_x7, pos_y7), radius7) + + bg.fill((183, 201, 226), (0, 15, bg.get_width(), 50)) + text1 = font.render( + (f"This is a {pg.mouse.get_cursor().type} cursor"), True, (0, 0, 0) + ) + text_rect1 = text1.get_rect(center=(bg.get_width() / 2, 40)) + bg.blit(text1, text_rect1) + + button = pg.draw.rect( + bg, + (100, 149, 240), + (139, 300, button_text.get_width() + 5, button_text.get_height() + 50), + ) + bg.blit(button_text, button_text_rect) + + # Check if button was clicked and change cursor + if button.collidepoint(mouse_x, mouse_y): + button = pg.draw.rect( + bg, + (60, 100, 255), + ( + 139, + 300, + button_text.get_width() + 5, + button_text.get_height() + 50, + ), + ) + bg.blit(button_text, button_text_rect) + + if pg.mouse.get_pressed()[0] == 1 and pressed == False: + button = pg.draw.rect( + bg, + (0, 0, 139), + ( + 139, + 300, + button_text.get_width() + 5, + button_text.get_height() + 50, + ), + ) + bg.blit(button_text, button_text_rect) + index += 1 + index %= len(cursors) + pg.mouse.set_cursor(cursors[index]) + pg.display.update() + pg.time.delay(40) + + if pg.mouse.get_pressed()[0] == 1: + pressed = True + elif pg.mouse.get_pressed()[0] == 0: + pressed = False + + for event in pg.event.get(): + if event.type == pg.QUIT: + pg.quit() + raise SystemExit + + pg.display.update() + + +if __name__ == "__main__": + main() diff --git a/.venv/Lib/site-packages/pygame/examples/data/BGR.png b/.venv/Lib/site-packages/pygame/examples/data/BGR.png new file mode 100644 index 00000000..f5dba748 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/BGR.png differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/alien1.gif b/.venv/Lib/site-packages/pygame/examples/data/alien1.gif new file mode 100644 index 00000000..c4497e08 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/alien1.gif differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/alien1.jpg b/.venv/Lib/site-packages/pygame/examples/data/alien1.jpg new file mode 100644 index 00000000..6d110a45 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/alien1.jpg differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/alien1.png b/.venv/Lib/site-packages/pygame/examples/data/alien1.png new file mode 100644 index 00000000..471d6a44 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/alien1.png differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/alien2.gif b/.venv/Lib/site-packages/pygame/examples/data/alien2.gif new file mode 100644 index 00000000..8df05a34 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/alien2.gif differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/alien2.png b/.venv/Lib/site-packages/pygame/examples/data/alien2.png new file mode 100644 index 00000000..aef5ace8 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/alien2.png differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/alien3.gif b/.venv/Lib/site-packages/pygame/examples/data/alien3.gif new file mode 100644 index 00000000..5305d419 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/alien3.gif differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/alien3.png b/.venv/Lib/site-packages/pygame/examples/data/alien3.png new file mode 100644 index 00000000..90d0f7ce Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/alien3.png differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/arraydemo.bmp b/.venv/Lib/site-packages/pygame/examples/data/arraydemo.bmp new file mode 100644 index 00000000..ad96338e Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/arraydemo.bmp differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/asprite.bmp b/.venv/Lib/site-packages/pygame/examples/data/asprite.bmp new file mode 100644 index 00000000..cc96356a Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/asprite.bmp differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/background.gif b/.venv/Lib/site-packages/pygame/examples/data/background.gif new file mode 100644 index 00000000..5041ce66 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/background.gif differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/black.ppm b/.venv/Lib/site-packages/pygame/examples/data/black.ppm new file mode 100644 index 00000000..698a52c1 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/data/black.ppm @@ -0,0 +1,3076 @@ +P3 +# Created by GIMP version 2.10.20 PNM plug-in +32 32 +255 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 diff --git a/.venv/Lib/site-packages/pygame/examples/data/blue.gif b/.venv/Lib/site-packages/pygame/examples/data/blue.gif new file mode 100644 index 00000000..98c6fd65 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/blue.gif differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/blue.mpg b/.venv/Lib/site-packages/pygame/examples/data/blue.mpg new file mode 100644 index 00000000..60dcecaf Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/blue.mpg differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/bomb.gif b/.venv/Lib/site-packages/pygame/examples/data/bomb.gif new file mode 100644 index 00000000..f885cbb7 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/bomb.gif differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/boom.wav b/.venv/Lib/site-packages/pygame/examples/data/boom.wav new file mode 100644 index 00000000..ad23adc0 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/data/boom.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:91fa16b345550c61efd0949ae3a9d76411b2c3bd3e8e97cfabf077d6a340fc5f +size 12562 diff --git a/.venv/Lib/site-packages/pygame/examples/data/brick.png b/.venv/Lib/site-packages/pygame/examples/data/brick.png new file mode 100644 index 00000000..cfe37a39 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/brick.png differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/car_door.wav b/.venv/Lib/site-packages/pygame/examples/data/car_door.wav new file mode 100644 index 00000000..d540837e --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/data/car_door.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4f061656a99ee4da955480f53787acf76452284753624c5b371e9d35a98afbfe +size 3910 diff --git a/.venv/Lib/site-packages/pygame/examples/data/chimp.png b/.venv/Lib/site-packages/pygame/examples/data/chimp.png new file mode 100644 index 00000000..9bf37b1a Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/chimp.png differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/city.png b/.venv/Lib/site-packages/pygame/examples/data/city.png new file mode 100644 index 00000000..202da5cf Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/city.png differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/crimson.pnm b/.venv/Lib/site-packages/pygame/examples/data/crimson.pnm new file mode 100644 index 00000000..28501e94 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/data/crimson.pnm @@ -0,0 +1,5 @@ +P6 +# CREATOR: GIMP PNM Filter Version 1.1 +32 32 +255 +<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< \ No newline at end of file diff --git a/.venv/Lib/site-packages/pygame/examples/data/cursor.png b/.venv/Lib/site-packages/pygame/examples/data/cursor.png new file mode 100644 index 00000000..9e2690d2 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/cursor.png differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/danger.gif b/.venv/Lib/site-packages/pygame/examples/data/danger.gif new file mode 100644 index 00000000..106d69c0 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/danger.gif differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/explosion1.gif b/.venv/Lib/site-packages/pygame/examples/data/explosion1.gif new file mode 100644 index 00000000..fabec160 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/explosion1.gif differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/fist.png b/.venv/Lib/site-packages/pygame/examples/data/fist.png new file mode 100644 index 00000000..9097629f Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/fist.png differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/green.pcx b/.venv/Lib/site-packages/pygame/examples/data/green.pcx new file mode 100644 index 00000000..c0aea8de Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/green.pcx differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/grey.pgm b/.venv/Lib/site-packages/pygame/examples/data/grey.pgm new file mode 100644 index 00000000..b181a5df --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/data/grey.pgm @@ -0,0 +1,1028 @@ +P2 +# Created by GIMP version 2.10.20 PNM plug-in +32 32 +255 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 +120 diff --git a/.venv/Lib/site-packages/pygame/examples/data/house_lo.mp3 b/.venv/Lib/site-packages/pygame/examples/data/house_lo.mp3 new file mode 100644 index 00000000..4c26994a Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/house_lo.mp3 differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/house_lo.ogg b/.venv/Lib/site-packages/pygame/examples/data/house_lo.ogg new file mode 100644 index 00000000..e050848e Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/house_lo.ogg differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/house_lo.wav b/.venv/Lib/site-packages/pygame/examples/data/house_lo.wav new file mode 100644 index 00000000..0ac52fa3 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/data/house_lo.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0750707c568f22c4b169ab21fa281523f604f5241dd410974539d200ab0dba76 +size 78464 diff --git a/.venv/Lib/site-packages/pygame/examples/data/laplacian.png b/.venv/Lib/site-packages/pygame/examples/data/laplacian.png new file mode 100644 index 00000000..8d064f57 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/laplacian.png differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/liquid.bmp b/.venv/Lib/site-packages/pygame/examples/data/liquid.bmp new file mode 100644 index 00000000..c4f12ebb Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/liquid.bmp differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/midikeys.png b/.venv/Lib/site-packages/pygame/examples/data/midikeys.png new file mode 100644 index 00000000..74ecb86b Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/midikeys.png differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/player1.gif b/.venv/Lib/site-packages/pygame/examples/data/player1.gif new file mode 100644 index 00000000..6c4eda74 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/player1.gif differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/punch.wav b/.venv/Lib/site-packages/pygame/examples/data/punch.wav new file mode 100644 index 00000000..07c859eb --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/data/punch.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:034175c53f1a219e9a348ff931c32a2f281bd447e6748ccf8b8916914e04c107 +size 4176 diff --git a/.venv/Lib/site-packages/pygame/examples/data/purple.xpm b/.venv/Lib/site-packages/pygame/examples/data/purple.xpm new file mode 100644 index 00000000..7798cc16 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/data/purple.xpm @@ -0,0 +1,36 @@ +/* XPM */ +static char * C:\Users\Kristof\Documents\purple_xpm[] = { +"32 32 1 1", +" c #FF00FF", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/.venv/Lib/site-packages/pygame/examples/data/red.jpg b/.venv/Lib/site-packages/pygame/examples/data/red.jpg new file mode 100644 index 00000000..11a9aa0c Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/red.jpg differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/sans.ttf b/.venv/Lib/site-packages/pygame/examples/data/sans.ttf new file mode 100644 index 00000000..09fac2ff Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/sans.ttf differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/scarlet.webp b/.venv/Lib/site-packages/pygame/examples/data/scarlet.webp new file mode 100644 index 00000000..cd0a15cd Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/scarlet.webp differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/secosmic_lo.wav b/.venv/Lib/site-packages/pygame/examples/data/secosmic_lo.wav new file mode 100644 index 00000000..45dd0982 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/data/secosmic_lo.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f842059338fb939a84a86d389fb9a7506529d52b32089e27d3c4f33d3eb4d036 +size 18700 diff --git a/.venv/Lib/site-packages/pygame/examples/data/shot.gif b/.venv/Lib/site-packages/pygame/examples/data/shot.gif new file mode 100644 index 00000000..18de5280 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/shot.gif differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/static.png b/.venv/Lib/site-packages/pygame/examples/data/static.png new file mode 100644 index 00000000..fb3b0572 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/static.png differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/teal.svg b/.venv/Lib/site-packages/pygame/examples/data/teal.svg new file mode 100644 index 00000000..85f41492 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/data/teal.svg @@ -0,0 +1,9 @@ + + teal + + + Layer 1 + + + + diff --git a/.venv/Lib/site-packages/pygame/examples/data/turquoise.tif b/.venv/Lib/site-packages/pygame/examples/data/turquoise.tif new file mode 100644 index 00000000..39b3620f Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/turquoise.tif differ diff --git a/.venv/Lib/site-packages/pygame/examples/data/whiff.wav b/.venv/Lib/site-packages/pygame/examples/data/whiff.wav new file mode 100644 index 00000000..28135e20 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/data/whiff.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:14c58cdd79d8b5c7ba98715740260f8334fec6ef90e032726d97e93e31bc7291 +size 5850 diff --git a/.venv/Lib/site-packages/pygame/examples/data/yellow.tga b/.venv/Lib/site-packages/pygame/examples/data/yellow.tga new file mode 100644 index 00000000..d0124fe5 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/examples/data/yellow.tga differ diff --git a/.venv/Lib/site-packages/pygame/examples/dropevent.py b/.venv/Lib/site-packages/pygame/examples/dropevent.py new file mode 100644 index 00000000..d1596166 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/dropevent.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +""" pygame.examples.dropfile + +Drag and drop an image on here. + +Uses these events: + +* DROPBEGIN +* DROPCOMPLETE +* DROPTEXT +* DROPFILE +""" +import pygame as pg + + +def main(): + pg.init() + + going = True + surf = pg.display.set_mode((640, 480)) + font = pg.font.SysFont("Arial", 24) + clock = pg.time.Clock() + + spr_file_text = font.render("Drag and drop a file or image!", 1, (255, 255, 255)) + spr_file_text_rect = spr_file_text.get_rect() + spr_file_text_rect.center = surf.get_rect().center + + spr_file_image = None + spr_file_image_rect = None + + while going: + for ev in pg.event.get(): + if ev.type == pg.QUIT: + going = False + elif ev.type == pg.DROPBEGIN: + print(ev) + print("File drop begin!") + elif ev.type == pg.DROPCOMPLETE: + print(ev) + print("File drop complete!") + elif ev.type == pg.DROPTEXT: + print(ev) + spr_file_text = font.render(ev.text, 1, (255, 255, 255)) + spr_file_text_rect = spr_file_text.get_rect() + spr_file_text_rect.center = surf.get_rect().center + elif ev.type == pg.DROPFILE: + print(ev) + spr_file_text = font.render(ev.file, 1, (255, 255, 255)) + spr_file_text_rect = spr_file_text.get_rect() + spr_file_text_rect.center = surf.get_rect().center + + # Try to open the file if it's an image + filetype = ev.file[-3:] + if filetype in ["png", "bmp", "jpg"]: + spr_file_image = pg.image.load(ev.file).convert() + spr_file_image.set_alpha(127) + spr_file_image_rect = spr_file_image.get_rect() + spr_file_image_rect.center = surf.get_rect().center + + surf.fill((0, 0, 0)) + surf.blit(spr_file_text, spr_file_text_rect) + if spr_file_image and spr_file_image_rect is not None: + surf.blit(spr_file_image, spr_file_image_rect) + + pg.display.flip() + clock.tick(30) + + pg.quit() + + +if __name__ == "__main__": + main() diff --git a/.venv/Lib/site-packages/pygame/examples/eventlist.py b/.venv/Lib/site-packages/pygame/examples/eventlist.py new file mode 100644 index 00000000..3afe5bc9 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/eventlist.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python +""" pygame.examples.eventlist + +Learn about pygame events and input. + +At the top of the screen are the state of several device values, +and a scrolling list of events are displayed on the bottom. + +""" + +usage = """ +Mouse Controls +============== + +- 1st button on mouse (left click) to toggle events 'grabed'. +- 3rd button on mouse (right click) to toggle mouse visible. +- The window can be resized. +- Mouse the mouse around to see mouse events. +- If events grabbed and mouse invisible show virtual mouse coords. + + +Keyboard Joystick Controls +========================== + +- press keys up an down to see events. +- you can see joystick events if any are plugged in. +- press "c" to toggle events generated by controllers. +""" + +from typing import List +import pygame as pg + +import pygame._sdl2.controller + + +img_on_off: List[pg.Surface] = [] +font: pg.font.Font +last_key = None + +# these are a running counter of mouse.get_rel() calls. +virtual_x = 0 +virtual_y = 0 + + +def showtext(win, pos, text, color, bgcolor): + textimg = font.render(text, 1, color, bgcolor) + win.blit(textimg, pos) + return pos[0] + textimg.get_width() + 5, pos[1] + + +def drawstatus(win): + global virtual_x, virtual_y + bgcolor = 50, 50, 50 + win.fill(bgcolor, (0, 0, 640, 120)) + win.blit(font.render("Status Area", 1, (155, 155, 155), bgcolor), (2, 2)) + + pos = showtext(win, (10, 30), "Mouse Focus", (255, 255, 255), bgcolor) + win.blit(img_on_off[pg.mouse.get_focused()], pos) + + pos = showtext( + win, (pos[0] + 50, pos[1]), "Mouse visible", (255, 255, 255), bgcolor + ) + win.blit(img_on_off[pg.mouse.get_visible()], pos) + + pos = showtext(win, (330, 30), "Keyboard Focus", (255, 255, 255), bgcolor) + win.blit(img_on_off[pg.key.get_focused()], pos) + + pos = showtext(win, (10, 60), "Mouse Position(rel)", (255, 255, 255), bgcolor) + rel = pg.mouse.get_rel() + virtual_x += rel[0] + virtual_y += rel[1] + + mouse_data = tuple(list(pg.mouse.get_pos()) + list(rel)) + p = "%s, %s (%s, %s)" % mouse_data + showtext(win, pos, p, bgcolor, (255, 255, 55)) + + pos = showtext(win, (330, 60), "Last Keypress", (255, 255, 255), bgcolor) + if last_key: + p = "%d, %s" % (last_key, pg.key.name(last_key)) + else: + p = "None" + showtext(win, pos, p, bgcolor, (255, 255, 55)) + + pos = showtext(win, (10, 90), "Input Grabbed", (255, 255, 255), bgcolor) + win.blit(img_on_off[pg.event.get_grab()], pos) + + is_virtual_mouse = pg.event.get_grab() and not pg.mouse.get_visible() + pos = showtext(win, (330, 90), "Virtual Mouse", (255, 255, 255), bgcolor) + win.blit(img_on_off[is_virtual_mouse], pos) + if is_virtual_mouse: + p = f"{virtual_x}, {virtual_y}" + showtext(win, (pos[0] + 50, pos[1]), p, bgcolor, (255, 255, 55)) + + +def drawhistory(win, history): + img = font.render("Event History Area", 1, (155, 155, 155), (0, 0, 0)) + win.blit(img, (2, 132)) + ypos = 450 + h = list(history) + h.reverse() + for line in h: + r = win.blit(line, (10, ypos)) + win.fill(0, (r.right, r.top, 620, r.height)) + ypos -= font.get_height() + + +def draw_usage_in_history(history, text): + lines = text.split("\n") + for line in lines: + if line == "" or "===" in line: + continue + img = font.render(line, 1, (50, 200, 50), (0, 0, 0)) + history.append(img) + + +def main(): + pg.init() + pygame._sdl2.controller.init() + + print(usage) + + win = pg.display.set_mode((640, 480), pg.RESIZABLE) + pg.display.set_caption("Mouse Focus Workout. h key for help") + + global font + font = pg.font.Font(None, 26) + + global img_on_off + img_on_off.append(font.render("Off", 1, (0, 0, 0), (255, 50, 50))) + img_on_off.append(font.render("On", 1, (0, 0, 0), (50, 255, 50))) + + # stores surfaces of text representing what has gone through the event queue + history = [] + + # let's turn on the joysticks just so we can play with em + for x in range(pg.joystick.get_count()): + if pygame._sdl2.controller.is_controller(x): + c = pygame._sdl2.controller.Controller(x) + txt = "Enabled controller: " + c.name + else: + j = pg.joystick.Joystick(x) + txt = "Enabled joystick: " + j.get_name() + + img = font.render(txt, 1, (50, 200, 50), (0, 0, 0)) + history.append(img) + if not pg.joystick.get_count(): + img = font.render("No Joysticks to Initialize", 1, (50, 200, 50), (0, 0, 0)) + history.append(img) + + going = True + while going: + for e in pg.event.get(): + if e.type == pg.KEYDOWN: + if e.key == pg.K_ESCAPE: + going = False + else: + global last_key + last_key = e.key + if e.key == pg.K_h: + draw_usage_in_history(history, usage) + if e.key == pg.K_c: + current_state = pygame._sdl2.controller.get_eventstate() + pygame._sdl2.controller.set_eventstate(not current_state) + + if e.type == pg.MOUSEBUTTONDOWN and e.button == 1: + pg.event.set_grab(not pg.event.get_grab()) + + if e.type == pg.MOUSEBUTTONDOWN and e.button == 3: + pg.mouse.set_visible(not pg.mouse.get_visible()) + + if e.type != pg.MOUSEMOTION: + txt = f"{pg.event.event_name(e.type)}: {e.dict}" + img = font.render(txt, 1, (50, 200, 50), (0, 0, 0)) + history.append(img) + history = history[-13:] + + if e.type == pg.VIDEORESIZE: + win = pg.display.set_mode(e.size, pg.RESIZABLE) + + if e.type == pg.QUIT: + going = False + + drawstatus(win) + drawhistory(win, history) + + pg.display.flip() + pg.time.wait(10) + + pg.quit() + raise SystemExit + + +if __name__ == "__main__": + main() diff --git a/.venv/Lib/site-packages/pygame/examples/font_viewer.py b/.venv/Lib/site-packages/pygame/examples/font_viewer.py new file mode 100644 index 00000000..9a695ae4 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/font_viewer.py @@ -0,0 +1,281 @@ +#!/usr/bin/env python +""" pygame.examples.font_viewer +Scroll through your system fonts from a list of surfaces or one huge buffer. + +This example exhibits: +* iterate over available fonts using font.get_fonts and font.SysFont() +* click and drag using mouse input +* scrolling with the scroll wheel +* save a surface to disk +* work with a very large surface +* simple mouse and keyboard scroll speed acceleration + +By default this example uses the fonts returned by pygame.font.get_fonts() +and opens them using pygame.font.SysFont(). +Alternatively, you may pass a path to the command line. The TTF files found +in that directory will be used instead. + +Mouse Controls: +* Use the mouse wheel or click and drag to scroll + +Keyboard Controls: +* Press up or down to scroll +* Press escape to exit +""" +import sys +import os + +import pygame as pg + +use_big_surface = False # draw into large buffer and save png file + + +class FontViewer: + """ + This example is encapsulated by the fontviewer class + It initializes the pygame window, handles input, and draws itself + to the screen. + """ + + KEY_SCROLL_SPEED = 10 + MOUSE_SCROLL_SPEED = 50 + + def __init__(self): + pg.init() + + # create a window that uses 80 percent of the screen + info = pg.display.Info() + w = info.current_w + h = info.current_h + pg.display.set_mode((int(w * 0.8), int(h * 0.8))) + self.font_size = h // 20 + + self.clock = pg.time.Clock() + self.y_offset = 0 + self.grabbed = False + self.render_fonts("&N abcDEF789") + + if use_big_surface or "big" in sys.argv: + self.render_surface() + self.display_surface() + self.save_png() + else: + self.display_fonts() + + def get_font_list(self): + """ + Generate a font list using font.get_fonts() for system fonts or + from a path from the command line. + """ + path = "" + if len(sys.argv) > 1 and os.path.exists(sys.argv[1]): + path = os.path.join(sys.argv[1], "") + fonts = [] + if os.path.exists(path): + # this list comprehension could replace the following loop + # fonts = [f in os.listdir(path) if f.endswith('.ttf')] + for font in os.listdir(path): + if font.endswith(".ttf"): + fonts.append(font) + return fonts or pg.font.get_fonts(), path + + def render_fonts(self, text="A display of font &N"): + """ + Build a list that includes a surface and the running total of their + height for each font in the font list. Store the largest width and + other variables for later use. + """ + font_size = self.font_size + color = (255, 255, 255) + instruction_color = (255, 255, 0) + self.back_color = (0, 0, 0) + + fonts, path = self.get_font_list() + font_surfaces = [] + total_height = 0 + max_width = 0 + + load_font = pg.font.Font if path else pg.font.SysFont + + # display instructions at the top of the display + font = pg.font.SysFont(pg.font.get_default_font(), font_size) + lines = ( + "Use the scroll wheel or click and drag", + "to scroll up and down.", + "Fonts that don't use the Latin Alphabet", + "might render incorrectly.", + f"Here are your {len(fonts)} fonts", + "", + ) + for line in lines: + surf = font.render(line, 1, instruction_color, self.back_color) + font_surfaces.append((surf, total_height)) + total_height += surf.get_height() + max_width = max(max_width, surf.get_width()) + + # render all the fonts and store them with the total height + for name in sorted(fonts): + try: + font = load_font(path + name, font_size) + except OSError: + continue + line = text.replace("&N", name) + try: + surf = font.render(line, 1, color, self.back_color) + except pg.error as e: + print(e) + break + + max_width = max(max_width, surf.get_width()) + font_surfaces.append((surf, total_height)) + total_height += surf.get_height() + + # store variables for later usage + self.total_height = total_height + self.max_width = max_width + self.font_surfaces = font_surfaces + self.max_y = total_height - pg.display.get_surface().get_height() + + def display_fonts(self): + """ + Display the visible fonts based on the y_offset value(updated in + handle_events) and the height of the pygame window. + """ + pg.display.set_caption("Font Viewer") + display = pg.display.get_surface() + clock = pg.time.Clock() + center = display.get_width() // 2 + + while True: + # draw visible surfaces + display.fill(self.back_color) + for surface, top in self.font_surfaces: + bottom = top + surface.get_height() + if ( + bottom >= self.y_offset + and top <= self.y_offset + display.get_height() + ): + x = center - surface.get_width() // 2 + display.blit(surface, (x, top - self.y_offset)) + # get input and update the screen + if not self.handle_events(): + break + pg.display.flip() + clock.tick(30) + + def render_surface(self): + """ + Note: this method uses twice the memory and is only called if + big_surface is set to true or big is added to the command line. + + Optionally generates one large buffer to draw all the font surfaces + into. This is necessary to save the display to a png file and may + be useful for testing large surfaces. + """ + + large_surface = pg.surface.Surface( + (self.max_width, self.total_height) + ).convert() + large_surface.fill(self.back_color) + print("scrolling surface created") + + # display the surface size and memory usage + byte_size = large_surface.get_bytesize() + total_size = byte_size * (self.max_width * self.total_height) + print( + "Surface Size = {}x{} @ {}bpp: {:,.3f}mb".format( + self.max_width, self.total_height, byte_size, total_size / 1000000.0 + ) + ) + + y = 0 + center = int(self.max_width / 2) + for surface, top in self.font_surfaces: + w = surface.get_width() + x = center - int(w / 2) + large_surface.blit(surface, (x, y)) + y += surface.get_height() + self.max_y = large_surface.get_height() - pg.display.get_surface().get_height() + self.surface = large_surface + + def display_surface(self, time=10): + """ + Display the large surface created by the render_surface method. Scrolls + based on the y_offset value(set in handle_events) and the height of the + pygame window. + """ + screen = pg.display.get_surface() + + # Create a Rect equal to size of screen. Then we can just change its + # top attribute to draw the desired part of the rendered font surface + # to the display surface + rect = pg.rect.Rect( + 0, + 0, + self.surface.get_width(), + min(self.surface.get_height(), screen.get_height()), + ) + + x = int((screen.get_width() - self.surface.get_width()) / 2) + going = True + while going: + if not self.handle_events(): + going = False + screen.fill(self.back_color) + rect.top = self.y_offset + screen.blit(self.surface, (x, 0), rect) + pg.display.flip() + self.clock.tick(20) + + def save_png(self, name="font_viewer.png"): + pg.image.save(self.surface, name) + file_size = os.path.getsize(name) // 1024 + print(f"font surface saved to {name}\nsize: {file_size:,}Kb") + + def handle_events(self): + """ + This method handles user input. It returns False when it receives + a pygame.QUIT event or the user presses escape. The y_offset is + changed based on mouse and keyboard input. display_fonts() and + display_surface() use the y_offset to scroll display. + """ + events = pg.event.get() + for e in events: + if e.type == pg.QUIT: + return False + elif e.type == pg.KEYDOWN: + if e.key == pg.K_ESCAPE: + return False + elif e.type == pg.MOUSEWHEEL: + self.y_offset += e.y * self.MOUSE_SCROLL_SPEED * -1 + elif e.type == pg.MOUSEBUTTONDOWN: + # enter dragging mode on mouse down + self.grabbed = True + pg.event.set_grab(True) + elif e.type == pg.MOUSEBUTTONUP: + # exit drag mode on mouse up + self.grabbed = False + pg.event.set_grab(False) + + # allow simple accelerated scrolling with the keyboard + keys = pg.key.get_pressed() + if keys[pg.K_UP]: + self.key_held += 1 + self.y_offset -= int(self.KEY_SCROLL_SPEED * (self.key_held // 10)) + elif keys[pg.K_DOWN]: + self.key_held += 1 + self.y_offset += int(self.KEY_SCROLL_SPEED * (self.key_held // 10)) + else: + self.key_held = 20 + + # set the y_offset for scrolling and keep it between 0 and max_y + y = pg.mouse.get_rel()[1] + if y and self.grabbed: + self.y_offset -= y + + self.y_offset = min((max(self.y_offset, 0), self.max_y)) + return True + + +viewer = FontViewer() +pg.quit() diff --git a/.venv/Lib/site-packages/pygame/examples/fonty.py b/.venv/Lib/site-packages/pygame/examples/fonty.py new file mode 100644 index 00000000..786f7413 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/fonty.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +""" pygame.examples.fonty + +Here we load a .TTF True Type font file, and display it in +a basic pygame window. + +Demonstrating several Font object attributes. + +- basic window, event, and font management. +""" +import pygame as pg + + +def main(): + # initialize + pg.init() + resolution = 400, 200 + screen = pg.display.set_mode(resolution) + + ## pg.mouse.set_cursor(*pg.cursors.diamond) + + fg = 250, 240, 230 + bg = 5, 5, 5 + wincolor = 40, 40, 90 + + # fill background + screen.fill(wincolor) + + # load font, prepare values + font = pg.font.Font(None, 80) + text = "Fonty" + size = font.size(text) + + # no AA, no transparency, normal + ren = font.render(text, 0, fg, bg) + screen.blit(ren, (10, 10)) + + # no AA, transparency, underline + font.set_underline(1) + ren = font.render(text, 0, fg) + screen.blit(ren, (10, 40 + size[1])) + font.set_underline(0) + + a_sys_font = pg.font.SysFont("Arial", 60) + + # AA, no transparency, bold + a_sys_font.set_bold(1) + ren = a_sys_font.render(text, 1, fg, bg) + screen.blit(ren, (30 + size[0], 10)) + a_sys_font.set_bold(0) + + # AA, transparency, italic + a_sys_font.set_italic(1) + ren = a_sys_font.render(text, 1, fg) + screen.blit(ren, (30 + size[0], 40 + size[1])) + a_sys_font.set_italic(0) + + # Get some metrics. + print(f"Font metrics for 'Fonty': {a_sys_font.metrics(text)}") + ch = "\u3060" + msg = f"Font metrics for '{ch}': {a_sys_font.metrics(ch)}" + print(msg) + + ## #some_japanese_unicode = u"\u304b\u3070\u306b" + ##some_japanese_unicode = unicode_('%c%c%c') % (0x304b, 0x3070, 0x306b) + + # AA, transparency, italic + ##ren = a_sys_font.render(some_japanese_unicode, 1, fg) + ##screen.blit(ren, (30 + size[0], 40 + size[1])) + + # show the surface and await user quit + pg.display.flip() + while True: + # use event.wait to keep from polling 100% cpu + if pg.event.wait().type in (pg.QUIT, pg.KEYDOWN, pg.MOUSEBUTTONDOWN): + break + pg.quit() + + +if __name__ == "__main__": + main() diff --git a/.venv/Lib/site-packages/pygame/examples/freetype_misc.py b/.venv/Lib/site-packages/pygame/examples/freetype_misc.py new file mode 100644 index 00000000..70a6090e --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/freetype_misc.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python +""" pygame.examples.freetype_misc + + +Miscellaneous (or misc) means: + "consisting of a mixture of various things that are not + usually connected with each other" + Adjective + + +All those words you read on computers, magazines, books, and such over the years? +Probably a lot of them were constructed with... + +The FreeType Project: a free, high-quality and portable Font engine. +https://freetype.org + +Next time you're reading something. Think of them. + + +Herein lies a *BOLD* demo consisting of a mixture of various things. + + Not only is it a *BOLD* demo, it's an + italics demo, + a rotated demo, + it's a blend, + and is sized to go nicely with a cup of tea*. + + * also goes well with coffee. + +Enjoy! +""" +import os +import pygame as pg +import pygame.freetype as freetype + + +def run(): + pg.init() + + fontdir = os.path.dirname(os.path.abspath(__file__)) + font = freetype.Font(os.path.join(fontdir, "data", "sans.ttf")) + + screen = pg.display.set_mode((800, 600)) + screen.fill("gray") + + font.underline_adjustment = 0.5 + font.pad = True + font.render_to( + screen, + (32, 32), + "Hello World", + "red3", + "dimgray", + size=64, + style=freetype.STYLE_UNDERLINE | freetype.STYLE_OBLIQUE, + ) + font.pad = False + + font.render_to( + screen, + (32, 128), + "abcdefghijklm", + "dimgray", + "green3", + size=64, + ) + + font.vertical = True + font.render_to(screen, (32, 200), "Vertical?", "blue3", None, size=32) + font.vertical = False + + font.render_to(screen, (64, 190), "Let's spin!", "red3", None, size=48, rotation=55) + + font.render_to( + screen, (160, 290), "All around!", "green3", None, size=48, rotation=-55 + ) + + font.render_to(screen, (250, 220), "and BLEND", (255, 0, 0, 128), None, size=64) + + font.render_to(screen, (265, 237), "or BLAND!", (0, 0xCC, 28, 128), None, size=64) + + # Some pinwheels + font.origin = True + for angle in range(0, 360, 45): + font.render_to(screen, (150, 420), ")", "black", size=48, rotation=angle) + font.vertical = True + for angle in range(15, 375, 30): + font.render_to(screen, (600, 400), "|^*", "orange", size=48, rotation=angle) + font.vertical = False + font.origin = False + + utext = "I \u2665 Unicode" + font.render_to(screen, (298, 320), utext, (0, 0xCC, 0xDD), None, size=64) + + utext = "\u2665" + font.render_to(screen, (480, 32), utext, "gray", "red3", size=148) + + font.render_to( + screen, + (380, 380), + "...yes, this is an SDL surface", + "black", + None, + size=24, + style=freetype.STYLE_STRONG, + ) + + font.origin = True + r = font.render_to( + screen, + (100, 530), + "stretch", + "red3", + None, + size=(24, 24), + style=freetype.STYLE_NORMAL, + ) + font.render_to( + screen, + (100 + r.width, 530), + " VERTICAL", + "red3", + None, + size=(24, 48), + style=freetype.STYLE_NORMAL, + ) + + r = font.render_to( + screen, + (100, 580), + "stretch", + "blue3", + None, + size=(24, 24), + style=freetype.STYLE_NORMAL, + ) + font.render_to( + screen, + (100 + r.width, 580), + " HORIZONTAL", + "blue3", + None, + size=(48, 24), + style=freetype.STYLE_NORMAL, + ) + + pg.display.flip() + + while True: + if pg.event.wait().type in (pg.QUIT, pg.KEYDOWN, pg.MOUSEBUTTONDOWN): + break + + pg.quit() + + +if __name__ == "__main__": + run() diff --git a/.venv/Lib/site-packages/pygame/examples/glcube.py b/.venv/Lib/site-packages/pygame/examples/glcube.py new file mode 100644 index 00000000..9d85c242 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/glcube.py @@ -0,0 +1,591 @@ +#!/usr/bin/env python +""" pygame.examples.glcube + +Draw a cube on the screen. + + + +Amazing. + +Every frame we orbit the camera around a small amount +creating the illusion of a spinning object. + +First we setup some points of a multicolored cube. Then we then go through +a semi-unoptimized loop to draw the cube points onto the screen. + +OpenGL does all the hard work for us. :] + + +Keyboard Controls +----------------- + +* ESCAPE key to quit +* f key to toggle fullscreen. + +""" +import math +import ctypes + +import pygame as pg + +try: + import OpenGL.GL as GL + import OpenGL.GLU as GLU +except ImportError: + print("pyopengl missing. The GLCUBE example requires: pyopengl numpy") + raise SystemExit + +try: + from numpy import array, dot, eye, zeros, float32, uint32 +except ImportError: + print("numpy missing. The GLCUBE example requires: pyopengl numpy") + raise SystemExit + + +# do we want to use the 'modern' OpenGL API or the old one? +# This example shows you how to do both. +USE_MODERN_GL = True + +# Some simple data for a colored cube here we have the 3D point position +# and color for each corner. A list of indices describes each face, and a +# list of indices describes each edge. + +CUBE_POINTS = ( + (0.5, -0.5, -0.5), + (0.5, 0.5, -0.5), + (-0.5, 0.5, -0.5), + (-0.5, -0.5, -0.5), + (0.5, -0.5, 0.5), + (0.5, 0.5, 0.5), + (-0.5, -0.5, 0.5), + (-0.5, 0.5, 0.5), +) + +# colors are 0-1 floating values +CUBE_COLORS = ( + (1, 0, 0), + (1, 1, 0), + (0, 1, 0), + (0, 0, 0), + (1, 0, 1), + (1, 1, 1), + (0, 0, 1), + (0, 1, 1), +) + +CUBE_QUAD_VERTS = ( + (0, 1, 2, 3), + (3, 2, 7, 6), + (6, 7, 5, 4), + (4, 5, 1, 0), + (1, 5, 7, 2), + (4, 0, 3, 6), +) + +CUBE_EDGES = ( + (0, 1), + (0, 3), + (0, 4), + (2, 1), + (2, 3), + (2, 7), + (6, 3), + (6, 4), + (6, 7), + (5, 1), + (5, 4), + (5, 7), +) + + +def translate(matrix, x=0.0, y=0.0, z=0.0): + """ + Translate (move) a matrix in the x, y and z axes. + + :param matrix: Matrix to translate. + :param x: direction and magnitude to translate in x axis. Defaults to 0. + :param y: direction and magnitude to translate in y axis. Defaults to 0. + :param z: direction and magnitude to translate in z axis. Defaults to 0. + :return: The translated matrix. + """ + translation_matrix = array( + [ + [1.0, 0.0, 0.0, x], + [0.0, 1.0, 0.0, y], + [0.0, 0.0, 1.0, z], + [0.0, 0.0, 0.0, 1.0], + ], + dtype=matrix.dtype, + ).T + matrix[...] = dot(matrix, translation_matrix) + return matrix + + +def frustum(left, right, bottom, top, znear, zfar): + """ + Build a perspective matrix from the clipping planes, or camera 'frustrum' + volume. + + :param left: left position of the near clipping plane. + :param right: right position of the near clipping plane. + :param bottom: bottom position of the near clipping plane. + :param top: top position of the near clipping plane. + :param znear: z depth of the near clipping plane. + :param zfar: z depth of the far clipping plane. + + :return: A perspective matrix. + """ + perspective_matrix = zeros((4, 4), dtype=float32) + perspective_matrix[0, 0] = +2.0 * znear / (right - left) + perspective_matrix[2, 0] = (right + left) / (right - left) + perspective_matrix[1, 1] = +2.0 * znear / (top - bottom) + perspective_matrix[3, 1] = (top + bottom) / (top - bottom) + perspective_matrix[2, 2] = -(zfar + znear) / (zfar - znear) + perspective_matrix[3, 2] = -2.0 * znear * zfar / (zfar - znear) + perspective_matrix[2, 3] = -1.0 + return perspective_matrix + + +def perspective(fovy, aspect, znear, zfar): + """ + Build a perspective matrix from field of view, aspect ratio and depth + planes. + + :param fovy: the field of view angle in the y axis. + :param aspect: aspect ratio of our view port. + :param znear: z depth of the near clipping plane. + :param zfar: z depth of the far clipping plane. + + :return: A perspective matrix. + """ + h = math.tan(fovy / 360.0 * math.pi) * znear + w = h * aspect + return frustum(-w, w, -h, h, znear, zfar) + + +def rotate(matrix, angle, x, y, z): + """ + Rotate a matrix around an axis. + + :param matrix: The matrix to rotate. + :param angle: The angle to rotate by. + :param x: x of axis to rotate around. + :param y: y of axis to rotate around. + :param z: z of axis to rotate around. + + :return: The rotated matrix + """ + angle = math.pi * angle / 180 + c, s = math.cos(angle), math.sin(angle) + n = math.sqrt(x * x + y * y + z * z) + x, y, z = x / n, y / n, z / n + cx, cy, cz = (1 - c) * x, (1 - c) * y, (1 - c) * z + rotation_matrix = array( + [ + [cx * x + c, cy * x - z * s, cz * x + y * s, 0], + [cx * y + z * s, cy * y + c, cz * y - x * s, 0], + [cx * z - y * s, cy * z + x * s, cz * z + c, 0], + [0, 0, 0, 1], + ], + dtype=matrix.dtype, + ).T + matrix[...] = dot(matrix, rotation_matrix) + return matrix + + +class Rotation: + """ + Data class that stores rotation angles in three axes. + """ + + def __init__(self): + self.theta = 20 + self.phi = 40 + self.psi = 25 + + +def drawcube_old(): + """ + Draw the cube using the old open GL methods pre 3.2 core context. + """ + allpoints = list(zip(CUBE_POINTS, CUBE_COLORS)) + + GL.glBegin(GL.GL_QUADS) + for face in CUBE_QUAD_VERTS: + for vert in face: + pos, color = allpoints[vert] + GL.glColor3fv(color) + GL.glVertex3fv(pos) + GL.glEnd() + + GL.glColor3f(1.0, 1.0, 1.0) + GL.glBegin(GL.GL_LINES) + for line in CUBE_EDGES: + for vert in line: + pos, color = allpoints[vert] + GL.glVertex3fv(pos) + + GL.glEnd() + + +def init_gl_stuff_old(): + """ + Initialise open GL, prior to core context 3.2 + """ + GL.glEnable(GL.GL_DEPTH_TEST) # use our zbuffer + + # setup the camera + GL.glMatrixMode(GL.GL_PROJECTION) + GL.glLoadIdentity() + GLU.gluPerspective(45.0, 640 / 480.0, 0.1, 100.0) # setup lens + GL.glTranslatef(0.0, 0.0, -3.0) # move back + GL.glRotatef(25, 1, 0, 0) # orbit higher + + +def init_gl_modern(display_size): + """ + Initialise open GL in the 'modern' open GL style for open GL versions + greater than 3.1. + + :param display_size: Size of the window/viewport. + """ + + # Create shaders + # -------------------------------------- + vertex_code = """ + + #version 150 + uniform mat4 model; + uniform mat4 view; + uniform mat4 projection; + + uniform vec4 colour_mul; + uniform vec4 colour_add; + + in vec4 vertex_colour; // vertex colour in + in vec3 vertex_position; + + out vec4 vertex_color_out; // vertex colour out + void main() + { + vertex_color_out = (colour_mul * vertex_colour) + colour_add; + gl_Position = projection * view * model * vec4(vertex_position, 1.0); + } + + """ + + fragment_code = """ + #version 150 + in vec4 vertex_color_out; // vertex colour from vertex shader + out vec4 fragColor; + void main() + { + fragColor = vertex_color_out; + } + """ + + program = GL.glCreateProgram() + vertex = GL.glCreateShader(GL.GL_VERTEX_SHADER) + fragment = GL.glCreateShader(GL.GL_FRAGMENT_SHADER) + GL.glShaderSource(vertex, vertex_code) + GL.glCompileShader(vertex) + + # this logs issues the shader compiler finds. + log = GL.glGetShaderInfoLog(vertex) + if isinstance(log, bytes): + log = log.decode() + for line in log.split("\n"): + print(line) + + GL.glAttachShader(program, vertex) + GL.glShaderSource(fragment, fragment_code) + GL.glCompileShader(fragment) + + # this logs issues the shader compiler finds. + log = GL.glGetShaderInfoLog(fragment) + if isinstance(log, bytes): + log = log.decode() + for line in log.split("\n"): + print(line) + + GL.glAttachShader(program, fragment) + GL.glValidateProgram(program) + GL.glLinkProgram(program) + + GL.glDetachShader(program, vertex) + GL.glDetachShader(program, fragment) + GL.glUseProgram(program) + + # Create vertex buffers and shader constants + # ------------------------------------------ + + # Cube Data + vertices = zeros( + 8, [("vertex_position", float32, 3), ("vertex_colour", float32, 4)] + ) + + vertices["vertex_position"] = [ + [1, 1, 1], + [-1, 1, 1], + [-1, -1, 1], + [1, -1, 1], + [1, -1, -1], + [1, 1, -1], + [-1, 1, -1], + [-1, -1, -1], + ] + + vertices["vertex_colour"] = [ + [0, 1, 1, 1], + [0, 0, 1, 1], + [0, 0, 0, 1], + [0, 1, 0, 1], + [1, 1, 0, 1], + [1, 1, 1, 1], + [1, 0, 1, 1], + [1, 0, 0, 1], + ] + + filled_cube_indices = array( + [ + 0, + 1, + 2, + 0, + 2, + 3, + 0, + 3, + 4, + 0, + 4, + 5, + 0, + 5, + 6, + 0, + 6, + 1, + 1, + 6, + 7, + 1, + 7, + 2, + 7, + 4, + 3, + 7, + 3, + 2, + 4, + 7, + 6, + 4, + 6, + 5, + ], + dtype=uint32, + ) + + outline_cube_indices = array( + [0, 1, 1, 2, 2, 3, 3, 0, 4, 7, 7, 6, 6, 5, 5, 4, 0, 5, 1, 6, 2, 7, 3, 4], + dtype=uint32, + ) + + shader_data = {"buffer": {}, "constants": {}} + + GL.glBindVertexArray(GL.glGenVertexArrays(1)) # Have to do this first + + shader_data["buffer"]["vertices"] = GL.glGenBuffers(1) + GL.glBindBuffer(GL.GL_ARRAY_BUFFER, shader_data["buffer"]["vertices"]) + GL.glBufferData(GL.GL_ARRAY_BUFFER, vertices.nbytes, vertices, GL.GL_DYNAMIC_DRAW) + + stride = vertices.strides[0] + offset = ctypes.c_void_p(0) + + loc = GL.glGetAttribLocation(program, "vertex_position") + GL.glEnableVertexAttribArray(loc) + GL.glVertexAttribPointer(loc, 3, GL.GL_FLOAT, False, stride, offset) + + offset = ctypes.c_void_p(vertices.dtype["vertex_position"].itemsize) + + loc = GL.glGetAttribLocation(program, "vertex_colour") + GL.glEnableVertexAttribArray(loc) + GL.glVertexAttribPointer(loc, 4, GL.GL_FLOAT, False, stride, offset) + + shader_data["buffer"]["filled"] = GL.glGenBuffers(1) + GL.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, shader_data["buffer"]["filled"]) + GL.glBufferData( + GL.GL_ELEMENT_ARRAY_BUFFER, + filled_cube_indices.nbytes, + filled_cube_indices, + GL.GL_STATIC_DRAW, + ) + + shader_data["buffer"]["outline"] = GL.glGenBuffers(1) + GL.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, shader_data["buffer"]["outline"]) + GL.glBufferData( + GL.GL_ELEMENT_ARRAY_BUFFER, + outline_cube_indices.nbytes, + outline_cube_indices, + GL.GL_STATIC_DRAW, + ) + + shader_data["constants"]["model"] = GL.glGetUniformLocation(program, "model") + GL.glUniformMatrix4fv(shader_data["constants"]["model"], 1, False, eye(4)) + + shader_data["constants"]["view"] = GL.glGetUniformLocation(program, "view") + view = translate(eye(4), z=-6) + GL.glUniformMatrix4fv(shader_data["constants"]["view"], 1, False, view) + + shader_data["constants"]["projection"] = GL.glGetUniformLocation( + program, "projection" + ) + GL.glUniformMatrix4fv(shader_data["constants"]["projection"], 1, False, eye(4)) + + # This colour is multiplied with the base vertex colour in producing + # the final output + shader_data["constants"]["colour_mul"] = GL.glGetUniformLocation( + program, "colour_mul" + ) + GL.glUniform4f(shader_data["constants"]["colour_mul"], 1, 1, 1, 1) + + # This colour is added on to the base vertex colour in producing + # the final output + shader_data["constants"]["colour_add"] = GL.glGetUniformLocation( + program, "colour_add" + ) + GL.glUniform4f(shader_data["constants"]["colour_add"], 0, 0, 0, 0) + + # Set GL drawing data + # ------------------- + GL.glClearColor(0, 0, 0, 0) + GL.glPolygonOffset(1, 1) + GL.glEnable(GL.GL_LINE_SMOOTH) + GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA) + GL.glDepthFunc(GL.GL_LESS) + GL.glHint(GL.GL_LINE_SMOOTH_HINT, GL.GL_NICEST) + GL.glLineWidth(1.0) + + projection = perspective(45.0, display_size[0] / float(display_size[1]), 2.0, 100.0) + GL.glUniformMatrix4fv(shader_data["constants"]["projection"], 1, False, projection) + + return shader_data, filled_cube_indices, outline_cube_indices + + +def draw_cube_modern(shader_data, filled_cube_indices, outline_cube_indices, rotation): + """ + Draw a cube in the 'modern' Open GL style, for post 3.1 versions of + open GL. + + :param shader_data: compile vertex & pixel shader data for drawing a cube. + :param filled_cube_indices: the indices to draw the 'filled' cube. + :param outline_cube_indices: the indices to draw the 'outline' cube. + :param rotation: the current rotations to apply. + """ + + GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) + + # Filled cube + GL.glDisable(GL.GL_BLEND) + GL.glEnable(GL.GL_DEPTH_TEST) + GL.glEnable(GL.GL_POLYGON_OFFSET_FILL) + GL.glUniform4f(shader_data["constants"]["colour_mul"], 1, 1, 1, 1) + GL.glUniform4f(shader_data["constants"]["colour_add"], 0, 0, 0, 0.0) + GL.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, shader_data["buffer"]["filled"]) + GL.glDrawElements( + GL.GL_TRIANGLES, len(filled_cube_indices), GL.GL_UNSIGNED_INT, None + ) + + # Outlined cube + GL.glDisable(GL.GL_POLYGON_OFFSET_FILL) + GL.glEnable(GL.GL_BLEND) + GL.glUniform4f(shader_data["constants"]["colour_mul"], 0, 0, 0, 0.0) + GL.glUniform4f(shader_data["constants"]["colour_add"], 1, 1, 1, 1.0) + GL.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, shader_data["buffer"]["outline"]) + GL.glDrawElements(GL.GL_LINES, len(outline_cube_indices), GL.GL_UNSIGNED_INT, None) + + # Rotate cube + # rotation.theta += 1.0 # degrees + rotation.phi += 1.0 # degrees + # rotation.psi += 1.0 # degrees + model = eye(4, dtype=float32) + # rotate(model, rotation.theta, 0, 0, 1) + rotate(model, rotation.phi, 0, 1, 0) + rotate(model, rotation.psi, 1, 0, 0) + GL.glUniformMatrix4fv(shader_data["constants"]["model"], 1, False, model) + + +def main(): + """run the demo""" + + # initialize pygame and setup an opengl display + pg.init() + + gl_version = (3, 0) # GL Version number (Major, Minor) + if USE_MODERN_GL: + gl_version = (3, 2) # GL Version number (Major, Minor) + + # By setting these attributes we can choose which Open GL Profile + # to use, profiles greater than 3.2 use a different rendering path + pg.display.gl_set_attribute(pg.GL_CONTEXT_MAJOR_VERSION, gl_version[0]) + pg.display.gl_set_attribute(pg.GL_CONTEXT_MINOR_VERSION, gl_version[1]) + pg.display.gl_set_attribute( + pg.GL_CONTEXT_PROFILE_MASK, pg.GL_CONTEXT_PROFILE_CORE + ) + + fullscreen = False # start in windowed mode + + display_size = (640, 480) + pg.display.set_mode(display_size, pg.OPENGL | pg.DOUBLEBUF | pg.RESIZABLE) + + if USE_MODERN_GL: + gpu, f_indices, o_indices = init_gl_modern(display_size) + rotation = Rotation() + else: + init_gl_stuff_old() + + going = True + while going: + # check for quit'n events + events = pg.event.get() + for event in events: + if event.type == pg.QUIT or ( + event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE + ): + going = False + + elif event.type == pg.KEYDOWN and event.key == pg.K_f: + if not fullscreen: + print("Changing to FULLSCREEN") + pg.display.set_mode( + (640, 480), pg.OPENGL | pg.DOUBLEBUF | pg.FULLSCREEN + ) + else: + print("Changing to windowed mode") + pg.display.set_mode((640, 480), pg.OPENGL | pg.DOUBLEBUF) + fullscreen = not fullscreen + if gl_version[0] >= 4 or (gl_version[0] == 3 and gl_version[1] >= 2): + gpu, f_indices, o_indices = init_gl_modern(display_size) + rotation = Rotation() + else: + init_gl_stuff_old() + + if USE_MODERN_GL: + draw_cube_modern(gpu, f_indices, o_indices, rotation) + else: + # clear screen and move camera + GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) + # orbit camera around by 1 degree + GL.glRotatef(1, 0, 1, 0) + drawcube_old() + + pg.display.flip() + pg.time.wait(10) + + pg.quit() + + +if __name__ == "__main__": + main() diff --git a/.venv/Lib/site-packages/pygame/examples/go_over_there.py b/.venv/Lib/site-packages/pygame/examples/go_over_there.py new file mode 100644 index 00000000..b63cea98 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/go_over_there.py @@ -0,0 +1,88 @@ +""" pg.examples.go_over_there +This simple tech demo is showcasing the use of Vector2.move_towards() +using multiple circles to represent Vectors. Each circle will have a +random position and speed once the demo starts. + +Mouse Controls: +* Use the mouse to click on a new target position + +Keyboard Controls: +* Press R to restart the demo +""" +import pygame as pg +import random + +MIN_SPEED = 0.25 +MAX_SPEED = 5 +MAX_BALLS = 1600 +SCREEN_SIZE = pg.Vector2(1000, 600) +CIRCLE_RADIUS = 5 + +pg.init() +screen = pg.display.set_mode(SCREEN_SIZE) +clock = pg.time.Clock() + +target_position = None +balls = [] + + +class Ball: + def __init__(self, position, speed): + self.position = position + self.speed = speed + + +def reset(): + global balls + global target_position + + target_position = None + balls = [] + for x in range(MAX_BALLS): + pos = pg.Vector2( + random.randint(0, SCREEN_SIZE.x), random.randint(0, SCREEN_SIZE.y) + ) + speed = random.uniform(MIN_SPEED, MAX_SPEED) + + b = Ball(pos, speed) + balls.append(b) + + +reset() +delta_time = 0 +running = True +while running: + for event in pg.event.get(): + if event.type == pg.QUIT: + running = False + + if event.type == pg.MOUSEBUTTONUP: + target_position = pg.mouse.get_pos() + + if event.type == pg.KEYUP: + if event.key == pg.K_ESCAPE: + running = False + + if event.key == pg.K_r: + reset() + + screen.fill((31, 143, 65)) + + for o in balls: + if target_position is not None: + try: + o.position.move_towards_ip(target_position, o.speed * delta_time) + except AttributeError: + raise RuntimeError( + f"""Version {pg.__version__} doesn't have Vector.move_towards_ip function. + Please update to >=2.1.3""" + ) + pg.draw.circle(screen, (118, 207, 145), o.position, CIRCLE_RADIUS) + + pg.display.flip() + delta_time = clock.tick(60) + pg.display.set_caption( + f"fps: {round(clock.get_fps(), 2)}, ball count: {len(balls)}" + ) + +pg.quit() diff --git a/.venv/Lib/site-packages/pygame/examples/grid.py b/.venv/Lib/site-packages/pygame/examples/grid.py new file mode 100644 index 00000000..dd29ab15 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/grid.py @@ -0,0 +1,64 @@ +import pygame as pg + +TITLE = "Grid" +TILES_HORIZONTAL = 10 +TILES_VERTICAL = 10 +TILE_SIZE = 80 +WINDOW_WIDTH = 800 +WINDOW_HEIGHT = 800 + + +class Player: + def __init__(self, surface): + self.surface = surface + self.pos = (40, 40) + + def draw(self): + pg.draw.circle(self.surface, (255, 255, 255), self.pos, 40) + + def move(self, target): + x = (80 * (target[0] // 80)) + 40 + y = (80 * (target[1] // 80)) + 40 + + self.pos = (x, y) + + +class Game: + def __init__(self): + pg.init() + self.clock = pg.time.Clock() + pg.display.set_caption(TITLE) + self.surface = pg.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT)) + self.loop = True + self.player = Player(self.surface) + + def main(self): + while self.loop: + self.grid_loop() + pg.quit() + + def grid_loop(self): + self.surface.fill((0, 0, 0)) + for row in range(TILES_HORIZONTAL): + for col in range(row % 2, TILES_HORIZONTAL, 2): + pg.draw.rect( + self.surface, + (40, 40, 40), + (row * TILE_SIZE, col * TILE_SIZE, TILE_SIZE, TILE_SIZE), + ) + self.player.draw() + for event in pg.event.get(): + if event.type == pg.QUIT: + self.loop = False + elif event.type == pg.KEYDOWN: + if event.key == pg.K_ESCAPE: + self.loop = False + elif event.type == pg.MOUSEBUTTONUP: + pos = pg.mouse.get_pos() + self.player.move(pos) + pg.display.update() + + +if __name__ == "__main__": + mygame = Game() + mygame.main() diff --git a/.venv/Lib/site-packages/pygame/examples/headless_no_windows_needed.py b/.venv/Lib/site-packages/pygame/examples/headless_no_windows_needed.py new file mode 100644 index 00000000..1b5a9431 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/headless_no_windows_needed.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +""" pygame.examples.headless_no_windows_needed + +How to use pygame with no windowing system, like on headless servers. + +Thumbnail generation with scaling is an example of what you can do with pygame. +NOTE: the pygame scale function uses mmx/sse if available, and can be run + in multiple threads. +""" +usage = """-scale inputimage outputimage new_width new_height +eg. -scale in.png out.png 50 50 + +""" + +import os +import sys + +# set SDL to use the dummy NULL video driver, +# so it doesn't need a windowing system. +os.environ["SDL_VIDEODRIVER"] = "dummy" + +import pygame as pg + +# Some platforms need to init the display for some parts of pg. +pg.display.init() +screen = pg.display.set_mode((1, 1)) + + +def scaleit(fin, fout, w, h): + i = pg.image.load(fin) + + if hasattr(pg.transform, "smoothscale"): + scaled_image = pg.transform.smoothscale(i, (w, h)) + else: + scaled_image = pg.transform.scale(i, (w, h)) + pg.image.save(scaled_image, fout) + + +def main(fin, fout, w, h): + """smoothscale image file named fin as fout with new size (w,h)""" + scaleit(fin, fout, w, h) + + +if __name__ == "__main__": + if "-scale" in sys.argv: + fin, fout, w, h = sys.argv[2:] + w, h = map(int, [w, h]) + main(fin, fout, w, h) + else: + print(usage) diff --git a/.venv/Lib/site-packages/pygame/examples/joystick.py b/.venv/Lib/site-packages/pygame/examples/joystick.py new file mode 100644 index 00000000..36351951 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/joystick.py @@ -0,0 +1,151 @@ +import pygame + +pygame.init() + + +# This is a simple class that will help us print to the screen. +# It has nothing to do with the joysticks, just outputting the +# information. +class TextPrint: + def __init__(self): + self.reset() + self.font = pygame.font.Font(None, 25) + + def tprint(self, screen, text): + text_bitmap = self.font.render(text, True, (0, 0, 0)) + screen.blit(text_bitmap, (self.x, self.y)) + self.y += self.line_height + + def reset(self): + self.x = 10 + self.y = 10 + self.line_height = 15 + + def indent(self): + self.x += 10 + + def unindent(self): + self.x -= 10 + + +def main(): + # Set the width and height of the screen (width, height), and name the window. + screen = pygame.display.set_mode((500, 700)) + pygame.display.set_caption("Joystick example") + + # Used to manage how fast the screen updates. + clock = pygame.time.Clock() + + # Get ready to print. + text_print = TextPrint() + + # This dict can be left as-is, since pygame will generate a + # pygame.JOYDEVICEADDED event for every joystick connected + # at the start of the program. + joysticks = {} + + done = False + while not done: + # Event processing step. + # Possible joystick events: JOYAXISMOTION, JOYBALLMOTION, JOYBUTTONDOWN, + # JOYBUTTONUP, JOYHATMOTION, JOYDEVICEADDED, JOYDEVICEREMOVED + for event in pygame.event.get(): + if event.type == pygame.QUIT: + done = True # Flag that we are done so we exit this loop. + + if event.type == pygame.JOYBUTTONDOWN: + print("Joystick button pressed.") + if event.button == 0: + joystick = joysticks[event.instance_id] + if joystick.rumble(0, 0.7, 500): + print(f"Rumble effect played on joystick {event.instance_id}") + + if event.type == pygame.JOYBUTTONUP: + print("Joystick button released.") + + # Handle hotplugging + if event.type == pygame.JOYDEVICEADDED: + # This event will be generated when the program starts for every + # joystick, filling up the list without needing to create them manually. + joy = pygame.joystick.Joystick(event.device_index) + joysticks[joy.get_instance_id()] = joy + print(f"Joystick {joy.get_instance_id()} connencted") + + if event.type == pygame.JOYDEVICEREMOVED: + del joysticks[event.instance_id] + print(f"Joystick {event.instance_id} disconnected") + + # Drawing step + # First, clear the screen to white. Don't put other drawing commands + # above this, or they will be erased with this command. + screen.fill((255, 255, 255)) + text_print.reset() + + # Get count of joysticks. + joystick_count = pygame.joystick.get_count() + + text_print.tprint(screen, f"Number of joysticks: {joystick_count}") + text_print.indent() + + # For each joystick: + for joystick in joysticks.values(): + jid = joystick.get_instance_id() + + text_print.tprint(screen, f"Joystick {jid}") + text_print.indent() + + # Get the name from the OS for the controller/joystick. + name = joystick.get_name() + text_print.tprint(screen, f"Joystick name: {name}") + + guid = joystick.get_guid() + text_print.tprint(screen, f"GUID: {guid}") + + power_level = joystick.get_power_level() + text_print.tprint(screen, f"Joystick's power level: {power_level}") + + # Usually axis run in pairs, up/down for one, and left/right for + # the other. Triggers count as axes. + axes = joystick.get_numaxes() + text_print.tprint(screen, f"Number of axes: {axes}") + text_print.indent() + + for i in range(axes): + axis = joystick.get_axis(i) + text_print.tprint(screen, f"Axis {i} value: {axis:>6.3f}") + text_print.unindent() + + buttons = joystick.get_numbuttons() + text_print.tprint(screen, f"Number of buttons: {buttons}") + text_print.indent() + + for i in range(buttons): + button = joystick.get_button(i) + text_print.tprint(screen, f"Button {i:>2} value: {button}") + text_print.unindent() + + hats = joystick.get_numhats() + text_print.tprint(screen, f"Number of hats: {hats}") + text_print.indent() + + # Hat position. All or nothing for direction, not a float like + # get_axis(). Position is a tuple of int values (x, y). + for i in range(hats): + hat = joystick.get_hat(i) + text_print.tprint(screen, f"Hat {i} value: {str(hat)}") + text_print.unindent() + + text_print.unindent() + + # Go ahead and update the screen with what we've drawn. + pygame.display.flip() + + # Limit to 30 frames per second. + clock.tick(30) + + +if __name__ == "__main__": + main() + # If you forget this line, the program will 'hang' + # on exit if running from IDLE. + pygame.quit() diff --git a/.venv/Lib/site-packages/pygame/examples/liquid.py b/.venv/Lib/site-packages/pygame/examples/liquid.py new file mode 100644 index 00000000..22431ba3 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/liquid.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +""" pygame.examples.liquid + +This example demonstrates a simplish water effect of an +image. It attempts to create a hardware display surface that +can use pageflipping for faster updates. Note that the colormap +from the loaded GIF image is copied to the colormap for the +display surface. + +This is based on the demo named F2KWarp by Brad Graham of Freedom2000 +done in BlitzBasic. I was just translating the BlitzBasic code to +pygame to compare the results. I didn't bother porting the text and +sound stuff, that's an easy enough challenge for the reader :] +""" + +import pygame as pg +import os +from math import sin +import time + +main_dir = os.path.split(os.path.abspath(__file__))[0] + + +def main(): + # initialize and setup screen + pg.init() + screen = pg.display.set_mode((640, 480), pg.HWSURFACE | pg.DOUBLEBUF) + + # load image and quadruple + imagename = os.path.join(main_dir, "data", "liquid.bmp") + bitmap = pg.image.load(imagename) + bitmap = pg.transform.scale2x(bitmap) + bitmap = pg.transform.scale2x(bitmap) + + # get the image and screen in the same format + if screen.get_bitsize() == 8: + screen.set_palette(bitmap.get_palette()) + else: + bitmap = bitmap.convert() + + # prep some variables + anim = 0.0 + + # mainloop + xblocks = range(0, 640, 20) + yblocks = range(0, 480, 20) + stopevents = pg.QUIT, pg.KEYDOWN, pg.MOUSEBUTTONDOWN + while True: + for e in pg.event.get(): + if e.type in stopevents: + return + + anim = anim + 0.02 + for x in xblocks: + xpos = (x + (sin(anim + x * 0.01) * 15)) + 20 + for y in yblocks: + ypos = (y + (sin(anim + y * 0.01) * 15)) + 20 + screen.blit(bitmap, (x, y), (xpos, ypos, 20, 20)) + + pg.display.flip() + time.sleep(0.01) + + +if __name__ == "__main__": + main() + pg.quit() + + +"""BTW, here is the code from the BlitzBasic example this was derived +from. i've snipped the sound and text stuff out. +----------------------------------------------------------------- +; Brad@freedom2000.com + +; Load a bmp pic (800x600) and slice it into 1600 squares +Graphics 640,480 +SetBuffer BackBuffer() +bitmap$="f2kwarp.bmp" +pic=LoadAnimImage(bitmap$,20,15,0,1600) + +; use SIN to move all 1600 squares around to give liquid effect +Repeat +f=0:w=w+10:If w=360 Then w=0 +For y=0 To 599 Step 15 +For x = 0 To 799 Step 20 +f=f+1:If f=1600 Then f=0 +DrawBlock pic,(x+(Sin(w+x)*40))/1.7+80,(y+(Sin(w+y)*40))/1.7+60,f +Next:Next:Flip:Cls +Until KeyDown(1) +""" diff --git a/.venv/Lib/site-packages/pygame/examples/mask.py b/.venv/Lib/site-packages/pygame/examples/mask.py new file mode 100644 index 00000000..c0ec0973 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/mask.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python +""" +pygame.examples.mask + +A pygame.mask collision detection production. + + + + +Brought + + to + you + by + + the + +pixels + 0000000000000 + and + 111111 + + +This is 32 bits: + 11111111111111111111111111111111 + +There are 32 or 64 bits in a computer 'word'. +Rather than using one word for a pixel, +the mask module represents 32 or 64 pixels in one word. +As you can imagine, this makes things fast, and saves memory. + +Compute intensive things like collision detection, +and computer vision benefit greatly from this. + + +This module can also be run as a stand-alone program, excepting +one or more image file names as command line arguments. +""" + +import os +import random +import sys + +import pygame as pg + + +class Sprite: + """ + Moving Sprite demonstrating pixel-perfect collisions between pg.mask.Mask objects + """ + + def __init__(self, pos, vel, surface, mask=None): + """ + Positional arguments: + pos: Position of the sprite (sequence of 2 integers) + vel: Movement velocity of the sprite (sequence of 2 integers) + surface: Image (as a pg.Surface) of the sprite + mask: pg.mask.Mask object (optional) + """ + self.surface = surface + self.width, self.height = self.surface.get_size() + if mask is not None: + self.mask = mask + else: + self.mask = pg.mask.from_surface(self.surface) + + self.pos = pg.Vector2(pos) + self.vel = pg.Vector2(vel) + + def collide(self, sprite): + """ + Test if the sprites are colliding and + resolve the collision in this case. + + Positional arguments: + sprite: other sprite to test for collisions + """ + offset = [int(x) for x in sprite.pos - self.pos] + overlap = self.mask.overlap_area(sprite.mask, offset) + if overlap == 0: + return + # Calculate collision normal + + # Number of collisions + n_collisions = pg.Vector2( + # x axis + self.mask.overlap_area(sprite.mask, (offset[0] + 1, offset[1])) + - self.mask.overlap_area(sprite.mask, (offset[0] - 1, offset[1])), + # y axis + self.mask.overlap_area(sprite.mask, (offset[0], offset[1] + 1)) + - self.mask.overlap_area(sprite.mask, (offset[0], offset[1] - 1)), + ) + if n_collisions.x == 0 and n_collisions.y == 0: + # One sprite is inside another + return + + delta_vel = sprite.vel - self.vel + j = delta_vel * n_collisions / (2 * n_collisions * n_collisions) + if j > 0: + # Can scale up to 2*j here to get bouncy collisions + j *= 1.9 + self.vel += [n_collisions.x * j, n_collisions.y * j] + sprite.vel += [-j * n_collisions.x, -j * n_collisions.y] + + # # Separate the sprites + # c1 = -overlap / (n_collisions * n_collisions) + # c2 = -c1 / 2 + # self.pos += [c2 * n_collisions.x, c2 * n_collisions.y] + # sprite.pos += [(c1 + c2) * n_collisions.x, (c1 + c2) * n_collisions.y] + + def update(self): + """ + Move the sprite + """ + self.pos += self.vel + + +def main(*args): + """ + Display multiple images bounce off each other using collision detection + + Positional arguments: + one or more image file names. + + This pg.masks demo will display multiple moving sprites bouncing + off each other. More than one sprite image can be provided. + """ + + if len(args) == 0: + raise ValueError("Require at least one image file name: non given") + pg.init() + + screen_size = (640, 480) + screen = pg.display.set_mode(screen_size) + clock = pg.time.Clock() + + images = [] + masks = [] + for image_path in args: + images.append(pg.image.load(image_path).convert_alpha()) + masks.append(pg.mask.from_surface(images[-1])) + + sprites = [] + for i in range(20): + j = i % len(images) + sprite = Sprite( + pos=( + random.uniform(0, screen_size[0]), + random.uniform(0, screen_size[1]), + ), + vel=( + random.uniform(-5, 5), + random.uniform(-5, 5), + ), + surface=images[j], + mask=masks[j], + ) + sprites.append(sprite) + + while True: + for event in pg.event.get(): + if event.type in (pg.QUIT, pg.KEYDOWN): + return + + screen.fill((240, 220, 100)) + + for sprite_index, sprite in enumerate(sprites): + for other_sprite in sprites[sprite_index + 1 :]: + sprite.collide(other_sprite) + + sprite.update() + + # If the sprite is outside of the screen on the left + if sprite.pos.x < -sprite.width: + sprite.pos.x = screen_size[0] + # right + elif sprite.pos.x > screen_size[0]: + sprite.pos.x = -sprite.width + # top + if sprite.pos.y < -sprite.height: + sprite.pos.y = screen_size[1] + # down + elif sprite.pos.y > screen_size[1]: + sprite.pos.y = -sprite.height + + screen.blit(sprite.surface, sprite.pos) + + clock.tick(30) + pg.display.flip() + + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: mask.py [ ...]") + print("Let many copies of IMAGE(s) bounce against each other") + print("Press any key to quit") + main_dir = os.path.split(os.path.abspath(__file__))[0] + main(os.path.join(main_dir, "data", "alien1.png")) + + else: + main(*sys.argv[1:]) + pg.quit() diff --git a/.venv/Lib/site-packages/pygame/examples/midi.py b/.venv/Lib/site-packages/pygame/examples/midi.py new file mode 100644 index 00000000..f087ae6f --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/midi.py @@ -0,0 +1,932 @@ +#!/usr/bin/env python +""" pygame.examples.midi + +midi input, and a separate example of midi output. + +By default it runs the output example. + +python -m pygame.examples.midi --output +python -m pygame.examples.midi --input +python -m pygame.examples.midi --input +""" + +from dataclasses import dataclass +import sys +import os +from typing import Any, Dict, List, Literal, Optional, Set, Tuple, Union + +import pygame as pg +import pygame.midi + +# black and white piano keys use b/w color values directly +BACKGROUNDCOLOR = "slategray" + + +def print_device_info(): + pygame.midi.init() + _print_device_info() + pygame.midi.quit() + + +def _print_device_info(): + for i in range(pygame.midi.get_count()): + r = pygame.midi.get_device_info(i) + (interf, name, input, output, opened) = r + + in_out = "" + if input: + in_out = "(input)" + if output: + in_out = "(output)" + + print( + "%2i: interface :%s:, name :%s:, opened :%s: %s" + % (i, interf, name, opened, in_out) + ) + + +def input_main(device_id=None): + pg.init() + + pygame.midi.init() + + _print_device_info() + + if device_id is None: + input_id = pygame.midi.get_default_input_id() + else: + input_id = device_id + + print(f"using input_id :{input_id}:") + i = pygame.midi.Input(input_id) + + pg.display.set_mode((1, 1)) + + going = True + while going: + events = pygame.event.get() + for e in events: + if e.type in [pg.QUIT]: + going = False + if e.type in [pg.KEYDOWN]: + going = False + if e.type in [pygame.midi.MIDIIN]: + print(e) + + if i.poll(): + midi_events = i.read(10) + # convert them into pygame events. + midi_evs = pygame.midi.midis2events(midi_events, i.device_id) + + for m_e in midi_evs: + pygame.event.post(m_e) + + del i + pygame.midi.quit() + + +def output_main(device_id=None): + """Execute a musical keyboard example for the Church Organ instrument + + This is a piano keyboard example, with a two octave keyboard, starting at + note F3. Left mouse down over a key starts a note, left up stops it. The + notes are also mapped to the computer keyboard keys, assuming an American + English PC keyboard (sorry everyone else, but I don't know if I can map to + absolute key position instead of value.) The white keys are on the second + row, TAB to BACKSLASH, starting with note F3. The black keys map to the top + row, '1' to BACKSPACE, starting with F#3. 'r' is middle C. Close the + window or press ESCAPE to quit the program. Key velocity (note + amplitude) varies vertically on the keyboard image, with minimum velocity + at the top of a key and maximum velocity at bottom. + + Default Midi output, no device_id given, is to the default output device + for the computer. + + """ + + # A note to new pygamers: + # + # All the midi module stuff is in this function. It is unnecessary to + # understand how the keyboard display works to appreciate how midi + # messages are sent. + + # The keyboard is drawn by a Keyboard instance. This instance maps Midi + # notes to musical keyboard keys. A regions surface maps window position + # to (Midi note, velocity) pairs. A key_mapping dictionary does the same + # for computer keyboard keys. Midi sound is controlled with direct method + # calls to a pygame.midi.Output instance. + # + # Things to consider when using pygame.midi: + # + # 1) Initialize the midi module with a to pygame.midi.init(). + # 2) Create a midi.Output instance for the desired output device port. + # 3) Select instruments with set_instrument() method calls. + # 4) Play notes with note_on() and note_off() method calls. + # 5) Call pygame.midi.Quit() when finished. Though the midi module tries + # to ensure that midi is properly shut down, it is best to do it + # explicitly. A try/finally statement is the safest way to do this. + # + + # GRAND_PIANO = 0 + CHURCH_ORGAN = 19 + + instrument = CHURCH_ORGAN + # instrument = GRAND_PIANO + start_note = 53 # F3 (white key note), start_note != 0 + n_notes = 24 # Two octaves (14 white keys) + + key_mapping = make_key_mapping( + [ + pg.K_TAB, + pg.K_1, + pg.K_q, + pg.K_2, + pg.K_w, + pg.K_3, + pg.K_e, + pg.K_r, + pg.K_5, + pg.K_t, + pg.K_6, + pg.K_y, + pg.K_u, + pg.K_8, + pg.K_i, + pg.K_9, + pg.K_o, + pg.K_0, + pg.K_p, + pg.K_LEFTBRACKET, + pg.K_EQUALS, + pg.K_RIGHTBRACKET, + pg.K_BACKSPACE, + pg.K_BACKSLASH, + ], + start_note, + ) + + pg.init() + pygame.midi.init() + + _print_device_info() + + if device_id is None: + port = pygame.midi.get_default_output_id() + else: + port = device_id + + print(f"using output_id :{port}:") + + midi_out = pygame.midi.Output(port, 0) + try: + midi_out.set_instrument(instrument) + keyboard = Keyboard(start_note, n_notes) + + screen = pg.display.set_mode(keyboard.rect.size) + screen.fill(BACKGROUNDCOLOR) + pg.display.flip() + + background = pg.Surface(screen.get_size()) + background.fill(BACKGROUNDCOLOR) + dirty_rects = [] + keyboard.draw(screen, background, dirty_rects) + pg.display.update(dirty_rects) + + regions = pg.Surface(screen.get_size()) # initial color (0,0,0) + keyboard.map_regions(regions) + + pg.event.set_blocked(pg.MOUSEMOTION) + mouse_note = 0 + on_notes = set() + while True: + e = pg.event.wait() + if e.type == pg.MOUSEBUTTONDOWN: + mouse_note, velocity, __, __ = regions.get_at(e.pos) + if mouse_note and mouse_note not in on_notes: + keyboard.key_down(mouse_note) + midi_out.note_on(mouse_note, velocity) + on_notes.add(mouse_note) + else: + mouse_note = 0 + elif e.type == pg.MOUSEBUTTONUP: + if mouse_note: + midi_out.note_off(mouse_note) + keyboard.key_up(mouse_note) + on_notes.remove(mouse_note) + mouse_note = 0 + elif e.type == pg.QUIT: + break + elif e.type == pg.KEYDOWN: + if e.key == pg.K_ESCAPE: + break + try: + note, velocity = key_mapping[e.key] + except KeyError: + pass + else: + if note not in on_notes: + keyboard.key_down(note) + midi_out.note_on(note, velocity) + on_notes.add(note) + elif e.type == pg.KEYUP: + try: + note, __ = key_mapping[e.key] + except KeyError: + pass + else: + if note in on_notes and note != mouse_note: + keyboard.key_up(note) + midi_out.note_off(note, 0) + on_notes.remove(note) + + dirty_rects = [] + keyboard.draw(screen, background, dirty_rects) + pg.display.update(dirty_rects) + finally: + del midi_out + pygame.midi.quit() + + +def make_key_mapping(keys, start_note): + """Return a dictionary of (note, velocity) by computer keyboard key code""" + mapping = {} + for i, key in enumerate(keys): + mapping[key] = (start_note + i, 127) + return mapping + + +class NullKey: + """A dummy key that ignores events passed to it by other keys + + A NullKey instance is the left key instance used by default + for the left most keyboard key. + + """ + + def _right_white_down(self): + pass + + def _right_white_up(self): + pass + + def _right_black_down(self): + pass + + def _right_black_up(self): + pass + + +null_key = NullKey() + + +@dataclass +class KeyData: + """Used for passing in data to subclasses of the Key class.""" + + is_white_key: bool + c_width: int + c_height: int + c_down_state_initial: int + c_down_state_rect_initial: pg.Rect + c_notify_down_method: str + c_notify_up_method: str + c_updates: Set[Any] + c_event_down: Dict[int, Tuple[int, pg.Rect]] + c_event_up: Dict[int, Tuple[int, pg.Rect]] + c_image_strip: pg.Surface + c_event_right_white_down: Dict[int, Tuple[int, Union[pg.Rect, None]]] + c_event_right_white_up: Dict[int, Tuple[int, Union[pg.Rect, None]]] + c_event_right_black_down: Dict[int, Tuple[int, Union[pg.Rect, None]]] + c_event_right_black_up: Dict[int, Tuple[int, Union[pg.Rect, None]]] + + +class Key: + """A key widget, maintains key state and draws the key's image + + Constructor arguments: + ident - A unique key identifier. Any immutable type suitable as a key. + posn - The location of the key on the display surface. + key_left - Optional, the adjacent white key to the left. Changes in + up and down state are propagated to that key. + + A key has an associated position and state. Related to state is the + image drawn. State changes are managed with method calls, one method + per event type. The up and down event methods are public. Other + internal methods are for passing on state changes to the key_left + key instance. + + """ + + key_data: KeyData + + def __init__(self, ident, posn, key_left=None): + """Return a new Key instance + + The initial state is up, with all adjacent keys to the right also + up. + + """ + if key_left is None: + key_left = null_key + rect = pg.Rect(posn[0], posn[1], self.key_data.c_width, self.key_data.c_height) + self.rect = rect + self._state = self.key_data.c_down_state_initial + self._source_rect = self.key_data.c_down_state_rect_initial + self._ident = ident + self._hash = hash(ident) + self._notify_down = getattr(key_left, self.key_data.c_notify_down_method) + self._notify_up = getattr(key_left, self.key_data.c_notify_up_method) + self._key_left = key_left + self._background_rect = pg.Rect( + rect.left, rect.bottom - 10, self.key_data.c_width, 10 + ) + self.key_data.c_updates.add(self) + self.is_white = self.key_data.is_white_key + + def down(self): + """Signal that this key has been depressed (is down)""" + + self._state, source_rect = self.key_data.c_event_down[self._state] + if source_rect is not None: + self._source_rect = source_rect + self.key_data.c_updates.add(self) + self._notify_down() + + def up(self): + """Signal that this key has been released (is up)""" + + self._state, source_rect = self.key_data.c_event_up[self._state] + if source_rect is not None: + self._source_rect = source_rect + self.key_data.c_updates.add(self) + self._notify_up() + + def _right_white_down(self): + """Signal that the adjacent white key has been depressed + + This method is for internal propagation of events between + key instances. + + """ + + self._state, source_rect = self.key_data.c_event_right_white_down[self._state] + if source_rect is not None: + self._source_rect = source_rect + self.key_data.c_updates.add(self) + + def _right_white_up(self): + """Signal that the adjacent white key has been released + + This method is for internal propagation of events between + key instances. + + """ + + self._state, source_rect = self.key_data.c_event_right_white_up[self._state] + if source_rect is not None: + self._source_rect = source_rect + self.key_data.c_updates.add(self) + + def _right_black_down(self): + """Signal that the adjacent black key has been depressed + + This method is for internal propagation of events between + key instances. + + """ + + self._state, source_rect = self.key_data.c_event_right_black_down[self._state] + if source_rect is not None: + self._source_rect = source_rect + self.key_data.c_updates.add(self) + + def _right_black_up(self): + """Signal that the adjacent black key has been released + + This method is for internal propagation of events between + key instances. + + """ + + self._state, source_rect = self.key_data.c_event_right_black_up[self._state] + if source_rect is not None: + self._source_rect = source_rect + self.key_data.c_updates.add(self) + + def __eq__(self, other): + """True if same identifiers""" + + return self._ident == other._ident + + def __hash__(self): + """Return the immutable hash value""" + + return self._hash + + def __str__(self): + """Return the key's identifier and position as a string""" + + return "" % (self._ident, self.rect.top, self.rect.left) + + def draw(self, surf, background, dirty_rects): + """Redraw the key on the surface surf + + The background is redrawn. The altered region is added to the + dirty_rects list. + + """ + + surf.blit(background, self._background_rect, self._background_rect) + surf.blit(self.key_data.c_image_strip, self.rect, self._source_rect) + dirty_rects.append(self.rect) + + +def key_class(updates, image_strip, image_rects: List[pg.Rect], is_white_key=True): + """Return a keyboard key widget class + + Arguments: + updates - a set into which a key instance adds itself if it needs + redrawing. + image_strip - The surface containing the images of all key states. + image_rects - A list of Rects giving the regions within image_strip that + are relevant to this key class. + is_white_key (default True) - Set false if this is a black key. + + This function automates the creation of a key widget class for the + three basic key types. A key has two basic states, up or down ( + depressed). Corresponding up and down images are drawn for each + of these two states. But to give the illusion of depth, a key + may have shadows cast upon it by the adjacent keys to its right. + These shadows change depending on the up/down state of the key and + its neighbors. So a key may support multiple images and states + depending on the shadows. A key type is determined by the length + of image_rects and the value of is_white. + + """ + + # Naming convention: Variables used by the Key class as part of a + # closure start with 'c_'. + + # State logic and shadows: + # + # A key may cast a shadow upon the key to its left. A black key casts a + # shadow on an adjacent white key. The shadow changes depending of whether + # the black or white key is depressed. A white key casts a shadow on the + # white key to its left if it is up and the left key is down. Therefore + # a keys state, and image it will draw, is determined entirely by its + # itself and the key immediately adjacent to it on the right. A white key + # is always assumed to have an adjacent white key. + # + # There can be up to eight key states, representing all permutations + # of the three fundamental states of self up/down, adjacent white + # right up/down, adjacent black up/down. + # + down_state_none = 0 + down_state_self = 1 + down_state_white = down_state_self << 1 + down_state_self_white = down_state_self | down_state_white + down_state_black = down_state_white << 1 + down_state_self_black = down_state_self | down_state_black + down_state_white_black = down_state_white | down_state_black + down_state_all = down_state_self | down_state_white_black + + # Some values used in the class. + # + c_down_state_initial = down_state_none + c_down_state_rect_initial = image_rects[0] + c_updates = updates + c_image_strip = image_strip + c_width, c_height = image_rects[0].size + + # A key propagates its up/down state change to the adjacent white key on + # the left by calling the adjacent key's _right_black_down or + # _right_white_down method. + # + if is_white_key: + key_color = "white" + else: + key_color = "black" + c_notify_down_method = f"_right_{key_color}_down" + c_notify_up_method = f"_right_{key_color}_up" + + # Images: + # + # A black key only needs two images, for the up and down states. Its + # appearance is unaffected by the adjacent keys to its right, which cast no + # shadows upon it. + # + # A white key with a no adjacent black to its right only needs three + # images, for self up, self down, and both self and adjacent white down. + # + # A white key with both a black and white key to its right needs six + # images: self up, self up and adjacent black down, self down, self and + # adjacent white down, self and adjacent black down, and all three down. + # + # Each 'c_event' dictionary maps the current key state to a new key state, + # along with corresponding image, for the related event. If no redrawing + # is required for the state change then the image rect is simply None. + # + c_event_down: Dict[int, Tuple[int, pygame.Rect]] = { + down_state_none: (down_state_self, image_rects[1]) + } + c_event_up: Dict[int, Tuple[int, pygame.Rect]] = { + down_state_self: (down_state_none, image_rects[0]) + } + c_event_right_white_down: Dict[int, Tuple[int, Union[pygame.Rect, None]]] = { + down_state_none: (down_state_none, None), + down_state_self: (down_state_self, None), + } + c_event_right_white_up = c_event_right_white_down.copy() + c_event_right_black_down = c_event_right_white_down.copy() + c_event_right_black_up = c_event_right_white_down.copy() + if len(image_rects) > 2: + c_event_down[down_state_white] = (down_state_self_white, image_rects[2]) + c_event_up[down_state_self_white] = (down_state_white, image_rects[0]) + c_event_right_white_down[down_state_none] = (down_state_white, None) + c_event_right_white_down[down_state_self] = ( + down_state_self_white, + image_rects[2], + ) + c_event_right_white_up[down_state_white] = (down_state_none, None) + c_event_right_white_up[down_state_self_white] = ( + down_state_self, + image_rects[1], + ) + c_event_right_black_down[down_state_white] = (down_state_white, None) + c_event_right_black_down[down_state_self_white] = (down_state_self_white, None) + c_event_right_black_up[down_state_white] = (down_state_white, None) + c_event_right_black_up[down_state_self_white] = (down_state_self_white, None) + if len(image_rects) > 3: + c_event_down[down_state_black] = (down_state_self_black, image_rects[4]) + c_event_down[down_state_white_black] = (down_state_all, image_rects[5]) + c_event_up[down_state_self_black] = (down_state_black, image_rects[3]) + c_event_up[down_state_all] = (down_state_white_black, image_rects[3]) + c_event_right_white_down[down_state_black] = (down_state_white_black, None) + c_event_right_white_down[down_state_self_black] = ( + down_state_all, + image_rects[5], + ) + c_event_right_white_up[down_state_white_black] = (down_state_black, None) + c_event_right_white_up[down_state_all] = (down_state_self_black, image_rects[4]) + c_event_right_black_down[down_state_none] = (down_state_black, image_rects[3]) + c_event_right_black_down[down_state_self] = ( + down_state_self_black, + image_rects[4], + ) + c_event_right_black_down[down_state_white] = ( + down_state_white_black, + image_rects[3], + ) + c_event_right_black_down[down_state_self_white] = ( + down_state_all, + image_rects[5], + ) + c_event_right_black_up[down_state_black] = (down_state_none, image_rects[0]) + c_event_right_black_up[down_state_self_black] = ( + down_state_self, + image_rects[1], + ) + c_event_right_black_up[down_state_white_black] = ( + down_state_white, + image_rects[0], + ) + c_event_right_black_up[down_state_all] = (down_state_self_white, image_rects[2]) + + class OurKey(Key): + key_data = KeyData( + is_white_key, + c_width, + c_height, + c_down_state_initial, + c_down_state_rect_initial, + c_notify_down_method, + c_notify_up_method, + c_updates, + c_event_down, + c_event_up, + c_image_strip, + c_event_right_white_down, + c_event_right_white_up, + c_event_right_black_down, + c_event_right_black_up, + ) + + return OurKey + + +def key_images() -> Tuple[pg.Surface, Dict[str, pg.Rect]]: + """Return a keyboard keys image strip and a mapping of image locations + + The return tuple is a pygame.Surface and a dictionary keyed by key name and valued by a pygame.Rect. + + This function encapsulates the constants relevant to the keyboard image + file. There are five key types. One is the black key. The other four + white keys are determined by the proximity of the black keys. The plain + white key has no black key adjacent to it. A white-left and white-right + key has a black key to the left or right of it respectively. A white-center + key has a black key on both sides. A key may have up to six related + images depending on the state of adjacent keys to its right. + + """ + + my_dir = os.path.split(os.path.abspath(__file__))[0] + strip_file = os.path.join(my_dir, "data", "midikeys.png") + white_key_width = 42 + white_key_height = 160 + black_key_width = 22 + black_key_height = 94 + strip = pg.image.load(strip_file) + names = [ + "black none", + "black self", + "white none", + "white self", + "white self-white", + "white-left none", + "white-left self", + "white-left black", + "white-left self-black", + "white-left self-white", + "white-left all", + "white-center none", + "white-center self", + "white-center black", + "white-center self-black", + "white-center self-white", + "white-center all", + "white-right none", + "white-right self", + "white-right self-white", + ] + rects = {} + for i in range(2): + rects[names[i]] = pg.Rect( + i * white_key_width, 0, black_key_width, black_key_height + ) + for i in range(2, len(names)): + rects[names[i]] = pg.Rect( + i * white_key_width, 0, white_key_width, white_key_height + ) + return strip, rects + + +class Keyboard: + """Musical keyboard widget + + Constructor arguments: + start_note: midi note value of the starting note on the keyboard. + n_notes: number of notes (keys) on the keyboard. + + A Keyboard instance draws the musical keyboard and maintains the state of + all the keyboard keys. Individual keys can be in a down (depressed) or + up (released) state. + + """ + + _image_strip, _rects = key_images() + + white_key_width, white_key_height = _rects["white none"].size + black_key_width, black_key_height = _rects["black none"].size + + _updates: Set[Any] = set() + + # There are five key classes, representing key shape: + # black key (BlackKey), plain white key (WhiteKey), white key to the left + # of a black key (WhiteKeyLeft), white key between two black keys + # (WhiteKeyCenter), and white key to the right of a black key + # (WhiteKeyRight). + BlackKey = key_class( + _updates, _image_strip, [_rects["black none"], _rects["black self"]], False + ) + WhiteKey = key_class( + _updates, + _image_strip, + [_rects["white none"], _rects["white self"], _rects["white self-white"]], + ) + WhiteKeyLeft = key_class( + _updates, + _image_strip, + [ + _rects["white-left none"], + _rects["white-left self"], + _rects["white-left self-white"], + _rects["white-left black"], + _rects["white-left self-black"], + _rects["white-left all"], + ], + ) + WhiteKeyCenter = key_class( + _updates, + _image_strip, + [ + _rects["white-center none"], + _rects["white-center self"], + _rects["white-center self-white"], + _rects["white-center black"], + _rects["white-center self-black"], + _rects["white-center all"], + ], + ) + WhiteKeyRight = key_class( + _updates, + _image_strip, + [ + _rects["white-right none"], + _rects["white-right self"], + _rects["white-right self-white"], + ], + ) + + def __init__(self, start_note: int, n_notes: int): + """Return a new Keyboard instance with n_note keys""" + + self._start_note = start_note + self._end_note = start_note + n_notes - 1 + self._add_keys() + + def _add_keys(self) -> None: + """Populate the keyboard with key instances + + Set the _keys and rect attributes. + + """ + + # Keys are entered in a list, where index is Midi note. Since there are + # only 128 possible Midi notes the list length is manageable. Unassigned + # note positions should never be accessed, so are set None to ensure + # the bug is quickly detected. + # + key_map: list[Key | Literal[None]] = [None] * 128 + + start_note = self._start_note + end_note = self._end_note + black_offset = self.black_key_width // 2 + prev_white_key = None + x = y = 0 + if is_white_key(start_note): + is_prev_white = True + else: + x += black_offset + is_prev_white = False + for note in range(start_note, end_note + 1): + ident = note # For now notes uniquely identify keyboard keys. + if is_white_key(note): + if is_prev_white: + if note == end_note or is_white_key(note + 1): + key = self.WhiteKey(ident, (x, y), prev_white_key) + else: + key = self.WhiteKeyLeft(ident, (x, y), prev_white_key) + else: + if note == end_note or is_white_key(note + 1): + key = self.WhiteKeyRight(ident, (x, y), prev_white_key) + else: + key = self.WhiteKeyCenter(ident, (x, y), prev_white_key) + is_prev_white = True + x += self.white_key_width + prev_white_key = key + else: + key = self.BlackKey(ident, (x - black_offset, y), prev_white_key) + is_prev_white = False + key_map[note] = key + self._keys = key_map + + the_key = key_map[self._end_note] + if the_key is None: + kb_width = 0 + else: + kb_width = the_key.rect.right + kb_height = self.white_key_height + self.rect = pg.Rect(0, 0, kb_width, kb_height) + + def map_regions(self, regions): + """Draw the key regions onto surface regions. + + Regions must have at least 3 byte pixels. Each pixel of the keyboard + rectangle is set to the color (note, velocity, 0). The regions surface + must be at least as large as (0, 0, self.rect.left, self.rect.bottom) + + """ + + # First draw the white key regions. Then add the overlapping + # black key regions. + # + cutoff = self.black_key_height + black_keys = [] + for note in range(self._start_note, self._end_note + 1): + key = self._keys[note] + if key is not None and key.is_white: + fill_region(regions, note, key.rect, cutoff) + else: + black_keys.append((note, key)) + for note, key in black_keys: + if key is not None: + fill_region(regions, note, key.rect, cutoff) + + def draw(self, surf, background, dirty_rects): + """Redraw all altered keyboard keys""" + + changed_keys = self._updates + while changed_keys: + changed_keys.pop().draw(surf, background, dirty_rects) + + def key_down(self, note): + """Signal a key down event for note""" + key = self._keys[note] + if key is not None: + key.down() + + def key_up(self, note): + """Signal a key up event for note""" + key = self._keys[note] + if key is not None: + key.up() + + +def fill_region(regions, note, rect, cutoff): + """Fill the region defined by rect with a (note, velocity, 0) color + + The velocity varies from a small value at the top of the region to + 127 at the bottom. The vertical region 0 to cutoff is split into + three parts, with velocities 42, 84 and 127. Everything below cutoff + has velocity 127. + + """ + + x, y, width, height = rect + if cutoff is None: + cutoff = height + delta_height = cutoff // 3 + regions.fill((note, 42, 0), (x, y, width, delta_height)) + regions.fill((note, 84, 0), (x, y + delta_height, width, delta_height)) + regions.fill( + (note, 127, 0), (x, y + 2 * delta_height, width, height - 2 * delta_height) + ) + + +def is_white_key(note): + """True if note is represented by a white key""" + + key_pattern = [ + True, + False, + True, + True, + False, + True, + False, + True, + True, + False, + True, + False, + ] + return key_pattern[(note - 21) % len(key_pattern)] + + +def usage(): + print("--input [device_id] : Midi message logger") + print("--output [device_id] : Midi piano keyboard") + print("--list : list available midi devices") + + +def main(mode="output", device_id=None): + """Run a Midi example + + Arguments: + mode - if 'output' run a midi keyboard output example + 'input' run a midi event logger input example + 'list' list available midi devices + (default 'output') + device_id - midi device number; if None then use the default midi input or + output device for the system + + """ + + if mode == "input": + input_main(device_id) + elif mode == "output": + output_main(device_id) + elif mode == "list": + print_device_info() + else: + raise ValueError(f"Unknown mode option '{mode}'") + + +if __name__ == "__main__": + device_id: Optional[int] = None + try: + device_id = int(sys.argv[-1]) + except ValueError: + device_id = None + + if "--input" in sys.argv or "-i" in sys.argv: + input_main(device_id) + + elif "--output" in sys.argv or "-o" in sys.argv: + output_main(device_id) + elif "--list" in sys.argv or "-l" in sys.argv: + print_device_info() + else: + usage() + + pg.quit() diff --git a/.venv/Lib/site-packages/pygame/examples/moveit.py b/.venv/Lib/site-packages/pygame/examples/moveit.py new file mode 100644 index 00000000..ffcc7f9e --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/moveit.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python +""" pygame.examples.moveit + +This is the full and final example from the Pygame Tutorial, +"How Do I Make It Move". It creates 10 objects and animates +them on the screen. + +It also has a separate player character that can be controlled with arrow keys. + +Note it's a bit scant on error checking, but it's easy to read. :] +Fortunately, this is python, and we needn't wrestle with a pile of +error codes. +""" +import os +import pygame as pg + +main_dir = os.path.split(os.path.abspath(__file__))[0] + +# Height and Width of screen +WIDTH = 640 +HEIGHT = 480 +# Height and width of the sprite +SPRITE_WIDTH = 80 +SPRITE_HEIGHT = 60 + + +# our game object class +class GameObject: + def __init__(self, image, height, speed): + self.speed = speed + self.image = image + self.pos = image.get_rect().move(0, height) + + # move the object. + def move(self, up=False, down=False, left=False, right=False): + if right: + self.pos.right += self.speed + if left: + self.pos.right -= self.speed + if down: + self.pos.top += self.speed + if up: + self.pos.top -= self.speed + + # controls the object such that it cannot leave the screen's viewpoint + if self.pos.right > WIDTH: + self.pos.left = 0 + if self.pos.top > HEIGHT - SPRITE_HEIGHT: + self.pos.top = 0 + if self.pos.right < SPRITE_WIDTH: + self.pos.right = WIDTH + if self.pos.top < 0: + self.pos.top = HEIGHT - SPRITE_HEIGHT + + +# quick function to load an image +def load_image(name): + path = os.path.join(main_dir, "data", name) + return pg.image.load(path).convert() + + +# here's the full code +def main(): + pg.init() + clock = pg.time.Clock() + screen = pg.display.set_mode((WIDTH, HEIGHT)) + + player = load_image("player1.gif") + entity = load_image("alien1.gif") + background = load_image("liquid.bmp") + + # scale the background image so that it fills the window and + # successfully overwrites the old sprite position. + background = pg.transform.scale2x(background) + background = pg.transform.scale2x(background) + + screen.blit(background, (0, 0)) + + objects = [] + p = GameObject(player, 10, 3) + for x in range(10): + o = GameObject(entity, x * 40, x) + objects.append(o) + + pg.display.set_caption("Move It!") + + # This is a simple event handler that enables player input. + while True: + # Get all keys currently pressed, and move when an arrow key is held. + keys = pg.key.get_pressed() + if keys[pg.K_UP]: + p.move(up=True) + if keys[pg.K_DOWN]: + p.move(down=True) + if keys[pg.K_LEFT]: + p.move(left=True) + if keys[pg.K_RIGHT]: + p.move(right=True) + + # Draw the background + screen.blit(background, (0, 0)) + for e in pg.event.get(): + # quit upon screen exit + if e.type == pg.QUIT: + return + for o in objects: + screen.blit(background, o.pos, o.pos) + for o in objects: + o.move(right=True) + screen.blit(o.image, o.pos) + screen.blit(p.image, p.pos) + clock.tick(60) + pg.display.update() + pg.time.delay(100) + + +if __name__ == "__main__": + main() + pg.quit() diff --git a/.venv/Lib/site-packages/pygame/examples/music_drop_fade.py b/.venv/Lib/site-packages/pygame/examples/music_drop_fade.py new file mode 100644 index 00000000..8988cd35 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/music_drop_fade.py @@ -0,0 +1,253 @@ +#!/usr/bin/env python +""" pygame.examples.music_drop_fade +Fade in and play music from a list while observing several events + +Adds music files to a playlist whenever played by one of the following methods +Music files passed from the commandline are played +Music files and filenames are played when drag and dropped onto the pygame window +Polls the clipboard and plays music files if it finds one there + +Keyboard Controls: +* Press space or enter to pause music playback +* Press up or down to change the music volume +* Press left or right to seek 5 seconds into the track +* Press escape to quit +* Press any other button to skip to the next music file in the list +""" + +from typing import List +import pygame as pg +import os, sys + +VOLUME_CHANGE_AMOUNT = 0.02 # how fast should up and down arrows change the volume? + + +def add_file(filename): + """ + This function will check if filename exists and is a music file + If it is the file will be added to a list of music files(even if already there) + Type checking is by the extension of the file, not by its contents + We can only discover if the file is valid when we mixer.music.load() it later + + It looks in the file directory and its data subdirectory + """ + if filename.rpartition(".")[2].lower() not in music_file_types: + print(f"{filename} not added to file list") + print("only these files types are allowed: ", music_file_types) + return False + elif os.path.exists(filename): + music_file_list.append(filename) + elif os.path.exists(os.path.join(main_dir, filename)): + music_file_list.append(os.path.join(main_dir, filename)) + elif os.path.exists(os.path.join(data_dir, filename)): + music_file_list.append(os.path.join(data_dir, filename)) + else: + print("file not found") + return False + print(f"{filename} added to file list") + return True + + +def play_file(filename): + """ + This function will call add_file and play it if successful + The music will fade in during the first 4 seconds + set_endevent is used to post a MUSIC_DONE event when the song finishes + The main loop will call play_next() when the MUSIC_DONE event is received + """ + global starting_pos + + if add_file(filename): + try: # we must do this in case the file is not a valid audio file + pg.mixer.music.load(music_file_list[-1]) + except pg.error as e: + print(e) # print description such as 'Not an Ogg Vorbis audio stream' + if filename in music_file_list: + music_file_list.remove(filename) + print(f"{filename} removed from file list") + return + pg.mixer.music.play(fade_ms=4000) + pg.mixer.music.set_volume(volume) + + if filename.rpartition(".")[2].lower() in music_can_seek: + print("file supports seeking") + starting_pos = 0 + else: + print("file does not support seeking") + starting_pos = -1 + pg.mixer.music.set_endevent(MUSIC_DONE) + + +def play_next(): + """ + This function will play the next song in music_file_list + It uses pop(0) to get the next song and then appends it to the end of the list + The song will fade in during the first 4 seconds + """ + + global starting_pos + if len(music_file_list) > 1: + nxt = music_file_list.pop(0) + + try: + pg.mixer.music.load(nxt) + except pg.error as e: + print(e) + print(f"{nxt} removed from file list") + + music_file_list.append(nxt) + print("starting next song: ", nxt) + else: + nxt = music_file_list[0] + pg.mixer.music.play(fade_ms=4000) + pg.mixer.music.set_volume(volume) + pg.mixer.music.set_endevent(MUSIC_DONE) + + if nxt.rpartition(".")[2].lower() in music_can_seek: + starting_pos = 0 + else: + starting_pos = -1 + + +def draw_text_line(text, y=0): + """ + Draws a line of text onto the display surface + The text will be centered horizontally at the given y position + The text's height is added to y and returned to the caller + """ + screen = pg.display.get_surface() + surf = font.render(text, 1, (255, 255, 255)) + y += surf.get_height() + x = (screen.get_width() - surf.get_width()) / 2 + screen.blit(surf, (x, y)) + return y + + +def change_music_position(amount): + """ + Changes current playback position by amount seconds. + This only works with OGG and MP3 files. + music.get_pos() returns how many milliseconds the song has played, not + the current position in the file. We must track the starting position + ourselves. music.set_pos() will set the position in seconds. + """ + global starting_pos + + if starting_pos >= 0: # will be -1 unless play_file() was OGG or MP3 + played_for = pg.mixer.music.get_pos() / 1000.0 + old_pos = starting_pos + played_for + starting_pos = old_pos + amount + pg.mixer.music.play(start=starting_pos) + print(f"jumped from {old_pos} to {starting_pos}") + + +MUSIC_DONE = pg.event.custom_type() # event to be set as mixer.music.set_endevent() +main_dir = os.path.split(os.path.abspath(__file__))[0] +data_dir = os.path.join(main_dir, "data") + +starting_pos = 0 # needed to fast forward and rewind +volume = 0.75 +music_file_list: List[str] = [] +music_file_types = ("mp3", "ogg", "mid", "mod", "it", "xm", "wav") +music_can_seek = ("mp3", "ogg", "mod", "it", "xm") + + +def main(): + global font # this will be used by the draw_text_line function + global volume, starting_pos + running = True + paused = False + + # we will be polling for key up and key down events + # users should be able to change the volume by holding the up and down arrows + # the change_volume variable will be set by key down events and cleared by key up events + change_volume = 0 + + pg.init() + pg.display.set_mode((640, 480)) + font = pg.font.SysFont("Arial", 24) + clock = pg.time.Clock() + + pg.scrap.init() + pg.SCRAP_TEXT = pg.scrap.get_types()[0] # TODO remove when scrap module is fixed + scrap_get = pg.scrap.get(pg.SCRAP_TEXT) + clipped = "" if scrap_get is None else scrap_get.decode("UTF-8") + # store the current text from the clipboard TODO remove decode + + # add the command line arguments to the music_file_list + for arg in sys.argv[1:]: + add_file(arg) + play_file("house_lo.ogg") # play default music included with pygame + + # draw instructions on screen + y = draw_text_line("Drop music files or path names onto this window", 20) + y = draw_text_line("Copy file names into the clipboard", y) + y = draw_text_line("Or feed them from the command line", y) + y = draw_text_line("If it's music it will play!", y) + y = draw_text_line("SPACE to pause or UP/DOWN to change volume", y) + y = draw_text_line("LEFT and RIGHT will skip around the track", y) + draw_text_line("Other keys will start the next track", y) + + """ + This is the main loop + It will respond to drag and drop, clipboard changes, and key presses + """ + while running: + for ev in pg.event.get(): + if ev.type == pg.QUIT: + running = False + elif ev.type == pg.DROPTEXT: + play_file(ev.text) + elif ev.type == pg.DROPFILE: + play_file(ev.file) + elif ev.type == MUSIC_DONE: + play_next() + elif ev.type == pg.KEYDOWN: + if ev.key == pg.K_ESCAPE: + running = False # exit loop + elif ev.key in (pg.K_SPACE, pg.K_RETURN): + if paused: + pg.mixer.music.unpause() + paused = False + else: + pg.mixer.music.pause() + paused = True + elif ev.key == pg.K_UP: + change_volume = VOLUME_CHANGE_AMOUNT + elif ev.key == pg.K_DOWN: + change_volume = -VOLUME_CHANGE_AMOUNT + elif ev.key == pg.K_RIGHT: + change_music_position(+5) + elif ev.key == pg.K_LEFT: + change_music_position(-5) + + else: + play_next() + + elif ev.type == pg.KEYUP: + if ev.key in (pg.K_UP, pg.K_DOWN): + change_volume = 0 + + # is the user holding up or down? + if change_volume: + volume += change_volume + volume = min(max(0, volume), 1) # volume should be between 0 and 1 + pg.mixer.music.set_volume(volume) + print("volume:", volume) + + # TODO remove decode when SDL2 scrap is fixed + scrap_get = pg.scrap.get(pg.SCRAP_TEXT) + new_text = "" if scrap_get is None else scrap_get.decode("UTF-8") + + if new_text != clipped: # has the clipboard changed? + clipped = new_text + play_file(clipped) # try to play the file if it has + + pg.display.flip() + clock.tick(9) # keep CPU use down by updating screen less often + + pg.quit() + + +if __name__ == "__main__": + main() diff --git a/.venv/Lib/site-packages/pygame/examples/pixelarray.py b/.venv/Lib/site-packages/pygame/examples/pixelarray.py new file mode 100644 index 00000000..6f5d58d0 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/pixelarray.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python +""" pygame.examples.pixelarray + +PixelArray does array processing of pixels. +Sort of like another array processor called 'numpy' - But for pixels. + + Flip it, + stripe it, + rotate it. + +Controls +-------- + +To see different effects - press a key or click a mouse. +""" +import os +import pygame as pg + + +main_dir = os.path.split(os.path.abspath(__file__))[0] +data_dir = os.path.join(main_dir, "data") + + +def show(image): + screen = pg.display.get_surface() + screen.fill((255, 255, 255)) + screen.blit(image, (0, 0)) + pg.display.flip() + while True: + event = pg.event.wait() + if event.type == pg.QUIT: + pg.quit() + raise SystemExit + if event.type in [pg.MOUSEBUTTONDOWN, pg.KEYDOWN]: + break + + +def main(): + pg.init() + + pg.display.set_mode((255, 255)) + surface = pg.Surface((255, 255)) + + pg.display.flip() + + # Create the PixelArray. + ar = pg.PixelArray(surface) + + # Do some easy gradient effect. + for y in range(255): + r, g, b = y, y, y + ar[:, y] = (r, g, b) + del ar + show(surface) + + # We have made some gradient effect, now flip it. + ar = pg.PixelArray(surface) + ar[:] = ar[:, ::-1] + del ar + show(surface) + + # Every second column will be made blue + ar = pg.PixelArray(surface) + ar[::2] = (0, 0, 255) + del ar + show(surface) + + # Every second row will be made green + ar = pg.PixelArray(surface) + ar[:, ::2] = (0, 255, 0) + del ar + show(surface) + + # Manipulate the image. Flip it around the y axis. + surface = pg.image.load(os.path.join(data_dir, "arraydemo.bmp")) + ar = pg.PixelArray(surface) + ar[:] = ar[:, ::-1] + del ar + show(surface) + + # Flip the image around the x axis. + ar = pg.PixelArray(surface) + ar[:] = ar[::-1, :] + del ar + show(surface) + + # Every second column will be made white. + ar = pg.PixelArray(surface) + ar[::2] = (255, 255, 255) + del ar + show(surface) + + # Flip the image around both axes, restoring its original layout. + ar = pg.PixelArray(surface) + ar[:] = ar[::-1, ::-1] + del ar + show(surface) + + # Rotate 90 degrees clockwise. + w, h = surface.get_size() + surface2 = pg.Surface((h, w), surface.get_flags(), surface) + ar = pg.PixelArray(surface) + ar2 = pg.PixelArray(surface2) + ar2[...] = ar.transpose()[::-1, :] + del ar, ar2 + show(surface2) + + # Scale it by throwing each second pixel away. + surface = pg.image.load(os.path.join(data_dir, "arraydemo.bmp")) + ar = pg.PixelArray(surface) + sf2 = ar[::2, ::2].make_surface() + del ar + show(sf2) + + # Replace anything looking like the blue color from the text. + ar = pg.PixelArray(surface) + ar.replace((60, 60, 255), (0, 255, 0), 0.06) + del ar + show(surface) + + # Extract anything which might be somewhat black. + surface = pg.image.load(os.path.join(data_dir, "arraydemo.bmp")) + ar = pg.PixelArray(surface) + ar2 = ar.extract((0, 0, 0), 0.07) + sf2 = ar2.surface + del ar, ar2 + show(sf2) + + # Compare two images. + surface = pg.image.load(os.path.join(data_dir, "alien1.gif")) + surface2 = pg.image.load(os.path.join(data_dir, "alien2.gif")) + ar1 = pg.PixelArray(surface) + ar2 = pg.PixelArray(surface2) + ar3 = ar1.compare(ar2, 0.07) + sf3 = ar3.surface + del ar1, ar2, ar3 + show(sf3) + + +if __name__ == "__main__": + main() + pg.quit() diff --git a/.venv/Lib/site-packages/pygame/examples/playmus.py b/.venv/Lib/site-packages/pygame/examples/playmus.py new file mode 100644 index 00000000..8c677333 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/playmus.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python +""" pygame.examples.playmus + +A simple music player. + + Use pygame.mixer.music to play an audio file. + +A window is created to handle keyboard events for playback commands. + + +Keyboard Controls +----------------- + +space - play/pause toggle +r - rewind +f - fade out +q - stop + +""" +import sys + +import pygame as pg +import pygame.freetype + + +class Window: + """The application's Pygame window + + A Window instance manages the creation of and drawing to a + window. It is a singleton class. Only one instance can exist. + + """ + + instance = None + + def __new__(cls, *args, **kwds): + """Return an open Pygame window""" + + if Window.instance is not None: + return Window.instance + self = object.__new__(cls) + pg.display.init() + self.screen = pg.display.set_mode((600, 400)) + Window.instance = self + return self + + def __init__(self, title): + pg.display.set_caption(title) + self.text_color = (254, 231, 21, 255) + self.background_color = (16, 24, 32, 255) + self.screen.fill(self.background_color) + pg.display.flip() + + pygame.freetype.init() + self.font = pygame.freetype.Font(None, 20) + self.font.origin = True + self.ascender = int(self.font.get_sized_ascender() * 1.5) + self.descender = int(self.font.get_sized_descender() * 1.5) + self.line_height = self.ascender - self.descender + + self.write_lines( + "\nPress 'q' or 'ESCAPE' or close this window to quit\n" + "Press 'SPACE' to play / pause\n" + "Press 'r' to rewind to the beginning (restart)\n" + "Press 'f' to fade music out over 5 seconds\n\n" + "Window will quit automatically when music ends\n", + 0, + ) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + return False + + def close(self): + pg.display.quit() + Window.instance = None + + def write_lines(self, text, line=0): + w, h = self.screen.get_size() + line_height = self.line_height + nlines = h // line_height + if line < 0: + line = nlines + line + for i, text_line in enumerate(text.split("\n"), line): + y = i * line_height + self.ascender + # Clear the line first. + self.screen.fill( + self.background_color, (0, i * line_height, w, line_height) + ) + # Write new text. + self.font.render_to(self.screen, (15, y), text_line, self.text_color) + pg.display.flip() + + +def show_usage_message(): + print("Usage: python playmus.py ") + print(" python -m pygame.examples.playmus ") + + +def main(file_path): + """Play an audio file with pg.mixer.music""" + + with Window(file_path) as win: + win.write_lines("Loading ...", -1) + pg.mixer.init(frequency=44100) + try: + paused = False + pg.mixer.music.load(file_path) + + # Make sure the event loop ticks over at least every 0.5 seconds. + pg.time.set_timer(pg.USEREVENT, 500) + + pg.mixer.music.play() + win.write_lines("Playing ...\n", -1) + + while pg.mixer.music.get_busy() or paused: + e = pg.event.wait() + if e.type == pg.KEYDOWN: + key = e.key + if key == pg.K_SPACE: + if paused: + pg.mixer.music.unpause() + paused = False + win.write_lines("Playing ...\n", -1) + else: + pg.mixer.music.pause() + paused = True + win.write_lines("Paused ...\n", -1) + elif key == pg.K_r: + if file_path[-3:].lower() in ("ogg", "mp3", "mod"): + status = "Rewound." + pg.mixer.music.rewind() + else: + status = "Restarted." + pg.mixer.music.play() + if paused: + pg.mixer.music.pause() + win.write_lines(status, -1) + elif key == pg.K_f: + win.write_lines("Fading out ...\n", -1) + pg.mixer.music.fadeout(5000) + # when finished get_busy() will return False. + elif key in [pg.K_q, pg.K_ESCAPE]: + paused = False + pg.mixer.music.stop() + # get_busy() will now return False. + elif e.type == pg.QUIT: + paused = False + pg.mixer.music.stop() + # get_busy() will now return False. + pg.time.set_timer(pg.USEREVENT, 0) + finally: + pg.mixer.quit() + pg.quit() + + +if __name__ == "__main__": + # Check the only command line argument, a file path + if len(sys.argv) != 2: + show_usage_message() + else: + main(sys.argv[1]) diff --git a/.venv/Lib/site-packages/pygame/examples/resizing_new.py b/.venv/Lib/site-packages/pygame/examples/resizing_new.py new file mode 100644 index 00000000..cda01f2f --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/resizing_new.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +import pygame as pg + +pg.init() + +RES = (160, 120) +FPS = 30 +clock = pg.time.Clock() + +screen = pg.display.set_mode(RES, pg.RESIZABLE) +pg.display._set_autoresize(False) + +# MAIN LOOP + +done = False + +i = 0 +j = 0 + +while not done: + for event in pg.event.get(): + if event.type == pg.KEYDOWN and event.key == pg.K_q: + done = True + if event.type == pg.QUIT: + done = True + # if event.type==pg.WINDOWRESIZED: + # screen=pg.display.get_surface() + if event.type == pg.VIDEORESIZE: + screen = pg.display.get_surface() + i += 1 + i = i % screen.get_width() + j += i % 2 + j = j % screen.get_height() + + screen.fill((255, 0, 255)) + pg.draw.circle(screen, (0, 0, 0), (100, 100), 20) + pg.draw.circle(screen, (0, 0, 200), (0, 0), 10) + pg.draw.circle(screen, (200, 0, 0), (160, 120), 30) + pg.draw.line(screen, (250, 250, 0), (0, 120), (160, 0)) + pg.draw.circle(screen, (255, 255, 255), (i, j), 5) + + pg.display.flip() + clock.tick(FPS) +pg.quit() diff --git a/.venv/Lib/site-packages/pygame/examples/scaletest.py b/.venv/Lib/site-packages/pygame/examples/scaletest.py new file mode 100644 index 00000000..0a79f6ff --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/scaletest.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python +""" pygame.examples.scaletest + +Shows an interactive image scaler. + +""" +import sys +import time +import pygame as pg + + +def main(imagefile, convert_alpha=False, run_speed_test=False): + """show an interactive image scaler + + Args: + imagefile - name of source image (required) + convert_alpha - use convert_alpha() on the surf (default False) + run_speed_test - (default False) + """ + + # initialize display + pg.display.init() + # load background image + background = pg.image.load(imagefile) + + if run_speed_test: + if convert_alpha: + # convert_alpha() requires the display mode to be set + pg.display.set_mode((1, 1)) + background = background.convert_alpha() + + SpeedTest(background) + return + + # start fullscreen mode + screen = pg.display.set_mode((1024, 768), pg.FULLSCREEN) + if convert_alpha: + background = background.convert_alpha() + + # turn off the mouse pointer + pg.mouse.set_visible(0) + # main loop + bRunning = True + bUp = False + bDown = False + bLeft = False + bRight = False + cursize = [background.get_width(), background.get_height()] + while bRunning: + image = pg.transform.smoothscale(background, cursize) + imgpos = image.get_rect(centerx=512, centery=384) + screen.fill((255, 255, 255)) + screen.blit(image, imgpos) + pg.display.flip() + for event in pg.event.get(): + if event.type == pg.QUIT or ( + event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE + ): + bRunning = False + if event.type == pg.KEYDOWN: + if event.key == pg.K_UP: + bUp = True + if event.key == pg.K_DOWN: + bDown = True + if event.key == pg.K_LEFT: + bLeft = True + if event.key == pg.K_RIGHT: + bRight = True + if event.type == pg.KEYUP: + if event.key == pg.K_UP: + bUp = False + if event.key == pg.K_DOWN: + bDown = False + if event.key == pg.K_LEFT: + bLeft = False + if event.key == pg.K_RIGHT: + bRight = False + if bUp: + cursize[1] -= 2 + if cursize[1] < 1: + cursize[1] = 1 + if bDown: + cursize[1] += 2 + if bLeft: + cursize[0] -= 2 + if cursize[0] < 1: + cursize[0] = 1 + if bRight: + cursize[0] += 2 + pg.quit() + + +def SpeedTest(image): + print(f"\nImage Scaling Speed Test - Image Size {str(image.get_size())}\n") + + imgsize = [image.get_width(), image.get_height()] + duration = 0.0 + for i in range(128): + shrinkx = (imgsize[0] * i) // 128 + shrinky = (imgsize[1] * i) // 128 + start = time.time() + tempimg = pg.transform.smoothscale(image, (shrinkx, shrinky)) + duration += time.time() - start + del tempimg + + print(f"Average transform.smoothscale shrink time: {duration / 128 * 1000:.4f} ms.") + + duration = 0.0 + for i in range(128): + expandx = (imgsize[0] * (i + 129)) // 128 + expandy = (imgsize[1] * (i + 129)) // 128 + start = time.time() + tempimg = pg.transform.smoothscale(image, (expandx, expandy)) + duration += time.time() - start + del tempimg + + print(f"Average transform.smoothscale expand time: {duration / 128 * 1000:.4f} ms.") + + duration = 0.0 + for i in range(128): + shrinkx = (imgsize[0] * i) // 128 + shrinky = (imgsize[1] * i) // 128 + start = time.time() + tempimg = pg.transform.scale(image, (shrinkx, shrinky)) + duration += time.time() - start + del tempimg + + print(f"Average transform.scale shrink time: {duration / 128 * 1000:.4f} ms.") + + duration = 0.0 + for i in range(128): + expandx = (imgsize[0] * (i + 129)) // 128 + expandy = (imgsize[1] * (i + 129)) // 128 + start = time.time() + tempimg = pg.transform.scale(image, (expandx, expandy)) + duration += time.time() - start + del tempimg + + print(f"Average transform.scale expand time: {duration / 128 * 1000:.4f} ms.") + + +if __name__ == "__main__": + # check input parameters + if len(sys.argv) < 2: + print(f"\nUsage: {sys.argv[0]} imagefile [-t] [-convert_alpha]") + print(" imagefile image filename (required)") + print(" -t run speed test") + print(" -convert_alpha use convert_alpha() on the image's " "surface\n") + else: + main( + sys.argv[1], + convert_alpha="-convert_alpha" in sys.argv, + run_speed_test="-t" in sys.argv, + ) diff --git a/.venv/Lib/site-packages/pygame/examples/scrap_clipboard.py b/.venv/Lib/site-packages/pygame/examples/scrap_clipboard.py new file mode 100644 index 00000000..e6b9773b --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/scrap_clipboard.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +""" pygame.examples.scrap_clipboard + +Demonstrates the clipboard capabilities of pygame. + +Copy/paste! + + +Keyboard Controls +----------------- + +g - get and print types in clipboard. If, image blit to screen. +p - place some text into clipboard +a - print types available in the clipboard +i - put image into the clipboard +""" +import os + +import pygame as pg +import pygame.scrap as scrap + +from io import BytesIO + + +def usage(): + print("Press the 'g' key to get all of the current clipboard data") + print("Press the 'p' key to put a string into the clipboard") + print("Press the 'a' key to get a list of the currently available types") + print("Press the 'i' key to put an image into the clipboard") + + +main_dir = os.path.split(os.path.abspath(__file__))[0] + +pg.init() +screen = pg.display.set_mode((200, 200)) +c = pg.time.Clock() +going = True + +# Initialize the scrap module and use the clipboard mode. +scrap.init() +scrap.set_mode(pg.SCRAP_CLIPBOARD) + +usage() + +while going: + for e in pg.event.get(): + if e.type == pg.QUIT or (e.type == pg.KEYDOWN and e.key == pg.K_ESCAPE): + going = False + + elif e.type == pg.KEYDOWN and e.key == pg.K_g: + # This means to look for data. + print("Getting the different clipboard data..") + for t in scrap.get_types(): + r = scrap.get(t) + if r and len(r) > 500: + print(f"Type {t} : (large {len(r)} byte buffer)") + elif r is None: + print(f"Type {t} : None") + else: + print(f"Type {t} : '{r.decode('ascii', 'ignore')}'") + if "image" in t: + namehint = t.split("/")[1] + if namehint in ["bmp", "png", "jpg"]: + f = BytesIO(r) + loaded_surf = pg.image.load(f, "." + namehint) + screen.blit(loaded_surf, (0, 0)) + + elif e.type == pg.KEYDOWN and e.key == pg.K_p: + # Place some text into the selection. + print("Placing clipboard text.") + scrap.put(pg.SCRAP_TEXT, b"Hello. This is a message from scrap.") + + elif e.type == pg.KEYDOWN and e.key == pg.K_a: + # Get all available types. + print("Getting the available types from the clipboard.") + types = scrap.get_types() + print(types) + if len(types) > 0: + print(f"Contains {types[0]}: {scrap.contains(types[0])}") + print("Contains _INVALID_: ", scrap.contains("_INVALID_")) + + elif e.type == pg.KEYDOWN and e.key == pg.K_i: + print("Putting image into the clipboard.") + scrap.set_mode(pg.SCRAP_CLIPBOARD) + fp = open(os.path.join(main_dir, "data", "liquid.bmp"), "rb") + buf = fp.read() + scrap.put("image/bmp", buf) + fp.close() + + elif e.type in (pg.KEYDOWN, pg.MOUSEBUTTONDOWN): + usage() + pg.display.flip() + c.tick(40) +pg.quit() diff --git a/.venv/Lib/site-packages/pygame/examples/scroll.py b/.venv/Lib/site-packages/pygame/examples/scroll.py new file mode 100644 index 00000000..7c61ebbd --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/scroll.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python +""" pygame.examples.scroll + +An zoomed image viewer that demonstrates Surface.scroll + +This example shows a scrollable image that has a zoom factor of eight. +It uses the Surface.scroll function to shift the image on the display +surface. A clip rectangle protects a margin area. If called as a function, +the example accepts an optional image file path. If run as a program +it takes an optional file path command line argument. If no file +is provided a default image file is used. + +When running click on a black triangle to move one pixel in the direction +the triangle points. Or use the arrow keys. Close the window or press ESC +to quit. +""" +import sys +import os + +import pygame as pg +from pygame.transform import scale + +main_dir = os.path.dirname(os.path.abspath(__file__)) + + +DIR_UP = 1 +DIR_DOWN = 2 +DIR_LEFT = 3 +DIR_RIGHT = 4 + +zoom_factor = 8 + + +def draw_arrow(surf, color, posn, direction: int): + x, y = posn + if direction == DIR_UP: + pointlist = ((x - 29, y + 30), (x + 30, y + 30), (x + 1, y - 29), (x, y - 29)) + elif direction == DIR_DOWN: + pointlist = ((x - 29, y - 29), (x + 30, y - 29), (x + 1, y + 30), (x, y + 30)) + elif direction == DIR_LEFT: + pointlist = ((x + 30, y - 29), (x + 30, y + 30), (x - 29, y + 1), (x - 29, y)) + else: + pointlist = ((x - 29, y - 29), (x - 29, y + 30), (x + 30, y + 1), (x + 30, y)) + pg.draw.polygon(surf, color, pointlist) + + +def add_arrow_button(screen, regions, posn, direction): + draw_arrow(screen, "black", posn, direction) + draw_arrow(regions, (direction, 0, 0), posn, direction) + + +def scroll_view(screen, image: pg.Surface, direction: int, view_rect): + src_rect = None + dst_rect = None + zoom_view_rect = screen.get_clip() + image_w, image_h = image.get_size() + if direction == DIR_UP: + if view_rect.top > 0: + screen.scroll(dy=zoom_factor) + view_rect.move_ip(0, -1) + src_rect = view_rect.copy() + src_rect.h = 1 + dst_rect = zoom_view_rect.copy() + dst_rect.h = zoom_factor + elif direction == DIR_DOWN: + if view_rect.bottom < image_h: + screen.scroll(dy=-zoom_factor) + view_rect.move_ip(0, 1) + src_rect = view_rect.copy() + src_rect.h = 1 + src_rect.bottom = view_rect.bottom + dst_rect = zoom_view_rect.copy() + dst_rect.h = zoom_factor + dst_rect.bottom = zoom_view_rect.bottom + elif direction == DIR_LEFT: + if view_rect.left > 0: + screen.scroll(dx=zoom_factor) + view_rect.move_ip(-1, 0) + src_rect = view_rect.copy() + src_rect.w = 1 + dst_rect = zoom_view_rect.copy() + dst_rect.w = zoom_factor + elif direction == DIR_RIGHT: + if view_rect.right < image_w: + screen.scroll(dx=-zoom_factor) + view_rect.move_ip(1, 0) + src_rect = view_rect.copy() + src_rect.w = 1 + src_rect.right = view_rect.right + dst_rect = zoom_view_rect.copy() + dst_rect.w = zoom_factor + dst_rect.right = zoom_view_rect.right + + if src_rect is not None and dst_rect is not None: + scale(image.subsurface(src_rect), dst_rect.size, screen.subsurface(dst_rect)) + pg.display.update(zoom_view_rect) + + +def main(image_file=None): + if image_file is None: + image_file = os.path.join(main_dir, "data", "arraydemo.bmp") + margin = 80 + view_size = (30, 20) + zoom_view_size = (view_size[0] * zoom_factor, view_size[1] * zoom_factor) + win_size = (zoom_view_size[0] + 2 * margin, zoom_view_size[1] + 2 * margin) + background_color = pg.Color("beige") + + pg.init() + pg.display.set_caption("Scroll Example") + + # set up key repeating so we can hold down the key to scroll. + old_k_delay, old_k_interval = pg.key.get_repeat() + pg.key.set_repeat(500, 30) + + try: + screen = pg.display.set_mode(win_size) + screen.fill(background_color) + pg.display.flip() + + image = pg.image.load(image_file).convert() + image_w, image_h = image.get_size() + + if image_w < view_size[0] or image_h < view_size[1]: + print("The source image is too small for this example.") + print("A %i by %i or larger image is required." % zoom_view_size) + return + + regions = pg.Surface(win_size, 0, 24) + add_arrow_button(screen, regions, (40, win_size[1] // 2), DIR_LEFT) + add_arrow_button( + screen, regions, (win_size[0] - 40, win_size[1] // 2), DIR_RIGHT + ) + add_arrow_button(screen, regions, (win_size[0] // 2, 40), DIR_UP) + add_arrow_button( + screen, regions, (win_size[0] // 2, win_size[1] - 40), DIR_DOWN + ) + pg.display.flip() + + screen.set_clip((margin, margin, zoom_view_size[0], zoom_view_size[1])) + + view_rect = pg.Rect(0, 0, view_size[0], view_size[1]) + + scale( + image.subsurface(view_rect), + zoom_view_size, + screen.subsurface(screen.get_clip()), + ) + pg.display.flip() + + # the direction we will scroll in. + direction = None + + clock = pg.time.Clock() + clock.tick() + + going = True + + while going: + # wait for events before doing anything. + # events = [pg.event.wait()] + pg.event.get() + events = pg.event.get() + + # During the loop, if a key is held, scroll the view. + keys = pg.key.get_pressed() + if keys[pg.K_UP]: + scroll_view(screen, image, DIR_UP, view_rect) + if keys[pg.K_DOWN]: + scroll_view(screen, image, DIR_DOWN, view_rect) + if keys[pg.K_LEFT]: + scroll_view(screen, image, DIR_LEFT, view_rect) + if keys[pg.K_RIGHT]: + scroll_view(screen, image, DIR_RIGHT, view_rect) + + for e in events: + # quit if the event is quit. + if e.type == pg.QUIT: + going = False + + # handle mouse button presses on arrows. + elif e.type == pg.MOUSEBUTTONDOWN: + direction = regions.get_at(e.pos)[0] + + elif e.type == pg.MOUSEBUTTONUP: + direction = None + + if direction: + scroll_view(screen, image, direction, view_rect) + clock.tick(30) + + finally: + pg.key.set_repeat(old_k_delay, old_k_interval) + pg.quit() + + +if __name__ == "__main__": + image_file = sys.argv[1] if len(sys.argv) > 1 else None + main(image_file) diff --git a/.venv/Lib/site-packages/pygame/examples/setmodescale.py b/.venv/Lib/site-packages/pygame/examples/setmodescale.py new file mode 100644 index 00000000..3f427d2a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/setmodescale.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +""" pygame.examples.setmodescale + +On high resolution displays(4k, 1080p) and tiny graphics games (640x480) +show up very small so that they are unplayable. SCALED scales up the window +for you. The game thinks it's a 640x480 window, but really it can be bigger. +Mouse events are scaled for you, so your game doesn't need to do it. + +Passing SCALED to pygame.display.set_mode means the resolution depends +on desktop size and the graphics are scaled. +""" + +import pygame as pg + +pg.init() + +RES = (160, 120) +FPS = 30 +clock = pg.time.Clock() + +print("desktops", pg.display.get_desktop_sizes()) +screen = pg.display.set_mode(RES, pg.SCALED | pg.RESIZABLE) + +# MAIN LOOP + +done = False + +i = 0 +j = 0 + +r_name, r_flags = pg.display._get_renderer_info() +print("renderer:", r_name, "flags:", bin(r_flags)) +for flag, name in [ + (1, "software"), + (2, "accelerated"), + (4, "VSync"), + (8, "render to texture"), +]: + if flag & r_flags: + print(name) + +while not done: + for event in pg.event.get(): + if event.type == pg.KEYDOWN and event.key == pg.K_q: + done = True + if event.type == pg.QUIT: + done = True + if event.type == pg.KEYDOWN and event.key == pg.K_f: + pg.display.toggle_fullscreen() + if event.type == pg.VIDEORESIZE: + pg.display._resize_event(event) + + i += 1 + i = i % screen.get_width() + j += i % 2 + j = j % screen.get_height() + + screen.fill((255, 0, 255)) + pg.draw.circle(screen, (0, 0, 0), (100, 100), 20) + pg.draw.circle(screen, (0, 0, 200), (0, 0), 10) + pg.draw.circle(screen, (200, 0, 0), (160, 120), 30) + pg.draw.line(screen, (250, 250, 0), (0, 120), (160, 0)) + pg.draw.circle(screen, (255, 255, 255), (i, j), 5) + + pg.display.flip() + clock.tick(FPS) +pg.quit() diff --git a/.venv/Lib/site-packages/pygame/examples/sound.py b/.venv/Lib/site-packages/pygame/examples/sound.py new file mode 100644 index 00000000..c5a23b97 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/sound.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +""" pygame.examples.sound + +Playing a soundfile and waiting for it to finish. You'll need the +pygame.mixer module for this to work. Note how in this simple example +we don't even bother loading all of the pygame package. +Just pick the mixer for sound and time for the delay function. + +Optional command line argument: audio file name +""" +import os +import sys +import pygame as pg + +main_dir = os.path.split(os.path.abspath(__file__))[0] + + +def main(file_path=None): + """Play an audio file as a buffered sound sample + + :param str file_path: audio file (default data/secosmic_low.wav) + """ + # choose a desired audio format + pg.mixer.init(11025) # raises exception on fail + + # load the sound + sound = pg.mixer.Sound(file_path) + + # start playing + print("Playing Sound...") + channel = sound.play() + + # poll until finished + while channel.get_busy(): # still playing + print(" ...still going...") + pg.time.wait(1000) + print("...Finished") + pg.quit() + + +if __name__ == "__main__": + if len(sys.argv) > 1: + main(sys.argv[1]) + else: + main(os.path.join(main_dir, "data", "secosmic_lo.wav")) diff --git a/.venv/Lib/site-packages/pygame/examples/sound_array_demos.py b/.venv/Lib/site-packages/pygame/examples/sound_array_demos.py new file mode 100644 index 00000000..855808b5 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/sound_array_demos.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python +""" pygame.examples.sound_array_demos + +Creates an echo effect on any Sound object. + +Uses sndarray and numpy to create offset faded copies of the +original sound. Currently it just uses hardcoded values for the +number of echos and the delay. Easy for you to recreate as +needed. + +version 2. changes: +- Should work with different sample rates now. +- put into a function. +- Uses numpy by default, but falls back on Numeric. +""" +import os +import pygame as pg +from numpy import zeros, int32, int16 +import time + + +# pg.mixer.init(44100, -16, 0) +pg.mixer.init() +# pg.mixer.init(11025, -16, 0) +# pg.mixer.init(11025) + + +def make_echo(sound, samples_per_second, mydebug=True): + """returns a sound which is echoed of the last one.""" + + echo_length = 3.5 + + a1 = pg.sndarray.array(sound) + if mydebug: + print(f"SHAPE1: {a1.shape}") + + length = a1.shape[0] + + # myarr = zeros(length+12000) + myarr = zeros(a1.shape, int32) + + if len(a1.shape) > 1: + # mult = a1.shape[1] + size = (a1.shape[0] + int(echo_length * a1.shape[0]), a1.shape[1]) + # size = (a1.shape[0] + int(a1.shape[0] + (echo_length * 3000)), a1.shape[1]) + else: + # mult = 1 + size = (a1.shape[0] + int(echo_length * a1.shape[0]),) + # size = (a1.shape[0] + int(a1.shape[0] + (echo_length * 3000)),) + + if mydebug: + print(int(echo_length * a1.shape[0])) + myarr = zeros(size, int32) + + if mydebug: + print(f"size {size}") + print(myarr.shape) + myarr[:length] = a1 + # print(myarr[3000:length+3000]) + # print(a1 >> 1) + # print("a1.shape %s" % (a1.shape,)) + # c = myarr[3000:length+(3000*mult)] + # print("c.shape %s" % (c.shape,)) + + incr = int(samples_per_second / echo_length) + gap = length + + myarr[incr : gap + incr] += a1 >> 1 + myarr[incr * 2 : gap + (incr * 2)] += a1 >> 2 + myarr[incr * 3 : gap + (incr * 3)] += a1 >> 3 + myarr[incr * 4 : gap + (incr * 4)] += a1 >> 4 + + if mydebug: + print(f"SHAPE2: {myarr.shape}") + + sound2 = pg.sndarray.make_sound(myarr.astype(int16)) + + return sound2 + + +def slow_down_sound(sound, rate): + """returns a sound which is a slowed down version of the original. + rate - at which the sound should be slowed down. eg. 0.5 would be half speed. + """ + + raise NotImplementedError() + # grow_rate = 1 / rate + # make it 1/rate times longer. + # a1 = pg.sndarray.array(sound) + # surf = pg.surfarray.make_surface(a1) + # print(a1.shape[0] * grow_rate) + # scaled_surf = pg.transform.scale(surf, (int(a1.shape[0] * grow_rate), a1.shape[1])) + # print(scaled_surf) + # print(surf) + + # a2 = a1 * rate + # print(a1.shape) + # print(a2.shape) + # print(a2) + # sound2 = pg.sndarray.make_sound(a2.astype(int16)) + # return sound2 + + +def sound_from_pos(sound, start_pos, samples_per_second=None, inplace=1): + """returns a sound which begins at the start_pos. + start_pos - in seconds from the beginning. + samples_per_second - + """ + + # see if we want to reuse the sound data or not. + if inplace: + a1 = pg.sndarray.samples(sound) + else: + a1 = pg.sndarray.array(sound) + + # see if samples per second has been given. If not, query the pg.mixer. + # eg. it might be set to 22050 + if samples_per_second is None: + samples_per_second = pg.mixer.get_init()[0] + + # figure out the start position in terms of samples. + start_pos_in_samples = int(start_pos * samples_per_second) + + # cut the beginning off the sound at the start position. + a2 = a1[start_pos_in_samples:] + + # make the Sound instance from the array. + sound2 = pg.sndarray.make_sound(a2) + + return sound2 + + +def main(): + """play various sndarray effects""" + + main_dir = os.path.split(os.path.abspath(__file__))[0] + print(f"mixer.get_init {pg.mixer.get_init()}") + + samples_per_second = pg.mixer.get_init()[0] + + print(("-" * 30) + "\n") + print("loading sound") + sound = pg.mixer.Sound(os.path.join(main_dir, "data", "car_door.wav")) + + print("-" * 30) + print("start positions") + print("-" * 30) + + start_pos = 0.1 + sound2 = sound_from_pos(sound, start_pos, samples_per_second) + + print(f"sound.get_length {sound.get_length()}") + print(f"sound2.get_length {sound2.get_length()}") + sound2.play() + while pg.mixer.get_busy(): + pg.time.wait(200) + + print("waiting 2 seconds") + pg.time.wait(2000) + print("playing original sound") + + sound.play() + while pg.mixer.get_busy(): + pg.time.wait(200) + + print("waiting 2 seconds") + pg.time.wait(2000) + + # if 0: + # #TODO: this is broken. + # print(("-" * 30) + "\n") + # print("Slow down the original sound.") + # rate = 0.2 + # slowed_sound = slow_down_sound(sound, rate) + # slowed_sound.play() + # while pg.mixer.get_busy(): + # pg.time.wait(200) + + print("-" * 30) + print("echoing") + print("-" * 30) + + t1 = time.time() + sound2 = make_echo(sound, samples_per_second) + print("time to make echo %i" % (time.time() - t1,)) + + print("original sound") + sound.play() + while pg.mixer.get_busy(): + pg.time.wait(200) + + print("echoed sound") + sound2.play() + while pg.mixer.get_busy(): + pg.time.wait(200) + + sound = pg.mixer.Sound(os.path.join(main_dir, "data", "secosmic_lo.wav")) + + t1 = time.time() + sound3 = make_echo(sound, samples_per_second) + print("time to make echo %i" % (time.time() - t1,)) + + print("original sound") + sound.play() + while pg.mixer.get_busy(): + pg.time.wait(200) + + print("echoed sound") + sound3.play() + while pg.mixer.get_busy(): + pg.time.wait(200) + + pg.quit() + + +if __name__ == "__main__": + main() diff --git a/.venv/Lib/site-packages/pygame/examples/sprite_texture.py b/.venv/Lib/site-packages/pygame/examples/sprite_texture.py new file mode 100644 index 00000000..fd7a63b4 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/sprite_texture.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +""" pygame.examples.sprite_texture + +Experimental! Uses APIs which may disappear in the next release (_sdl2 is private). + + +Hardware accelerated Image objects with pygame.sprite. + +_sdl2.video.Image is a backwards compatible way with to use Texture with +pygame.sprite groups. +""" +import os +import pygame as pg + +if pg.get_sdl_version()[0] < 2: + raise SystemExit("This example requires pygame 2 and SDL2.") +from pygame._sdl2 import Window, Texture, Image, Renderer + + +data_dir = os.path.join(os.path.split(os.path.abspath(__file__))[0], "data") + + +def load_img(file): + return pg.image.load(os.path.join(data_dir, file)) + + +pg.display.init() +pg.key.set_repeat(10, 10) + +win = Window("asdf", resizable=True) +renderer = Renderer(win) +tex = Texture.from_surface(renderer, load_img("alien1.gif")) + + +class Something(pg.sprite.Sprite): + def __init__(self, img): + pg.sprite.Sprite.__init__(self) + + self.rect = img.get_rect() + self.image = img + + self.rect.w *= 5 + self.rect.h *= 5 + + img.origin = self.rect.w / 2, self.rect.h / 2 + + +sprite = Something(Image(tex, (0, 0, tex.width / 2, tex.height / 2))) +sprite.rect.x = 250 +sprite.rect.y = 50 + +# sprite2 = Something(Image(sprite.image)) +sprite2 = Something(Image(tex)) +sprite2.rect.x = 250 +sprite2.rect.y = 250 +sprite2.rect.w /= 2 +sprite2.rect.h /= 2 + +group = pg.sprite.Group() +group.add(sprite2) +group.add(sprite) + +import math + +t = 0 +running = True +clock = pg.time.Clock() +renderer.draw_color = (255, 0, 0, 255) + +while running: + for event in pg.event.get(): + if event.type == pg.QUIT: + running = False + elif event.type == pg.KEYDOWN: + if event.key == pg.K_ESCAPE: + running = False + elif event.key == pg.K_LEFT: + sprite.rect.x -= 5 + elif event.key == pg.K_RIGHT: + sprite.rect.x += 5 + elif event.key == pg.K_DOWN: + sprite.rect.y += 5 + elif event.key == pg.K_UP: + sprite.rect.y -= 5 + + renderer.clear() + t += 1 + + img = sprite.image + img.angle += 1 + try: + img.flip_x = t % 50 < 25 + img.flip_y = t % 100 < 50 + except AttributeError: + # backwards compatibility for <=2.1.2 + img.flipX = t % 50 < 25 + img.flipY = t % 100 < 50 + + img.color[0] = int(255.0 * (0.5 + math.sin(0.5 * t + 10.0) / 2.0)) + img.alpha = int(255.0 * (0.5 + math.sin(0.1 * t) / 2.0)) + # img.draw(dstrect=(x, y, 5 * img.srcrect['w'], 5 * img.srcrect['h'])) + + group.draw(renderer) + + renderer.present() + + clock.tick(60) + win.title = str(f"FPS: {clock.get_fps()}") + +pg.quit() diff --git a/.venv/Lib/site-packages/pygame/examples/stars.py b/.venv/Lib/site-packages/pygame/examples/stars.py new file mode 100644 index 00000000..f154511e --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/stars.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +""" pg.examples.stars + + We are all in the gutter, + but some of us are looking at the stars. + -- Oscar Wilde + +A simple starfield example. Note you can move the 'center' of +the starfield by leftclicking in the window. This example show +the basics of creating a window, simple pixel plotting, and input +event management. +""" +import random +import math +import pygame as pg + +# constants +WINSIZE = [640, 480] +WINCENTER = [320, 240] +NUMSTARS = 150 + + +def init_star(steps=-1): + "creates new star values" + dir = random.randrange(100000) + steps_velocity = 1 if steps == -1 else steps * 0.09 + velmult = steps_velocity * (random.random() * 0.6 + 0.4) + vel = [math.sin(dir) * velmult, math.cos(dir) * velmult] + + if steps is None: + return [vel, [WINCENTER[0] + (vel[0] * steps), WINCENTER[1] + (vel[1] * steps)]] + return [vel, WINCENTER[:]] + + +def initialize_stars(): + "creates a new starfield" + random.seed() + stars = [init_star(steps=random.randint(0, WINCENTER[0])) for _ in range(NUMSTARS)] + move_stars(stars) + return stars + + +def draw_stars(surface, stars, color): + "used to draw (and clear) the stars" + for _, pos in stars: + pos = (int(pos[0]), int(pos[1])) + surface.set_at(pos, color) + + +def move_stars(stars): + "animate the star values" + for vel, pos in stars: + pos[0] = pos[0] + vel[0] + pos[1] = pos[1] + vel[1] + if not 0 <= pos[0] <= WINSIZE[0] or not 0 <= pos[1] <= WINSIZE[1]: + vel[:], pos[:] = init_star() + else: + vel[0] = vel[0] * 1.05 + vel[1] = vel[1] * 1.05 + + +def main(): + "This is the starfield code" + # create our starfield + stars = initialize_stars() + + # initialize and prepare screen + pg.init() + screen = pg.display.set_mode(WINSIZE) + pg.display.set_caption("pygame Stars Example") + white = 255, 240, 200 + black = 20, 20, 40 + screen.fill(black) + + clock = pg.time.Clock() + + # main game loop + done = 0 + while not done: + draw_stars(screen, stars, black) + move_stars(stars) + draw_stars(screen, stars, white) + pg.display.update() + for e in pg.event.get(): + if e.type == pg.QUIT or (e.type == pg.KEYUP and e.key == pg.K_ESCAPE): + done = 1 + break + if e.type == pg.MOUSEBUTTONDOWN and e.button == 1: + WINCENTER[:] = list(e.pos) + clock.tick(50) + pg.quit() + + +# So `python -m pygame.example.stars` will work. +if __name__ == "__main__": + main() + + # I prefer the time of insects to the time of stars. + # + # -- Wisława Szymborska diff --git a/.venv/Lib/site-packages/pygame/examples/testsprite.py b/.venv/Lib/site-packages/pygame/examples/testsprite.py new file mode 100644 index 00000000..40469b7d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/testsprite.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python +""" pg.examples.testsprite + +Like the testsprite.c that comes with libsdl, this pygame version shows +lots of sprites moving around. + +It is an abomination of ugly code, and mostly used for testing. + + +See pg.examples.aliens for some prettyier code. +""" +import sys +import os + +from random import randint +from time import time +from typing import List + +import pygame as pg + +main_dir = os.path.split(os.path.abspath(__file__))[0] +data_dir = os.path.join(main_dir, "data") + + +# use this to use update rects or not. +# If the screen is mostly full, then update rects are not useful. +update_rects = True +if "-update_rects" in sys.argv: + update_rects = True +if "-noupdate_rects" in sys.argv: + update_rects = False + +use_static = False +if "-static" in sys.argv: + use_static = True + + +use_layered_dirty = False +if "-layered_dirty" in sys.argv: + update_rects = True + use_layered_dirty = True + + +flags = 0 +if "-flip" in sys.argv: + flags ^= pg.DOUBLEBUF + +if "-fullscreen" in sys.argv: + flags ^= pg.FULLSCREEN + +if "-sw" in sys.argv: + flags ^= pg.SWSURFACE + +use_rle = True + +if "-hw" in sys.argv: + flags ^= pg.HWSURFACE + use_rle = False + +if "-scaled" in sys.argv: + flags ^= pg.SCALED + +screen_dims = [640, 480] + +if "-height" in sys.argv: + i = sys.argv.index("-height") + screen_dims[1] = int(sys.argv[i + 1]) + +if "-width" in sys.argv: + i = sys.argv.index("-width") + screen_dims[0] = int(sys.argv[i + 1]) + +use_alpha = "-alpha" in sys.argv + +print(screen_dims) + + +##class Thingy(pg.sprite.Sprite): +## images = None +## def __init__(self): +## pg.sprite.Sprite.__init__(self) +## self.image = Thingy.images[0] +## self.rect = self.image.get_rect() +## self.rect.x = randint(0, screen_dims[0]) +## self.rect.y = randint(0, screen_dims[1]) +## #self.vel = [randint(-10, 10), randint(-10, 10)] +## self.vel = [randint(-1, 1), randint(-1, 1)] +## +## def move(self): +## for i in [0, 1]: +## nv = self.rect[i] + self.vel[i] +## if nv >= screen_dims[i] or nv < 0: +## self.vel[i] = -self.vel[i] +## nv = self.rect[i] + self.vel[i] +## self.rect[i] = nv + + +class Thingy(pg.sprite.DirtySprite): + images: List[pg.Surface] = [] + + def __init__(self): + ## pg.sprite.Sprite.__init__(self) + pg.sprite.DirtySprite.__init__(self) + self.image = Thingy.images[0] + self.rect = self.image.get_rect() + self.rect.x = randint(0, screen_dims[0]) + self.rect.y = randint(0, screen_dims[1]) + # self.vel = [randint(-10, 10), randint(-10, 10)] + self.vel = [randint(-1, 1), randint(-1, 1)] + self.dirty = 2 + + def update(self): + for i in [0, 1]: + nv = self.rect[i] + self.vel[i] + if nv >= screen_dims[i] or nv < 0: + self.vel[i] = -self.vel[i] + nv = self.rect[i] + self.vel[i] + self.rect[i] = nv + + +class Static(pg.sprite.DirtySprite): + images: List[pg.Surface] = [] + + def __init__(self): + pg.sprite.DirtySprite.__init__(self) + self.image = Static.images[0] + self.rect = self.image.get_rect() + self.rect.x = randint(0, 3 * screen_dims[0] // 4) + self.rect.y = randint(0, 3 * screen_dims[1] // 4) + + +def main( + update_rects=True, + use_static=False, + use_layered_dirty=False, + screen_dims=(640, 480), + use_alpha=False, + flags=0, +): + """Show lots of sprites moving around + + Optional keyword arguments: + update_rects - use the RenderUpdate sprite group class (default True) + use_static - include non-moving images (default False) + use_layered_dirty - Use the FastRenderGroup sprite group (default False) + screen_dims - Pygame window dimensions (default [640, 480]) + use_alpha - use alpha blending (default False) + flags - additional display mode flags (default no additional flags) + + """ + + if use_layered_dirty: + update_rects = True + + pg.init() # needed to initialise time module for get_ticks() + pg.display.init() + + # if "-fast" in sys.argv: + + screen = pg.display.set_mode(screen_dims, flags, vsync="-vsync" in sys.argv) + + # this is mainly for GP2X, so it can quit. + pg.joystick.init() + num_joysticks = pg.joystick.get_count() + if num_joysticks > 0: + stick = pg.joystick.Joystick(0) + stick.init() # now we will receive events for the joystick + + screen.fill([0, 0, 0]) + pg.display.flip() + sprite_surface = pg.image.load(os.path.join(data_dir, "asprite.bmp")) + sprite_surface2 = pg.image.load(os.path.join(data_dir, "static.png")) + + if use_rle: + sprite_surface.set_colorkey([0xFF, 0xFF, 0xFF], pg.SRCCOLORKEY | pg.RLEACCEL) + sprite_surface2.set_colorkey([0xFF, 0xFF, 0xFF], pg.SRCCOLORKEY | pg.RLEACCEL) + else: + sprite_surface.set_colorkey([0xFF, 0xFF, 0xFF], pg.SRCCOLORKEY) + sprite_surface2.set_colorkey([0xFF, 0xFF, 0xFF], pg.SRCCOLORKEY) + + if use_alpha: + sprite_surface = sprite_surface.convert_alpha() + sprite_surface2 = sprite_surface2.convert_alpha() + else: + sprite_surface = sprite_surface.convert() + sprite_surface2 = sprite_surface2.convert() + + Thingy.images = [sprite_surface] + if use_static: + Static.images = [sprite_surface2] + + if len(sys.argv) > 1: + try: + numsprites = int(sys.argv[-1]) + except Exception: + numsprites = 100 + else: + numsprites = 100 + sprites = None + if use_layered_dirty: + ## sprites = pg.sprite.FastRenderGroup() + sprites = pg.sprite.LayeredDirty() + else: + if update_rects: + sprites = pg.sprite.RenderUpdates() + else: + sprites = pg.sprite.Group() + + for i in range(0, numsprites): + if use_static and i % 2 == 0: + sprites.add(Static()) + sprites.add(Thingy()) + + frames = 0 + start = time() + + background = pg.Surface(screen.get_size()) + background = background.convert() + background.fill([0, 0, 0]) + + going = True + while going: + if not update_rects: + screen.fill([0, 0, 0]) + + ## for sprite in sprites: + ## sprite.move() + + if update_rects: + sprites.clear(screen, background) + sprites.update() + + rects = sprites.draw(screen) + if update_rects: + pg.display.update(rects) + else: + pg.display.flip() + + for event in pg.event.get(): + if event.type in [pg.QUIT, pg.KEYDOWN, pg.QUIT, pg.JOYBUTTONDOWN]: + going = False + + frames += 1 + end = time() + print(f"FPS: {frames / (end - start):f}") + pg.quit() + + +if __name__ == "__main__": + main(update_rects, use_static, use_layered_dirty, screen_dims, use_alpha, flags) diff --git a/.venv/Lib/site-packages/pygame/examples/textinput.py b/.venv/Lib/site-packages/pygame/examples/textinput.py new file mode 100644 index 00000000..8852a8fa --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/textinput.py @@ -0,0 +1,235 @@ +#!/usr/bin/env python +""" pg.examples.textinput + +A little "console" where you can write in text. + +Shows how to use the TEXTEDITING and TEXTINPUT events. +""" +import sys +import os +from typing import List + +import pygame +import pygame as pg +import pygame.freetype as freetype + +# This environment variable is important +# If not added the candidate list will not show +os.environ["SDL_IME_SHOW_UI"] = "1" + + +class TextInput: + """ + A simple TextInput class that allows you to receive inputs in pygame. + """ + + # Add font name for each language, + # otherwise some text can't be correctly displayed. + FONT_NAMES = ",".join( + str(x) + for x in [ + "notosanscjktcregular", + "notosansmonocjktcregular", + "notosansregular,", + "microsoftjhengheimicrosoftjhengheiuilight", + "microsoftyaheimicrosoftyaheiuilight", + "msgothicmsuigothicmspgothic", + "msmincho", + "Arial", + ] + ) + + def __init__( + self, prompt: str, pos, screen_dimensions, print_event: bool, text_color="white" + ) -> None: + self.prompt = prompt + self.print_event = print_event + # position of chatlist and chatbox + self.CHAT_LIST_POS = pg.Rect((pos[0], pos[1] + 50), (screen_dimensions[0], 400)) + self.CHAT_BOX_POS = pg.Rect(pos, (screen_dimensions[1], 40)) + self.CHAT_LIST_MAXSIZE = 20 + + self._ime_editing = False + self._ime_text = "" + self._ime_text_pos = 0 + self._ime_editing_text = "" + self._ime_editing_pos = 0 + self.chat_list: List[str] = [] + + # Freetype + # The font name can be a comma separated list + # of font names to search for. + self.font = freetype.SysFont(self.FONT_NAMES, 24) + self.font_small = freetype.SysFont(self.FONT_NAMES, 16) + self.text_color = text_color + + print("Using font: " + self.font.name) + + def update(self, events) -> None: + """ + Updates the text input widget + """ + for event in events: + if event.type == pg.KEYDOWN: + if self.print_event: + print(event) + + if self._ime_editing: + if len(self._ime_editing_text) == 0: + self._ime_editing = False + continue + + if event.key == pg.K_BACKSPACE: + if len(self._ime_text) > 0 and self._ime_text_pos > 0: + self._ime_text = ( + self._ime_text[0 : self._ime_text_pos - 1] + + self._ime_text[self._ime_text_pos :] + ) + self._ime_text_pos = max(0, self._ime_text_pos - 1) + + elif event.key == pg.K_DELETE: + self._ime_text = ( + self._ime_text[0 : self._ime_text_pos] + + self._ime_text[self._ime_text_pos + 1 :] + ) + elif event.key == pg.K_LEFT: + self._ime_text_pos = max(0, self._ime_text_pos - 1) + elif event.key == pg.K_RIGHT: + self._ime_text_pos = min( + len(self._ime_text), self._ime_text_pos + 1 + ) + # Handle ENTER key + elif event.key in [pg.K_RETURN, pg.K_KP_ENTER]: + # Block if we have no text to append + if len(self._ime_text) == 0: + continue + + # Append chat list + self.chat_list.append(self._ime_text) + if len(self.chat_list) > self.CHAT_LIST_MAXSIZE: + self.chat_list.pop(0) + self._ime_text = "" + self._ime_text_pos = 0 + + elif event.type == pg.TEXTEDITING: + if self.print_event: + print(event) + self._ime_editing = True + self._ime_editing_text = event.text + self._ime_editing_pos = event.start + + elif event.type == pg.TEXTINPUT: + if self.print_event: + print(event) + self._ime_editing = False + self._ime_editing_text = "" + self._ime_text = ( + self._ime_text[0 : self._ime_text_pos] + + event.text + + self._ime_text[self._ime_text_pos :] + ) + self._ime_text_pos += len(event.text) + + def draw(self, screen: pygame.Surface) -> None: + """ + Draws the text input widget onto the provided surface + """ + + # Chat List updates + chat_height = self.CHAT_LIST_POS.height / self.CHAT_LIST_MAXSIZE + for i, chat in enumerate(self.chat_list): + self.font_small.render_to( + screen, + (self.CHAT_LIST_POS.x, self.CHAT_LIST_POS.y + i * chat_height), + chat, + self.text_color, + ) + + # Chat box updates + start_pos = self.CHAT_BOX_POS.copy() + ime_text_l = self.prompt + self._ime_text[0 : self._ime_text_pos] + ime_text_m = ( + self._ime_editing_text[0 : self._ime_editing_pos] + + "|" + + self._ime_editing_text[self._ime_editing_pos :] + ) + ime_text_r = self._ime_text[self._ime_text_pos :] + + rect_text_l = self.font.render_to( + screen, start_pos, ime_text_l, self.text_color + ) + start_pos.x += rect_text_l.width + + # Editing texts should be underlined + rect_text_m = self.font.render_to( + screen, + start_pos, + ime_text_m, + self.text_color, + None, + freetype.STYLE_UNDERLINE, + ) + start_pos.x += rect_text_m.width + self.font.render_to(screen, start_pos, ime_text_r, self.text_color) + + +class Game: + """ + A class that handles the game's events, mainloop etc. + """ + + # CONSTANTS + # Frames per second, the general speed of the program + FPS = 50 + # Size of window + SCREEN_WIDTH, SCREEN_HEIGHT = 640, 480 + BG_COLOR = "black" + + def __init__(self, caption: str) -> None: + # Initialize + pg.init() + self.screen = pg.display.set_mode((self.SCREEN_WIDTH, self.SCREEN_HEIGHT)) + pg.display.set_caption(caption) + self.clock = pg.time.Clock() + + # Text input + # Set to true or add 'showevent' in argv to see IME and KEYDOWN events + self.print_event = "showevent" in sys.argv + self.text_input = TextInput( + prompt="> ", + pos=(0, 20), + screen_dimensions=(self.SCREEN_WIDTH, self.SCREEN_HEIGHT), + print_event=self.print_event, + text_color="green", + ) + + def main_loop(self) -> None: + pg.key.start_text_input() + input_rect = pg.Rect(80, 80, 320, 40) + pg.key.set_text_input_rect(input_rect) + + while True: + events = pg.event.get() + for event in events: + if event.type == pg.QUIT: + pg.quit() + return + + self.text_input.update(events) + + # Screen updates + self.screen.fill(self.BG_COLOR) + self.text_input.draw(self.screen) + + pg.display.update() + self.clock.tick(self.FPS) + + +# Main loop process +def main(): + game = Game("Text Input Example") + game.main_loop() + + +if __name__ == "__main__": + main() diff --git a/.venv/Lib/site-packages/pygame/examples/vgrade.py b/.venv/Lib/site-packages/pygame/examples/vgrade.py new file mode 100644 index 00000000..06202c24 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/vgrade.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +""" pg.examples.vgrade + +This example demonstrates creating an image with numpy +python, and displaying that through SDL. You can look at the +method of importing numpy and pg.surfarray. This method +will fail 'gracefully' if it is not available. +I've tried mixing in a lot of comments where the code might +not be self explanatory, nonetheless it may still seem a bit +strange. Learning to use numpy for images like this takes a +bit of learning, but the payoff is extremely fast image +manipulation in python. + +For Pygame 1.9.2 and up, this example also showcases a new feature +of surfarray.blit_surface: array broadcasting. If a source array +has either a width or height of 1, the array is repeatedly blitted +to the surface along that dimension to fill the surface. In fact, +a (1, 1) or (1, 1, 3) array results in a simple surface color fill. + +Just so you know how this breaks down. For each sampling of +time, 30% goes to each creating the gradient and blitting the +array. The final 40% goes to flipping/updating the display surface + +The window will have no border decorations. + +The code also demonstrates use of the timer events. +""" + + +import os +import pygame as pg + +try: + import numpy as np + import numpy.random as np_random +except ImportError: + raise SystemExit("This example requires numpy and the pygame surfarray module") + +timer = 0 + + +def stopwatch(message=None): + "simple routine to time python code" + global timer + if not message: + timer = pg.time.get_ticks() + return + now = pg.time.get_ticks() + runtime = (now - timer) / 1000.0 + 0.001 + print(f"{message} {runtime} seconds\t{(1.0 / runtime):.2f}fps") + timer = now + + +def VertGradientColumn(surf, topcolor, bottomcolor): + "creates a new 3d vertical gradient array" + topcolor = np.array(topcolor, copy=False) + bottomcolor = np.array(bottomcolor, copy=False) + diff = bottomcolor - topcolor + width, height = surf.get_size() + # create array from 0.0 to 1.0 triplets + column = np.arange(height, dtype="float") / height + column = np.repeat(column[:, np.newaxis], [3], 1) + # create a single column of gradient + column = topcolor + (diff * column).astype("int") + # make the column a 3d image column by adding X + column = column.astype("uint8")[np.newaxis, :, :] + # 3d array into 2d array + return pg.surfarray.map_array(surf, column) + + +def DisplayGradient(surf): + "choose random colors and show them" + stopwatch() + colors = np_random.randint(0, 255, (2, 3)) + column = VertGradientColumn(surf, colors[0], colors[1]) + pg.surfarray.blit_array(surf, column) + pg.display.flip() + stopwatch("Gradient:") + + +def main(): + pg.init() + pg.mixer.quit() # remove ALSA underflow messages for Debian squeeze + size = 600, 400 + os.environ["SDL_VIDEO_CENTERED"] = "1" + screen = pg.display.set_mode(size, pg.NOFRAME, 0) + + pg.event.set_blocked(pg.MOUSEMOTION) # keep our queue cleaner + pg.time.set_timer(pg.USEREVENT, 500) + + while True: + event = pg.event.wait() + if event.type in (pg.QUIT, pg.KEYDOWN, pg.MOUSEBUTTONDOWN): + break + elif event.type == pg.USEREVENT: + DisplayGradient(screen) + + pg.quit() + + +if __name__ == "__main__": + main() diff --git a/.venv/Lib/site-packages/pygame/examples/video.py b/.venv/Lib/site-packages/pygame/examples/video.py new file mode 100644 index 00000000..7104f473 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/examples/video.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python +""" pg.examples.video + +Experimental! + +* dialog message boxes with messagebox. +* multiple windows with Window +* driver selection +* Renderer, Texture, and Image classes +* Drawing lines, rects, and such onto Renderers. +""" +import os +import pygame as pg +from pygame._sdl2 import Window, Texture, Image, Renderer, get_drivers, messagebox + +data_dir = os.path.join(os.path.split(os.path.abspath(__file__))[0], "data") + + +def load_img(file): + return pg.image.load(os.path.join(data_dir, file)) + + +pg.display.init() +pg.key.set_repeat(1000, 10) + +for driver in get_drivers(): + print(driver) + +import random + +try: + answer = messagebox( + "I will open two windows! Continue?", + "Hello!", + info=True, + buttons=("Yes", "No", "Chance"), + return_button=0, + escape_button=1, + ) + if answer == 1 or (answer == 2 and random.random() < 0.5): + import sys + + sys.exit(0) +except: + pass + +win = Window("asdf", resizable=True) +renderer = Renderer(win) +tex = Texture.from_surface(renderer, load_img("alien1.gif")) +img = Image(tex) + +running = True + +x, y = 250, 50 +clock = pg.time.Clock() + +backgrounds = [(255, 0, 0, 255), (0, 255, 0, 255), (0, 0, 255, 255)] +bg_index = 0 + +renderer.draw_color = backgrounds[bg_index] + +win2 = Window("2nd window", size=(256, 256), always_on_top=True) +win2.opacity = 0.5 +win2.set_icon(load_img("bomb.gif")) +renderer2 = Renderer(win2) +tex2 = Texture.from_surface(renderer2, load_img("asprite.bmp")) +renderer2.clear() +tex2.draw() +renderer2.present() +del tex2 + +full = 0 + +surf = pg.Surface((64, 64)) +streamtex = Texture(renderer, (64, 64), streaming=True) +tex_update_interval = 1000 +next_tex_update = pg.time.get_ticks() + + +while running: + for event in pg.event.get(): + if event.type == pg.QUIT: + running = False + elif getattr(event, "window", None) == win2: + if ( + event.type == pg.KEYDOWN + and event.key == pg.K_ESCAPE + or event.type == pg.WINDOWCLOSE + ): + win2.destroy() + elif event.type == pg.KEYDOWN: + if event.key == pg.K_ESCAPE: + running = False + elif event.key == pg.K_LEFT: + x -= 5 + elif event.key == pg.K_RIGHT: + x += 5 + elif event.key == pg.K_DOWN: + y += 5 + elif event.key == pg.K_UP: + y -= 5 + elif event.key == pg.K_f: + if full == 0: + win.set_fullscreen(True) + full = 1 + else: + win.set_windowed() + full = 0 + elif event.key == pg.K_s: + readsurf = renderer.to_surface() + pg.image.save(readsurf, "test.png") + + elif event.key == pg.K_SPACE: + bg_index = (bg_index + 1) % len(backgrounds) + renderer.draw_color = backgrounds[bg_index] + + renderer.clear() + + # update texture + curtime = pg.time.get_ticks() + if curtime >= next_tex_update: + for x_ in range(streamtex.width // 4): + for y_ in range(streamtex.height // 4): + newcol = ( + random.randint(0, 255), + random.randint(0, 255), + random.randint(0, 255), + 255, + ) + area = (4 * x_, 4 * y_, 4, 4) + surf.fill(newcol, area) + streamtex.update(surf) + next_tex_update = curtime + tex_update_interval + streamtex.draw(dstrect=pg.Rect(64, 128, 64, 64)) + + img.draw(dstrect=(x, y)) + + # TODO: should these be? + # - line instead of draw_line + # - point instead of draw_point + # - rect(rect, width=1)->draw 1 pixel, instead of draw_rect + # - rect(rect, width=0)->filled ? , instead of fill_rect + # + # TODO: should these work with pg.draw.line(renderer, ...) functions? + renderer.draw_color = (255, 255, 255, 255) + renderer.draw_line((0, 0), (64, 64)) + renderer.draw_line((64, 64), (128, 0)) + renderer.draw_point((72, 32)) + renderer.draw_rect(pg.Rect(0, 64, 64, 64)) + renderer.fill_rect(pg.Rect(0, 128, 64, 64)) + renderer.draw_color = backgrounds[bg_index] + + renderer.present() + + clock.tick(60) + win.title = str(f"FPS: {clock.get_fps()}") + +pg.quit() diff --git a/.venv/Lib/site-packages/pygame/fastevent.py b/.venv/Lib/site-packages/pygame/fastevent.py new file mode 100644 index 00000000..e102fc48 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/fastevent.py @@ -0,0 +1,88 @@ +""" +A compatibility shim for pygame.fastevent based on pygame.event. +This module was deprecated in pygame 2.2, and is scheduled for removal in a +future pygame version. If you are using pygame.fastevent, please migrate to +using regular pygame.event module +""" + +import pygame.event +import pygame.display +from pygame import error, register_quit +from pygame.event import Event + +_ft_init = False + + +def _ft_init_check(): + """ + Raises error if module is not init + """ + if not _ft_init: + raise error("fastevent system not initialized") + + +def _quit_hook(): + """ + Hook that gets run to quit module + """ + global _ft_init + _ft_init = False + + +def init(): + """init() -> None + initialize pygame.fastevent + """ + global _ft_init + if not pygame.display.get_init(): + raise error("video system not initialized") + + register_quit(_quit_hook) + _ft_init = True + + +def get_init(): + """get_init() -> bool + returns True if the fastevent module is currently initialized + """ + return _ft_init + + +def pump(): + """pump() -> None + internally process pygame event handlers + """ + _ft_init_check() + pygame.event.pump() + + +def wait(): + """wait() -> Event + wait for an event + """ + _ft_init_check() + return pygame.event.wait() + + +def poll(): + """poll() -> Event + get an available event + """ + _ft_init_check() + return pygame.event.poll() + + +def get(): + """get() -> list of Events + get all events from the queue + """ + _ft_init_check() + return pygame.event.get() + + +def post(event: Event): + """post(Event) -> None + place an event on the queue + """ + _ft_init_check() + pygame.event.post(event) diff --git a/.venv/Lib/site-packages/pygame/fastevent.pyi b/.venv/Lib/site-packages/pygame/fastevent.pyi new file mode 100644 index 00000000..9cfa701e --- /dev/null +++ b/.venv/Lib/site-packages/pygame/fastevent.pyi @@ -0,0 +1,11 @@ +from typing import List + +from pygame.event import Event + +def init() -> None: ... +def get_init() -> bool: ... +def pump() -> None: ... +def wait() -> Event: ... +def poll() -> Event: ... +def get() -> List[Event]: ... +def post(event: Event) -> None: ... diff --git a/.venv/Lib/site-packages/pygame/font.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/font.cp311-win_amd64.pyd new file mode 100644 index 00000000..46a7406d Binary files /dev/null and b/.venv/Lib/site-packages/pygame/font.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/font.pyi b/.venv/Lib/site-packages/pygame/font.pyi new file mode 100644 index 00000000..b1618aa3 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/font.pyi @@ -0,0 +1,61 @@ +from typing import Callable, Hashable, Iterable, List, Optional, Tuple, Union + +from pygame.surface import Surface + +from ._common import ColorValue, FileArg, Literal + +# TODO: Figure out a way to type this attribute such that mypy knows it's not +# always defined at runtime +UCS4: Literal[1] + +def init() -> None: ... +def quit() -> None: ... +def get_init() -> bool: ... +def get_sdl_ttf_version(linked: bool = True) -> Tuple[int, int, int]: ... +def get_default_font() -> str: ... +def get_fonts() -> List[str]: ... +def match_font( + name: Union[str, bytes, Iterable[Union[str, bytes]]], + bold: Hashable = False, + italic: Hashable = False, +) -> str: ... +def SysFont( + name: Union[str, bytes, Iterable[Union[str, bytes]]], + size: int, + bold: Hashable = False, + italic: Hashable = False, + constructor: Optional[Callable[[Optional[str], int, bool, bool], Font]] = None, +) -> Font: ... + +class Font: + bold: bool + italic: bool + underline: bool + strikethrough: bool + def __init__(self, name: Optional[FileArg], size: int) -> None: ... + def render( + self, + text: Union[str, bytes, None], + antialias: bool | Literal[0] | Literal[1], + color: ColorValue, + background: Optional[ColorValue] = None, + ) -> Surface: ... + def size(self, text: Union[str, bytes]) -> Tuple[int, int]: ... + def set_underline(self, value: bool) -> None: ... + def get_underline(self) -> bool: ... + def set_strikethrough(self, value: bool) -> None: ... + def get_strikethrough(self) -> bool: ... + def set_bold(self, value: bool) -> None: ... + def get_bold(self) -> bool: ... + def set_italic(self, value: bool | Literal[0] | Literal[1]) -> None: ... + def metrics( + self, text: Union[str, bytes] + ) -> List[Tuple[int, int, int, int, int]]: ... + def get_italic(self) -> bool: ... + def get_linesize(self) -> int: ... + def get_height(self) -> int: ... + def get_ascent(self) -> int: ... + def get_descent(self) -> int: ... + def set_script(self, script_code: str) -> None: ... + +FontType = Font diff --git a/.venv/Lib/site-packages/pygame/freesansbold.ttf b/.venv/Lib/site-packages/pygame/freesansbold.ttf new file mode 100644 index 00000000..a98562fc Binary files /dev/null and b/.venv/Lib/site-packages/pygame/freesansbold.ttf differ diff --git a/.venv/Lib/site-packages/pygame/freetype.dll b/.venv/Lib/site-packages/pygame/freetype.dll new file mode 100644 index 00000000..67b61d1d Binary files /dev/null and b/.venv/Lib/site-packages/pygame/freetype.dll differ diff --git a/.venv/Lib/site-packages/pygame/freetype.py b/.venv/Lib/site-packages/pygame/freetype.py new file mode 100644 index 00000000..50a63cf1 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/freetype.py @@ -0,0 +1,78 @@ +"""Enhanced Pygame module for loading and rendering computer fonts""" + +from pygame._freetype import ( + Font, + STYLE_NORMAL, + STYLE_OBLIQUE, + STYLE_STRONG, + STYLE_UNDERLINE, + STYLE_WIDE, + STYLE_DEFAULT, + init, + quit, + get_init, + was_init, + get_cache_size, + get_default_font, + get_default_resolution, + get_error, + get_version, + set_default_resolution, +) +from pygame.sysfont import match_font, get_fonts, SysFont as _SysFont + +__all__ = [ + "Font", + "STYLE_NORMAL", + "STYLE_OBLIQUE", + "STYLE_STRONG", + "STYLE_UNDERLINE", + "STYLE_WIDE", + "STYLE_DEFAULT", + "init", + "quit", + "get_init", + "was_init", + "get_cache_size", + "get_default_font", + "get_default_resolution", + "get_error", + "get_version", + "set_default_resolution", + "match_font", + "get_fonts", +] + + +def SysFont(name, size, bold=False, italic=False, constructor=None): + """pygame.ftfont.SysFont(name, size, bold=False, italic=False, constructor=None) -> Font + Create a pygame Font from system font resources. + + This will search the system fonts for the given font + name. You can also enable bold or italic styles, and + the appropriate system font will be selected if available. + + This will always return a valid Font object, and will + fallback on the builtin pygame font if the given font + is not found. + + Name can also be an iterable of font names, a string of + comma-separated font names, or a bytes of comma-separated + font names, in which case the set of names will be searched + in order. Pygame uses a small set of common font aliases. If the + specific font you ask for is not available, a reasonable + alternative may be used. + + If optional constructor is provided, it must be a function with + signature constructor(fontpath, size, bold, italic) which returns + a Font instance. If None, a pygame.freetype.Font object is created. + """ + if constructor is None: + + def constructor(fontpath, size, bold, italic): + font = Font(fontpath, size) + font.strong = bold + font.oblique = italic + return font + + return _SysFont(name, size, bold, italic, constructor) diff --git a/.venv/Lib/site-packages/pygame/freetype.pyi b/.venv/Lib/site-packages/pygame/freetype.pyi new file mode 100644 index 00000000..3ab6cbed --- /dev/null +++ b/.venv/Lib/site-packages/pygame/freetype.pyi @@ -0,0 +1,128 @@ +from typing import Any, Callable, Iterable, List, Optional, Tuple, Union + +from pygame.color import Color +from pygame.rect import Rect +from pygame.surface import Surface + +from ._common import ColorValue, FileArg, RectValue + +def get_error() -> str: ... +def get_version(linked: bool = True) -> Tuple[int, int, int]: ... +def init(cache_size: int = 64, resolution: int = 72) -> None: ... +def quit() -> None: ... +def get_init() -> bool: ... +def was_init() -> bool: ... +def get_cache_size() -> int: ... +def get_default_resolution() -> int: ... +def set_default_resolution(resolution: int) -> None: ... +def SysFont( + name: Union[str, bytes, Iterable[Union[str, bytes]]], + size: int, + bold: int = False, + italic: int = False, + constructor: Optional[Callable[[Optional[str], int, bool, bool], Font]] = None, +) -> Font: ... +def get_default_font() -> str: ... +def get_fonts() -> List[str]: ... +def match_font( + name: Union[str, bytes, Iterable[Union[str, bytes]]], + bold: Any = False, + italic: Any = False, +) -> str: ... + +STYLE_NORMAL: int +STYLE_UNDERLINE: int +STYLE_OBLIQUE: int +STYLE_STRONG: int +STYLE_WIDE: int +STYLE_DEFAULT: int + +class Font: + name: str + path: str + size: Union[float, Tuple[float, float]] + height: int + ascender: int + descender: int + style: int + underline: bool + strong: bool + oblique: bool + wide: bool + strength: float + underline_adjustment: float + fixed_width: bool + fixed_sizes: int + scalable: bool + use_bitmap_strikes: bool + antialiased: bool + kerning: bool + vertical: bool + rotation: int + fgcolor: Color + bgcolor: Color + origin: bool + pad: bool + ucs4: bool + resolution: int + def __init__( + self, + file: Optional[FileArg], + size: float = 0, + font_index: int = 0, + resolution: int = 0, + ucs4: int = False, + ) -> None: ... + def get_rect( + self, + text: str, + style: int = STYLE_DEFAULT, + rotation: int = 0, + size: float = 0, + ) -> Rect: ... + def get_metrics( + self, text: str, size: float = 0 + ) -> List[Tuple[int, int, int, int, float, float]]: ... + def get_sized_ascender(self, size: float) -> int: ... + def get_sized_descender(self, size: float) -> int: ... + def get_sized_height(self, size: float) -> int: ... + def get_sized_glyph_height(self, size: float) -> int: ... + def get_sizes(self) -> List[Tuple[int, int, int, float, float]]: ... + def render( + self, + text: str, + fgcolor: Optional[ColorValue] = None, + bgcolor: Optional[ColorValue] = None, + style: int = STYLE_DEFAULT, + rotation: int = 0, + size: float = 0, + ) -> Tuple[Surface, Rect]: ... + def render_to( + self, + surf: Surface, + dest: RectValue, + text: str, + fgcolor: Optional[ColorValue] = None, + bgcolor: Optional[ColorValue] = None, + style: int = STYLE_DEFAULT, + rotation: int = 0, + size: float = 0, + ) -> Rect: ... + def render_raw( + self, + text: str, + style: int = STYLE_DEFAULT, + rotation: int = 0, + size: float = 0, + invert: bool = False, + ) -> Tuple[bytes, Tuple[int, int]]: ... + def render_raw_to( + self, + array: Any, + text: str, + dest: Optional[RectValue] = None, + style: int = STYLE_DEFAULT, + rotation: int = 0, + size: float = 0, + invert: bool = False, + ) -> Rect: ... diff --git a/.venv/Lib/site-packages/pygame/ftfont.py b/.venv/Lib/site-packages/pygame/ftfont.py new file mode 100644 index 00000000..e060291a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/ftfont.py @@ -0,0 +1,203 @@ +"""pygame module for loading and rendering fonts (freetype alternative)""" + +__all__ = [ + "Font", + "init", + "quit", + "get_default_font", + "get_init", + "SysFont", + "match_font", + "get_fonts", +] + +from pygame._freetype import init, Font as _Font, get_default_resolution +from pygame._freetype import quit, get_default_font, get_init as _get_init +from pygame._freetype import _internal_mod_init +from pygame.sysfont import match_font, get_fonts, SysFont as _SysFont +from pygame import encode_file_path + + +class Font(_Font): + """Font(filename, size) -> Font + Font(object, size) -> Font + create a new Font object from a file (freetype alternative) + + This Font type differs from font.Font in that it can render glyphs + for Unicode code points in the supplementary planes (> 0xFFFF). + """ + + __encode_file_path = staticmethod(encode_file_path) + __get_default_resolution = staticmethod(get_default_resolution) + __default_font = encode_file_path(get_default_font()) + + __unull = "\x00" + __bnull = b"\x00" + + def __init__(self, file=None, size=-1): + size = max(size, 1) + if isinstance(file, str): + try: + bfile = self.__encode_file_path(file, ValueError) + except ValueError: + bfile = "" + else: + bfile = file + if isinstance(bfile, bytes) and bfile == self.__default_font: + file = None + if file is None: + resolution = int(self.__get_default_resolution() * 0.6875) + if resolution == 0: + resolution = 1 + else: + resolution = 0 + super().__init__(file, size=size, resolution=resolution) + self.strength = 1.0 / 12.0 + self.kerning = False + self.origin = True + self.pad = True + self.ucs4 = True + self.underline_adjustment = 1.0 + + def render(self, text, antialias, color, background=None): + """render(text, antialias, color, background=None) -> Surface + draw text on a new Surface""" + + if text is None: + text = "" + if isinstance(text, str) and self.__unull in text: + raise ValueError("A null character was found in the text") + if isinstance(text, bytes) and self.__bnull in text: + raise ValueError("A null character was found in the text") + save_antialiased = ( + self.antialiased # pylint: disable = access-member-before-definition + ) + self.antialiased = bool(antialias) + try: + s, _ = super().render(text, color, background) + return s + finally: + self.antialiased = save_antialiased + + def set_bold(self, value): + """set_bold(bool) -> None + enable fake rendering of bold text""" + + self.wide = bool(value) + + def get_bold(self): + """get_bold() -> bool + check if text will be rendered bold""" + + return self.wide + + bold = property(get_bold, set_bold) + + def set_italic(self, value): + """set_italic(bool) -> None + enable fake rendering of italic text""" + + self.oblique = bool(value) + + def get_italic(self): + """get_italic() -> bool + check if the text will be rendered italic""" + + return self.oblique + + italic = property(get_italic, set_italic) + + def set_underline(self, value): + """set_underline(bool) -> None + control if text is rendered with an underline""" + + self.underline = bool(value) + + def get_underline(self): + """set_bold(bool) -> None + enable fake rendering of bold text""" + + return self.underline + + def metrics(self, text): + """metrics(text) -> list + Gets the metrics for each character in the passed string.""" + + return self.get_metrics(text) + + def get_ascent(self): + """get_ascent() -> int + get the ascent of the font""" + + return self.get_sized_ascender() + + def get_descent(self): + """get_descent() -> int + get the descent of the font""" + + return self.get_sized_descender() + + def get_height(self): + """get_height() -> int + get the height of the font""" + + return self.get_sized_ascender() - self.get_sized_descender() + 1 + + def get_linesize(self): + """get_linesize() -> int + get the line space of the font text""" + + return self.get_sized_height() + + def size(self, text): + """size(text) -> (width, height) + determine the amount of space needed to render text""" + + return self.get_rect(text).size + + +FontType = Font + + +def get_init(): + """get_init() -> bool + true if the font module is initialized""" + + return _get_init() + + +def SysFont(name, size, bold=0, italic=0, constructor=None): + """pygame.ftfont.SysFont(name, size, bold=False, italic=False, constructor=None) -> Font + Create a pygame Font from system font resources. + + This will search the system fonts for the given font + name. You can also enable bold or italic styles, and + the appropriate system font will be selected if available. + + This will always return a valid Font object, and will + fallback on the builtin pygame font if the given font + is not found. + + Name can also be an iterable of font names, a string of + comma-separated font names, or a bytes of comma-separated + font names, in which case the set of names will be searched + in order. Pygame uses a small set of common font aliases. If the + specific font you ask for is not available, a reasonable + alternative may be used. + + If optional constructor is provided, it must be a function with + signature constructor(fontpath, size, bold, italic) which returns + a Font instance. If None, a pygame.ftfont.Font object is created. + """ + if constructor is None: + + def constructor(fontpath, size, bold, italic): + font = Font(fontpath, size) + font.set_bold(bold) + font.set_italic(italic) + return font + + return _SysFont(name, size, bold, italic, constructor) + + +del _Font, get_default_resolution, encode_file_path diff --git a/.venv/Lib/site-packages/pygame/gfxdraw.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/gfxdraw.cp311-win_amd64.pyd new file mode 100644 index 00000000..b26ec94d Binary files /dev/null and b/.venv/Lib/site-packages/pygame/gfxdraw.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/gfxdraw.pyi b/.venv/Lib/site-packages/pygame/gfxdraw.pyi new file mode 100644 index 00000000..dca1334e --- /dev/null +++ b/.venv/Lib/site-packages/pygame/gfxdraw.pyi @@ -0,0 +1,91 @@ +from typing import Sequence + +from pygame.surface import Surface + +from ._common import ColorValue, Coordinate, RectValue + +def pixel(surface: Surface, x: int, y: int, color: ColorValue) -> None: ... +def hline(surface: Surface, x1: int, x2: int, y: int, color: ColorValue) -> None: ... +def vline(surface: Surface, x: int, y1: int, y2: int, color: ColorValue) -> None: ... +def line( + surface: Surface, x1: int, y1: int, x2: int, y2: int, color: ColorValue +) -> None: ... +def rectangle(surface: Surface, rect: RectValue, color: ColorValue) -> None: ... +def box(surface: Surface, rect: RectValue, color: ColorValue) -> None: ... +def circle(surface: Surface, x: int, y: int, r: int, color: ColorValue) -> None: ... +def aacircle(surface: Surface, x: int, y: int, r: int, color: ColorValue) -> None: ... +def filled_circle( + surface: Surface, x: int, y: int, r: int, color: ColorValue +) -> None: ... +def ellipse( + surface: Surface, x: int, y: int, rx: int, ry: int, color: ColorValue +) -> None: ... +def aaellipse( + surface: Surface, x: int, y: int, rx: int, ry: int, color: ColorValue +) -> None: ... +def filled_ellipse( + surface: Surface, x: int, y: int, rx: int, ry: int, color: ColorValue +) -> None: ... +def arc( + surface: Surface, + x: int, + y: int, + r: int, + start_angle: int, + atp_angle: int, + color: ColorValue, +) -> None: ... +def pie( + surface: Surface, + x: int, + y: int, + r: int, + start_angle: int, + atp_angle: int, + color: ColorValue, +) -> None: ... +def trigon( + surface: Surface, + x1: int, + y1: int, + x2: int, + y2: int, + x3: int, + y3: int, + color: ColorValue, +) -> None: ... +def aatrigon( + surface: Surface, + x1: int, + y1: int, + x2: int, + y2: int, + x3: int, + y3: int, + color: ColorValue, +) -> None: ... +def filled_trigon( + surface: Surface, + x1: int, + y1: int, + x2: int, + y2: int, + x3: int, + y3: int, + color: ColorValue, +) -> None: ... +def polygon( + surface: Surface, points: Sequence[Coordinate], color: ColorValue +) -> None: ... +def aapolygon( + surface: Surface, points: Sequence[Coordinate], color: ColorValue +) -> None: ... +def filled_polygon( + surface: Surface, points: Sequence[Coordinate], color: ColorValue +) -> None: ... +def textured_polygon( + surface: Surface, points: Sequence[Coordinate], texture: Surface, tx: int, ty: int +) -> None: ... +def bezier( + surface: Surface, points: Sequence[Coordinate], steps: int, color: ColorValue +) -> None: ... diff --git a/.venv/Lib/site-packages/pygame/image.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/image.cp311-win_amd64.pyd new file mode 100644 index 00000000..f22e8ab1 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/image.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/image.pyi b/.venv/Lib/site-packages/pygame/image.pyi new file mode 100644 index 00000000..69e90cd6 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/image.pyi @@ -0,0 +1,45 @@ +from typing import Optional, Sequence, Tuple, Union + +from pygame.bufferproxy import BufferProxy +from pygame.surface import Surface + +from ._common import FileArg, Literal + +_BufferStyle = Union[BufferProxy, bytes, bytearray, memoryview] +_to_string_format = Literal[ + "P", "RGB", "RGBX", "RGBA", "ARGB", "BGRA", "RGBA_PREMULT", "ARGB_PREMULT" +] +_from_buffer_format = Literal["P", "RGB", "BGR", "BGRA", "RGBX", "RGBA", "ARGB"] +_from_string_format = Literal["P", "RGB", "RGBX", "RGBA", "ARGB", "BGRA"] + +def load(filename: FileArg, namehint: str = "") -> Surface: ... +def save(surface: Surface, filename: FileArg, namehint: str = "") -> None: ... +def get_sdl_image_version(linked: bool = True) -> Optional[Tuple[int, int, int]]: ... +def get_extended() -> bool: ... +def tostring( + surface: Surface, format: _to_string_format, flipped: bool = False +) -> bytes: ... +def fromstring( + bytes: bytes, + size: Union[Sequence[int], Tuple[int, int]], + format: _from_string_format, + flipped: bool = False, +) -> Surface: ... +# the use of tobytes/frombytes is preferred over tostring/fromstring +def tobytes( + surface: Surface, format: _to_string_format, flipped: bool = False +) -> bytes: ... +def frombytes( + bytes: bytes, + size: Union[Sequence[int], Tuple[int, int]], + format: _from_string_format, + flipped: bool = False, +) -> Surface: ... +def frombuffer( + bytes: _BufferStyle, + size: Union[Sequence[int], Tuple[int, int]], + format: _from_buffer_format, +) -> Surface: ... +def load_basic(filename: FileArg) -> Surface: ... +def load_extended(filename: FileArg, namehint: str = "") -> Surface: ... +def save_extended(surface: Surface, filename: FileArg, namehint: str = "") -> None: ... diff --git a/.venv/Lib/site-packages/pygame/imageext.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/imageext.cp311-win_amd64.pyd new file mode 100644 index 00000000..52041034 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/imageext.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/joystick.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/joystick.cp311-win_amd64.pyd new file mode 100644 index 00000000..5d0f2860 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/joystick.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/joystick.pyi b/.venv/Lib/site-packages/pygame/joystick.pyi new file mode 100644 index 00000000..760a39d5 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/joystick.pyi @@ -0,0 +1,35 @@ +from typing import Tuple, final + +def init() -> None: ... +def quit() -> None: ... +def get_init() -> bool: ... +def get_count() -> int: ... +@final +class JoystickType: + def __init__(self, id: int) -> None: ... + def init(self) -> None: ... + def quit(self) -> None: ... + def get_init(self) -> bool: ... + def get_id(self) -> int: ... + def get_instance_id(self) -> int: ... + def get_guid(self) -> str: ... + def get_power_level(self) -> str: ... + def get_name(self) -> str: ... + def get_numaxes(self) -> int: ... + def get_axis(self, axis_number: int) -> float: ... + def get_numballs(self) -> int: ... + def get_ball(self, ball_number: int) -> Tuple[float, float]: ... + def get_numbuttons(self) -> int: ... + def get_button(self, button: int) -> bool: ... + def get_numhats(self) -> int: ... + def get_hat(self, hat_number: int) -> Tuple[float, float]: ... + def rumble( + self, low_frequency: float, high_frequency: float, duration: int + ) -> bool: ... + def stop_rumble(self) -> None: ... + +# according to the current implementation, Joystick is a function that returns +# a JoystickType instance. In the future, when the C implementation is fixed to +# add __init__/__new__ to Joystick and it's exported directly, the typestubs +# here must be updated too +def Joystick(id: int) -> JoystickType: ... diff --git a/.venv/Lib/site-packages/pygame/key.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/key.cp311-win_amd64.pyd new file mode 100644 index 00000000..aecd9475 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/key.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/key.pyi b/.venv/Lib/site-packages/pygame/key.pyi new file mode 100644 index 00000000..fa75a164 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/key.pyi @@ -0,0 +1,17 @@ +from typing import Tuple + +from ._common import RectValue + +class ScancodeWrapper(Tuple[bool, ...]): ... + +def get_focused() -> bool: ... +def get_pressed() -> ScancodeWrapper: ... +def get_mods() -> int: ... +def set_mods(mods: int) -> None: ... +def set_repeat(delay: int = 0, interval: int = 0) -> None: ... +def get_repeat() -> Tuple[int, int]: ... +def name(key: int, use_compat: bool = True) -> str: ... +def key_code(name: str) -> int: ... +def start_text_input() -> None: ... +def stop_text_input() -> None: ... +def set_text_input_rect(rect: RectValue) -> None: ... diff --git a/.venv/Lib/site-packages/pygame/libjpeg-9.dll b/.venv/Lib/site-packages/pygame/libjpeg-9.dll new file mode 100644 index 00000000..9a05528e Binary files /dev/null and b/.venv/Lib/site-packages/pygame/libjpeg-9.dll differ diff --git a/.venv/Lib/site-packages/pygame/libmodplug-1.dll b/.venv/Lib/site-packages/pygame/libmodplug-1.dll new file mode 100644 index 00000000..b2e841ba Binary files /dev/null and b/.venv/Lib/site-packages/pygame/libmodplug-1.dll differ diff --git a/.venv/Lib/site-packages/pygame/libogg-0.dll b/.venv/Lib/site-packages/pygame/libogg-0.dll new file mode 100644 index 00000000..72733eb6 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/libogg-0.dll differ diff --git a/.venv/Lib/site-packages/pygame/libopus-0.dll b/.venv/Lib/site-packages/pygame/libopus-0.dll new file mode 100644 index 00000000..92e8ff7c Binary files /dev/null and b/.venv/Lib/site-packages/pygame/libopus-0.dll differ diff --git a/.venv/Lib/site-packages/pygame/libopusfile-0.dll b/.venv/Lib/site-packages/pygame/libopusfile-0.dll new file mode 100644 index 00000000..8e1b6338 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/libopusfile-0.dll differ diff --git a/.venv/Lib/site-packages/pygame/libpng16-16.dll b/.venv/Lib/site-packages/pygame/libpng16-16.dll new file mode 100644 index 00000000..709f7244 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/libpng16-16.dll differ diff --git a/.venv/Lib/site-packages/pygame/libtiff-5.dll b/.venv/Lib/site-packages/pygame/libtiff-5.dll new file mode 100644 index 00000000..fc8a7c0c Binary files /dev/null and b/.venv/Lib/site-packages/pygame/libtiff-5.dll differ diff --git a/.venv/Lib/site-packages/pygame/libwebp-7.dll b/.venv/Lib/site-packages/pygame/libwebp-7.dll new file mode 100644 index 00000000..fad57b22 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/libwebp-7.dll differ diff --git a/.venv/Lib/site-packages/pygame/locals.py b/.venv/Lib/site-packages/pygame/locals.py new file mode 100644 index 00000000..38801d5f --- /dev/null +++ b/.venv/Lib/site-packages/pygame/locals.py @@ -0,0 +1,30 @@ +# pygame - Python Game Library +# Copyright (C) 2000-2003 Pete Shinners +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Pete Shinners +# pete@shinners.org + + +"""Set of functions from pygame that are handy to have in +the local namespace for your module""" + +import pygame +from pygame.constants import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] +from pygame.rect import Rect +from pygame.color import Color + +__all__ = pygame.constants.__all__ + ["Rect", "Color"] diff --git a/.venv/Lib/site-packages/pygame/locals.pyi b/.venv/Lib/site-packages/pygame/locals.pyi new file mode 100644 index 00000000..b6c2dd0e --- /dev/null +++ b/.venv/Lib/site-packages/pygame/locals.pyi @@ -0,0 +1,562 @@ +# buildconfig/stubs/gen_stubs.py +# A script to auto-generate locals.pyi, constants.pyi and __init__.pyi typestubs +# IMPORTANT NOTE: Do not edit this file by hand! + +ACTIVEEVENT: int +ANYFORMAT: int +APPACTIVE: int +APPINPUTFOCUS: int +APPMOUSEFOCUS: int +APP_DIDENTERBACKGROUND: int +APP_DIDENTERFOREGROUND: int +APP_LOWMEMORY: int +APP_TERMINATING: int +APP_WILLENTERBACKGROUND: int +APP_WILLENTERFOREGROUND: int +ASYNCBLIT: int +AUDIODEVICEADDED: int +AUDIODEVICEREMOVED: int +AUDIO_ALLOW_ANY_CHANGE: int +AUDIO_ALLOW_CHANNELS_CHANGE: int +AUDIO_ALLOW_FORMAT_CHANGE: int +AUDIO_ALLOW_FREQUENCY_CHANGE: int +AUDIO_S16: int +AUDIO_S16LSB: int +AUDIO_S16MSB: int +AUDIO_S16SYS: int +AUDIO_S8: int +AUDIO_U16: int +AUDIO_U16LSB: int +AUDIO_U16MSB: int +AUDIO_U16SYS: int +AUDIO_U8: int +BIG_ENDIAN: int +BLENDMODE_ADD: int +BLENDMODE_BLEND: int +BLENDMODE_MOD: int +BLENDMODE_NONE: int +BLEND_ADD: int +BLEND_ALPHA_SDL2: int +BLEND_MAX: int +BLEND_MIN: int +BLEND_MULT: int +BLEND_PREMULTIPLIED: int +BLEND_RGBA_ADD: int +BLEND_RGBA_MAX: int +BLEND_RGBA_MIN: int +BLEND_RGBA_MULT: int +BLEND_RGBA_SUB: int +BLEND_RGB_ADD: int +BLEND_RGB_MAX: int +BLEND_RGB_MIN: int +BLEND_RGB_MULT: int +BLEND_RGB_SUB: int +BLEND_SUB: int +BUTTON_LEFT: int +BUTTON_MIDDLE: int +BUTTON_RIGHT: int +BUTTON_WHEELDOWN: int +BUTTON_WHEELUP: int +BUTTON_X1: int +BUTTON_X2: int +CLIPBOARDUPDATE: int +CONTROLLERAXISMOTION: int +CONTROLLERBUTTONDOWN: int +CONTROLLERBUTTONUP: int +CONTROLLERDEVICEADDED: int +CONTROLLERDEVICEREMAPPED: int +CONTROLLERDEVICEREMOVED: int +CONTROLLERSENSORUPDATE: int +CONTROLLERTOUCHPADDOWN: int +CONTROLLERTOUCHPADMOTION: int +CONTROLLERTOUCHPADUP: int +CONTROLLER_AXIS_INVALID: int +CONTROLLER_AXIS_LEFTX: int +CONTROLLER_AXIS_LEFTY: int +CONTROLLER_AXIS_MAX: int +CONTROLLER_AXIS_RIGHTX: int +CONTROLLER_AXIS_RIGHTY: int +CONTROLLER_AXIS_TRIGGERLEFT: int +CONTROLLER_AXIS_TRIGGERRIGHT: int +CONTROLLER_BUTTON_A: int +CONTROLLER_BUTTON_B: int +CONTROLLER_BUTTON_BACK: int +CONTROLLER_BUTTON_DPAD_DOWN: int +CONTROLLER_BUTTON_DPAD_LEFT: int +CONTROLLER_BUTTON_DPAD_RIGHT: int +CONTROLLER_BUTTON_DPAD_UP: int +CONTROLLER_BUTTON_GUIDE: int +CONTROLLER_BUTTON_INVALID: int +CONTROLLER_BUTTON_LEFTSHOULDER: int +CONTROLLER_BUTTON_LEFTSTICK: int +CONTROLLER_BUTTON_MAX: int +CONTROLLER_BUTTON_RIGHTSHOULDER: int +CONTROLLER_BUTTON_RIGHTSTICK: int +CONTROLLER_BUTTON_START: int +CONTROLLER_BUTTON_X: int +CONTROLLER_BUTTON_Y: int +Color: type +DOUBLEBUF: int +DROPBEGIN: int +DROPCOMPLETE: int +DROPFILE: int +DROPTEXT: int +FINGERDOWN: int +FINGERMOTION: int +FINGERUP: int +FULLSCREEN: int +GL_ACCELERATED_VISUAL: int +GL_ACCUM_ALPHA_SIZE: int +GL_ACCUM_BLUE_SIZE: int +GL_ACCUM_GREEN_SIZE: int +GL_ACCUM_RED_SIZE: int +GL_ALPHA_SIZE: int +GL_BLUE_SIZE: int +GL_BUFFER_SIZE: int +GL_CONTEXT_DEBUG_FLAG: int +GL_CONTEXT_FLAGS: int +GL_CONTEXT_FORWARD_COMPATIBLE_FLAG: int +GL_CONTEXT_MAJOR_VERSION: int +GL_CONTEXT_MINOR_VERSION: int +GL_CONTEXT_PROFILE_COMPATIBILITY: int +GL_CONTEXT_PROFILE_CORE: int +GL_CONTEXT_PROFILE_ES: int +GL_CONTEXT_PROFILE_MASK: int +GL_CONTEXT_RELEASE_BEHAVIOR: int +GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH: int +GL_CONTEXT_RELEASE_BEHAVIOR_NONE: int +GL_CONTEXT_RESET_ISOLATION_FLAG: int +GL_CONTEXT_ROBUST_ACCESS_FLAG: int +GL_DEPTH_SIZE: int +GL_DOUBLEBUFFER: int +GL_FRAMEBUFFER_SRGB_CAPABLE: int +GL_GREEN_SIZE: int +GL_MULTISAMPLEBUFFERS: int +GL_MULTISAMPLESAMPLES: int +GL_RED_SIZE: int +GL_SHARE_WITH_CURRENT_CONTEXT: int +GL_STENCIL_SIZE: int +GL_STEREO: int +GL_SWAP_CONTROL: int +HAT_CENTERED: int +HAT_DOWN: int +HAT_LEFT: int +HAT_LEFTDOWN: int +HAT_LEFTUP: int +HAT_RIGHT: int +HAT_RIGHTDOWN: int +HAT_RIGHTUP: int +HAT_UP: int +HIDDEN: int +HWACCEL: int +HWPALETTE: int +HWSURFACE: int +JOYAXISMOTION: int +JOYBALLMOTION: int +JOYBUTTONDOWN: int +JOYBUTTONUP: int +JOYDEVICEADDED: int +JOYDEVICEREMOVED: int +JOYHATMOTION: int +KEYDOWN: int +KEYMAPCHANGED: int +KEYUP: int +KMOD_ALT: int +KMOD_CAPS: int +KMOD_CTRL: int +KMOD_GUI: int +KMOD_LALT: int +KMOD_LCTRL: int +KMOD_LGUI: int +KMOD_LMETA: int +KMOD_LSHIFT: int +KMOD_META: int +KMOD_MODE: int +KMOD_NONE: int +KMOD_NUM: int +KMOD_RALT: int +KMOD_RCTRL: int +KMOD_RGUI: int +KMOD_RMETA: int +KMOD_RSHIFT: int +KMOD_SHIFT: int +KSCAN_0: int +KSCAN_1: int +KSCAN_2: int +KSCAN_3: int +KSCAN_4: int +KSCAN_5: int +KSCAN_6: int +KSCAN_7: int +KSCAN_8: int +KSCAN_9: int +KSCAN_A: int +KSCAN_AC_BACK: int +KSCAN_APOSTROPHE: int +KSCAN_B: int +KSCAN_BACKSLASH: int +KSCAN_BACKSPACE: int +KSCAN_BREAK: int +KSCAN_C: int +KSCAN_CAPSLOCK: int +KSCAN_CLEAR: int +KSCAN_COMMA: int +KSCAN_CURRENCYSUBUNIT: int +KSCAN_CURRENCYUNIT: int +KSCAN_D: int +KSCAN_DELETE: int +KSCAN_DOWN: int +KSCAN_E: int +KSCAN_END: int +KSCAN_EQUALS: int +KSCAN_ESCAPE: int +KSCAN_EURO: int +KSCAN_F: int +KSCAN_F1: int +KSCAN_F10: int +KSCAN_F11: int +KSCAN_F12: int +KSCAN_F13: int +KSCAN_F14: int +KSCAN_F15: int +KSCAN_F2: int +KSCAN_F3: int +KSCAN_F4: int +KSCAN_F5: int +KSCAN_F6: int +KSCAN_F7: int +KSCAN_F8: int +KSCAN_F9: int +KSCAN_G: int +KSCAN_GRAVE: int +KSCAN_H: int +KSCAN_HELP: int +KSCAN_HOME: int +KSCAN_I: int +KSCAN_INSERT: int +KSCAN_INTERNATIONAL1: int +KSCAN_INTERNATIONAL2: int +KSCAN_INTERNATIONAL3: int +KSCAN_INTERNATIONAL4: int +KSCAN_INTERNATIONAL5: int +KSCAN_INTERNATIONAL6: int +KSCAN_INTERNATIONAL7: int +KSCAN_INTERNATIONAL8: int +KSCAN_INTERNATIONAL9: int +KSCAN_J: int +KSCAN_K: int +KSCAN_KP0: int +KSCAN_KP1: int +KSCAN_KP2: int +KSCAN_KP3: int +KSCAN_KP4: int +KSCAN_KP5: int +KSCAN_KP6: int +KSCAN_KP7: int +KSCAN_KP8: int +KSCAN_KP9: int +KSCAN_KP_0: int +KSCAN_KP_1: int +KSCAN_KP_2: int +KSCAN_KP_3: int +KSCAN_KP_4: int +KSCAN_KP_5: int +KSCAN_KP_6: int +KSCAN_KP_7: int +KSCAN_KP_8: int +KSCAN_KP_9: int +KSCAN_KP_DIVIDE: int +KSCAN_KP_ENTER: int +KSCAN_KP_EQUALS: int +KSCAN_KP_MINUS: int +KSCAN_KP_MULTIPLY: int +KSCAN_KP_PERIOD: int +KSCAN_KP_PLUS: int +KSCAN_L: int +KSCAN_LALT: int +KSCAN_LANG1: int +KSCAN_LANG2: int +KSCAN_LANG3: int +KSCAN_LANG4: int +KSCAN_LANG5: int +KSCAN_LANG6: int +KSCAN_LANG7: int +KSCAN_LANG8: int +KSCAN_LANG9: int +KSCAN_LCTRL: int +KSCAN_LEFT: int +KSCAN_LEFTBRACKET: int +KSCAN_LGUI: int +KSCAN_LMETA: int +KSCAN_LSHIFT: int +KSCAN_LSUPER: int +KSCAN_M: int +KSCAN_MENU: int +KSCAN_MINUS: int +KSCAN_MODE: int +KSCAN_N: int +KSCAN_NONUSBACKSLASH: int +KSCAN_NONUSHASH: int +KSCAN_NUMLOCK: int +KSCAN_NUMLOCKCLEAR: int +KSCAN_O: int +KSCAN_P: int +KSCAN_PAGEDOWN: int +KSCAN_PAGEUP: int +KSCAN_PAUSE: int +KSCAN_PERIOD: int +KSCAN_POWER: int +KSCAN_PRINT: int +KSCAN_PRINTSCREEN: int +KSCAN_Q: int +KSCAN_R: int +KSCAN_RALT: int +KSCAN_RCTRL: int +KSCAN_RETURN: int +KSCAN_RGUI: int +KSCAN_RIGHT: int +KSCAN_RIGHTBRACKET: int +KSCAN_RMETA: int +KSCAN_RSHIFT: int +KSCAN_RSUPER: int +KSCAN_S: int +KSCAN_SCROLLLOCK: int +KSCAN_SCROLLOCK: int +KSCAN_SEMICOLON: int +KSCAN_SLASH: int +KSCAN_SPACE: int +KSCAN_SYSREQ: int +KSCAN_T: int +KSCAN_TAB: int +KSCAN_U: int +KSCAN_UNKNOWN: int +KSCAN_UP: int +KSCAN_V: int +KSCAN_W: int +KSCAN_X: int +KSCAN_Y: int +KSCAN_Z: int +K_0: int +K_1: int +K_2: int +K_3: int +K_4: int +K_5: int +K_6: int +K_7: int +K_8: int +K_9: int +K_AC_BACK: int +K_AMPERSAND: int +K_ASTERISK: int +K_AT: int +K_BACKQUOTE: int +K_BACKSLASH: int +K_BACKSPACE: int +K_BREAK: int +K_CAPSLOCK: int +K_CARET: int +K_CLEAR: int +K_COLON: int +K_COMMA: int +K_CURRENCYSUBUNIT: int +K_CURRENCYUNIT: int +K_DELETE: int +K_DOLLAR: int +K_DOWN: int +K_END: int +K_EQUALS: int +K_ESCAPE: int +K_EURO: int +K_EXCLAIM: int +K_F1: int +K_F10: int +K_F11: int +K_F12: int +K_F13: int +K_F14: int +K_F15: int +K_F2: int +K_F3: int +K_F4: int +K_F5: int +K_F6: int +K_F7: int +K_F8: int +K_F9: int +K_GREATER: int +K_HASH: int +K_HELP: int +K_HOME: int +K_INSERT: int +K_KP0: int +K_KP1: int +K_KP2: int +K_KP3: int +K_KP4: int +K_KP5: int +K_KP6: int +K_KP7: int +K_KP8: int +K_KP9: int +K_KP_0: int +K_KP_1: int +K_KP_2: int +K_KP_3: int +K_KP_4: int +K_KP_5: int +K_KP_6: int +K_KP_7: int +K_KP_8: int +K_KP_9: int +K_KP_DIVIDE: int +K_KP_ENTER: int +K_KP_EQUALS: int +K_KP_MINUS: int +K_KP_MULTIPLY: int +K_KP_PERIOD: int +K_KP_PLUS: int +K_LALT: int +K_LCTRL: int +K_LEFT: int +K_LEFTBRACKET: int +K_LEFTPAREN: int +K_LESS: int +K_LGUI: int +K_LMETA: int +K_LSHIFT: int +K_LSUPER: int +K_MENU: int +K_MINUS: int +K_MODE: int +K_NUMLOCK: int +K_NUMLOCKCLEAR: int +K_PAGEDOWN: int +K_PAGEUP: int +K_PAUSE: int +K_PERCENT: int +K_PERIOD: int +K_PLUS: int +K_POWER: int +K_PRINT: int +K_PRINTSCREEN: int +K_QUESTION: int +K_QUOTE: int +K_QUOTEDBL: int +K_RALT: int +K_RCTRL: int +K_RETURN: int +K_RGUI: int +K_RIGHT: int +K_RIGHTBRACKET: int +K_RIGHTPAREN: int +K_RMETA: int +K_RSHIFT: int +K_RSUPER: int +K_SCROLLLOCK: int +K_SCROLLOCK: int +K_SEMICOLON: int +K_SLASH: int +K_SPACE: int +K_SYSREQ: int +K_TAB: int +K_UNDERSCORE: int +K_UNKNOWN: int +K_UP: int +K_a: int +K_b: int +K_c: int +K_d: int +K_e: int +K_f: int +K_g: int +K_h: int +K_i: int +K_j: int +K_k: int +K_l: int +K_m: int +K_n: int +K_o: int +K_p: int +K_q: int +K_r: int +K_s: int +K_t: int +K_u: int +K_v: int +K_w: int +K_x: int +K_y: int +K_z: int +LIL_ENDIAN: int +LOCALECHANGED: int +MIDIIN: int +MIDIOUT: int +MOUSEBUTTONDOWN: int +MOUSEBUTTONUP: int +MOUSEMOTION: int +MOUSEWHEEL: int +MULTIGESTURE: int +NOEVENT: int +NOFRAME: int +NUMEVENTS: int +OPENGL: int +OPENGLBLIT: int +PREALLOC: int +QUIT: int +RENDER_DEVICE_RESET: int +RENDER_TARGETS_RESET: int +RESIZABLE: int +RLEACCEL: int +RLEACCELOK: int +Rect: type +SCALED: int +SCRAP_BMP: str +SCRAP_CLIPBOARD: int +SCRAP_PBM: str +SCRAP_PPM: str +SCRAP_SELECTION: int +SCRAP_TEXT: str +SHOWN: int +SRCALPHA: int +SRCCOLORKEY: int +SWSURFACE: int +SYSTEM_CURSOR_ARROW: int +SYSTEM_CURSOR_CROSSHAIR: int +SYSTEM_CURSOR_HAND: int +SYSTEM_CURSOR_IBEAM: int +SYSTEM_CURSOR_NO: int +SYSTEM_CURSOR_SIZEALL: int +SYSTEM_CURSOR_SIZENESW: int +SYSTEM_CURSOR_SIZENS: int +SYSTEM_CURSOR_SIZENWSE: int +SYSTEM_CURSOR_SIZEWE: int +SYSTEM_CURSOR_WAIT: int +SYSTEM_CURSOR_WAITARROW: int +SYSWMEVENT: int +TEXTEDITING: int +TEXTINPUT: int +TIMER_RESOLUTION: int +USEREVENT: int +USEREVENT_DROPFILE: int +VIDEOEXPOSE: int +VIDEORESIZE: int +WINDOWCLOSE: int +WINDOWDISPLAYCHANGED: int +WINDOWENTER: int +WINDOWEXPOSED: int +WINDOWFOCUSGAINED: int +WINDOWFOCUSLOST: int +WINDOWHIDDEN: int +WINDOWHITTEST: int +WINDOWICCPROFCHANGED: int +WINDOWLEAVE: int +WINDOWMAXIMIZED: int +WINDOWMINIMIZED: int +WINDOWMOVED: int +WINDOWRESIZED: int +WINDOWRESTORED: int +WINDOWSHOWN: int +WINDOWSIZECHANGED: int +WINDOWTAKEFOCUS: int diff --git a/.venv/Lib/site-packages/pygame/macosx.py b/.venv/Lib/site-packages/pygame/macosx.py new file mode 100644 index 00000000..0a16d317 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/macosx.py @@ -0,0 +1,13 @@ +import platform +import os +import sys + +__all__ = ["Video_AutoInit"] + + +def Video_AutoInit(): + """Called from the base.c just before display module is initialized.""" + if "Darwin" in platform.platform(): + if (os.getcwd() == "/") and len(sys.argv) > 1: + os.chdir(os.path.dirname(sys.argv[0])) + return True diff --git a/.venv/Lib/site-packages/pygame/mask.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/mask.cp311-win_amd64.pyd new file mode 100644 index 00000000..4bf23884 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/mask.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/mask.pyi b/.venv/Lib/site-packages/pygame/mask.pyi new file mode 100644 index 00000000..dbe248fe --- /dev/null +++ b/.venv/Lib/site-packages/pygame/mask.pyi @@ -0,0 +1,59 @@ +from typing import Any, List, Optional, Sequence, Tuple, Union + +from pygame.rect import Rect +from pygame.surface import Surface + +from ._common import ColorValue, Coordinate, RectValue + +def from_surface(surface: Surface, threshold: int = 127) -> Mask: ... +def from_threshold( + surface: Surface, + color: ColorValue, + threshold: ColorValue = (0, 0, 0, 255), + othersurface: Optional[Surface] = None, + palette_colors: int = 1, +) -> Mask: ... + +class Mask: + def __init__(self, size: Coordinate, fill: bool = False) -> None: ... + def __copy__(self) -> Mask: ... + copy = __copy__ + def get_size(self) -> Tuple[int, int]: ... + def get_rect(self, **kwargs: Any) -> Rect: ... # Dict type needs to be completed + def get_at(self, pos: Coordinate) -> int: ... + def set_at(self, pos: Coordinate, value: int = 1) -> None: ... + def overlap(self, other: Mask, offset: Coordinate) -> Optional[Tuple[int, int]]: ... + def overlap_area(self, other: Mask, offset: Coordinate) -> int: ... + def overlap_mask(self, other: Mask, offset: Coordinate) -> Mask: ... + def fill(self) -> None: ... + def clear(self) -> None: ... + def invert(self) -> None: ... + def scale(self, scale: Coordinate) -> Mask: ... + def draw(self, other: Mask, offset: Coordinate) -> None: ... + def erase(self, other: Mask, offset: Coordinate) -> None: ... + def count(self) -> int: ... + def centroid(self) -> Tuple[int, int]: ... + def angle(self) -> float: ... + def outline(self, every: int = 1) -> List[Tuple[int, int]]: ... + def convolve( + self, + other: Mask, + output: Optional[Mask] = None, + offset: Coordinate = (0, 0), + ) -> Mask: ... + def connected_component( + self, pos: Union[Sequence[int], Tuple[int, int]] = ... + ) -> Mask: ... + def connected_components(self, minimum: int = 0) -> List[Mask]: ... + def get_bounding_rects(self) -> Rect: ... + def to_surface( + self, + surface: Optional[Surface] = None, + setsurface: Optional[Surface] = None, + unsetsurface: Optional[Surface] = None, + setcolor: Optional[ColorValue] = (255, 255, 255, 255), + unsetcolor: Optional[ColorValue] = (0, 0, 0, 255), + dest: Union[RectValue, Coordinate] = (0, 0), + ) -> Surface: ... + +MaskType = Mask diff --git a/.venv/Lib/site-packages/pygame/math.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/math.cp311-win_amd64.pyd new file mode 100644 index 00000000..a153d74f Binary files /dev/null and b/.venv/Lib/site-packages/pygame/math.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/math.pyi b/.venv/Lib/site-packages/pygame/math.pyi new file mode 100644 index 00000000..5ee662c6 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/math.pyi @@ -0,0 +1,342 @@ +import sys +from typing import ( + Any, + Generic, + Iterator, + List, + Literal, + Sequence, + Tuple, + Type, + TypeVar, + Union, + final, + overload, + Optional, +) + +from typing_extensions import Protocol + +if sys.version_info >= (3, 9): + from collections.abc import Collection +else: + from typing import Collection + +def lerp(a: float, b: float, weight: float) -> float: ... +def clamp(value: float, min: float, max: float) -> float: ... + +_TVec = TypeVar("_TVec", bound=_GenericVector) + +# not implemented in code, only implemented here for ease of implementing +# typestubs. Contains attributes/methods common to Vector2 and Vector3 +# Also used with _TVec generics +class _GenericVector(Collection[float]): + epsilon: float + __hash__: None # type: ignore + def __len__(self) -> int: ... + @overload + def __setitem__(self, key: int, value: float) -> None: ... + @overload + def __setitem__(self, key: slice, value: Union[Sequence[float], _TVec]) -> None: ... + @overload + def __getitem__(self, i: int) -> float: ... + @overload + def __getitem__(self, s: slice) -> List[float]: ... + def __iter__(self) -> VectorIterator: ... + def __add__(self: _TVec, other: Union[Sequence[float], _TVec]) -> _TVec: ... + def __radd__(self: _TVec, other: Union[Sequence[float], _TVec]) -> _TVec: ... + def __sub__(self: _TVec, other: Union[Sequence[float], _TVec]) -> _TVec: ... + def __rsub__(self: _TVec, other: Union[Sequence[float], _TVec]) -> _TVec: ... + @overload + def __mul__(self: _TVec, other: Union[Sequence[float], _TVec]) -> float: ... + @overload + def __mul__(self: _TVec, other: float) -> _TVec: ... + def __rmul__(self: _TVec, other: float) -> _TVec: ... + def __truediv__(self: _TVec, other: float) -> _TVec: ... + def __rtruediv__(self: _TVec, other: float) -> _TVec: ... + def __floordiv__(self: _TVec, other: float) -> _TVec: ... + def __neg__(self: _TVec) -> _TVec: ... + def __pos__(self: _TVec) -> _TVec: ... + def __bool__(self) -> bool: ... + def __iadd__(self: _TVec, other: Union[Sequence[float], _TVec]) -> _TVec: ... + def __isub__(self: _TVec, other: Union[Sequence[float], _TVec]) -> _TVec: ... + @overload + def __imul__(self: _TVec, other: Union[Sequence[float], _TVec]) -> float: ... + @overload + def __imul__(self: _TVec, other: float) -> _TVec: ... + def __copy__(self: _TVec) -> _TVec: ... + copy = __copy__ + def __safe_for_unpickling__(self) -> Literal[True]: ... + def __contains__(self, other: float) -> bool: ... # type: ignore[override] + def dot(self: _TVec, other: Union[Sequence[float], _TVec]) -> float: ... + def magnitude(self) -> float: ... + def magnitude_squared(self) -> float: ... + def length(self) -> float: ... + def length_squared(self) -> float: ... + def normalize(self: _TVec) -> _TVec: ... + def normalize_ip(self) -> None: ... + def is_normalized(self) -> bool: ... + def scale_to_length(self, value: float) -> None: ... + def reflect(self: _TVec, other: Union[Sequence[float], _TVec]) -> _TVec: ... + def reflect_ip(self: _TVec, other: Union[Sequence[float], _TVec]) -> None: ... + def distance_to(self: _TVec, other: Union[Sequence[float], _TVec]) -> float: ... + def distance_squared_to( + self: _TVec, other: Union[Sequence[float], _TVec] + ) -> float: ... + def lerp( + self: _TVec, + other: Union[Sequence[float], _TVec], + value: float, + ) -> _TVec: ... + def slerp( + self: _TVec, + other: Union[Sequence[float], _TVec], + value: float, + ) -> _TVec: ... + def elementwise(self: _TVec) -> VectorElementwiseProxy[_TVec]: ... + def angle_to(self: _TVec, other: Union[Sequence[float], _TVec]) -> float: ... + def move_towards( + self: _TVec, + target: Union[Sequence[float], _TVec], + max_distance: float, + ) -> _TVec: ... + def move_towards_ip( + self: _TVec, + target: Union[Sequence[float], _TVec], + max_distance: float, + ) -> None: ... + @overload + def clamp_magnitude(self: _TVec, max_length: float) -> _TVec: ... + @overload + def clamp_magnitude( + self: _TVec, min_length: float, max_length: float + ) -> _TVec: ... + @overload + def clamp_magnitude_ip(self, max_length: float) -> None: ... + @overload + def clamp_magnitude_ip(self, min_length: float, max_length: float) -> None: ... + def project(self: _TVec, other: Union[Sequence[float], _TVec]) -> _TVec: ... + def __round__(self: _TVec, ndigits: Optional[int]) -> _TVec: ... + +# VectorElementwiseProxy is a generic, it can be an elementwiseproxy object for +# Vector2, Vector3 and vector subclass objects +@final +class VectorElementwiseProxy(Generic[_TVec]): + def __add__( + self, + other: Union[float, _TVec, VectorElementwiseProxy[_TVec]], + ) -> _TVec: ... + def __radd__( + self, + other: Union[float, _TVec, VectorElementwiseProxy[_TVec]], + ) -> _TVec: ... + def __sub__( + self, + other: Union[float, _TVec, VectorElementwiseProxy[_TVec]], + ) -> _TVec: ... + def __rsub__( + self, + other: Union[float, _TVec, VectorElementwiseProxy[_TVec]], + ) -> _TVec: ... + def __mul__( + self, + other: Union[float, _TVec, VectorElementwiseProxy[_TVec]], + ) -> _TVec: ... + def __rmul__( + self, + other: Union[float, _TVec, VectorElementwiseProxy[_TVec]], + ) -> _TVec: ... + def __truediv__( + self, + other: Union[float, _TVec, VectorElementwiseProxy[_TVec]], + ) -> _TVec: ... + def __rtruediv__( + self, + other: Union[float, _TVec, VectorElementwiseProxy[_TVec]], + ) -> _TVec: ... + def __floordiv__( + self, + other: Union[float, _TVec, VectorElementwiseProxy[_TVec]], + ) -> _TVec: ... + def __rfloordiv__( + self, + other: Union[float, _TVec, VectorElementwiseProxy[_TVec]], + ) -> _TVec: ... + def __mod__( + self, + other: Union[float, _TVec, VectorElementwiseProxy[_TVec]], + ) -> _TVec: ... + def __rmod__( + self, + other: Union[float, _TVec, VectorElementwiseProxy[_TVec]], + ) -> _TVec: ... + def __pow__( + self, + power: Union[float, _TVec, VectorElementwiseProxy[_TVec]], + mod: None = None, + ) -> _TVec: ... + def __rpow__( + self, + power: Union[float, _TVec, VectorElementwiseProxy[_TVec]], + mod: None = None, + ) -> _TVec: ... + def __eq__(self, other: Any) -> bool: ... + def __ne__(self, other: Any) -> bool: ... + def __gt__( + self, + other: Union[float, _TVec, VectorElementwiseProxy[_TVec]], + ) -> bool: ... + def __lt__( + self, + other: Union[float, _TVec, VectorElementwiseProxy[_TVec]], + ) -> bool: ... + def __ge__( + self, + other: Union[float, _TVec, VectorElementwiseProxy[_TVec]], + ) -> bool: ... + def __le__( + self, + other: Union[float, _TVec, VectorElementwiseProxy[_TVec]], + ) -> bool: ... + def __abs__(self) -> _TVec: ... + def __neg__(self) -> _TVec: ... + def __pos__(self) -> _TVec: ... + def __bool__(self) -> bool: ... + +@final +class VectorIterator: + def __length_hint__(self) -> int: ... + def __iter__(self) -> Iterator[float]: ... + def __next__(self) -> float: ... + +# Not defined in code, only for type checking from_polar ClassObjectMethod +class _from_polar_protocol(Protocol): + def __call__(self, value: Tuple[float, float]) -> Optional[_TVec]: ... + +class Vector2(_GenericVector): + x: float + y: float + xx: Vector2 + xy: Vector2 + yx: Vector2 + yy: Vector2 + from_polar: _from_polar_protocol + @overload + def __init__( + self: _TVec, + x: Union[str, float, Sequence[float], _TVec] = 0, + ) -> None: ... + @overload + def __init__(self, x: float, y: float) -> None: ... + def __reduce__(self: _TVec) -> Tuple[Type[_TVec], Tuple[float, float]]: ... + def rotate(self: _TVec, angle: float) -> _TVec: ... + def rotate_rad(self: _TVec, angle: float) -> _TVec: ... + def rotate_ip(self, angle: float) -> None: ... + def rotate_rad_ip(self, angle: float) -> None: ... + def rotate_ip_rad(self, angle: float) -> None: ... + def cross(self: _TVec, other: Union[Sequence[float], _TVec]) -> float: ... + def as_polar(self) -> Tuple[float, float]: ... + @overload + def update( + self: _TVec, + x: Union[str, float, Sequence[float], _TVec] = 0, + ) -> None: ... + @overload + def update(self, x: float = 0, y: float = 0) -> None: ... + +# Not defined in code, only for type checking from_spherical ClassObjectMethod +class _from_spherical_protocol(Protocol): + def __call__(self, value: Tuple[float, float, float]) -> Optional[_TVec]: ... + +class Vector3(_GenericVector): + x: float + y: float + z: float + xx: Vector2 + xy: Vector2 + xz: Vector2 + yx: Vector2 + yy: Vector2 + yz: Vector2 + zx: Vector2 + zy: Vector2 + zz: Vector2 + xxx: Vector3 + xxy: Vector3 + xxz: Vector3 + xyx: Vector3 + xyy: Vector3 + xyz: Vector3 + xzx: Vector3 + xzy: Vector3 + xzz: Vector3 + yxx: Vector3 + yxy: Vector3 + yxz: Vector3 + yyx: Vector3 + yyy: Vector3 + yyz: Vector3 + yzx: Vector3 + yzy: Vector3 + yzz: Vector3 + zxx: Vector3 + zxy: Vector3 + zxz: Vector3 + zyx: Vector3 + zyy: Vector3 + zyz: Vector3 + zzx: Vector3 + zzy: Vector3 + zzz: Vector3 + from_spherical: _from_spherical_protocol + @overload + def __init__( + self: _TVec, + x: Union[str, float, Sequence[float], _TVec] = 0, + ) -> None: ... + @overload + def __init__(self, x: float, y: float, z: float) -> None: ... + def __reduce__(self: _TVec) -> Tuple[Type[_TVec], Tuple[float, float, float]]: ... + def cross(self: _TVec, other: Union[Sequence[float], _TVec]) -> _TVec: ... + def rotate( + self: _TVec, angle: float, axis: Union[Sequence[float], _TVec] + ) -> _TVec: ... + def rotate_rad( + self: _TVec, angle: float, axis: Union[Sequence[float], _TVec] + ) -> _TVec: ... + def rotate_ip( + self: _TVec, angle: float, axis: Union[Sequence[float], _TVec] + ) -> None: ... + def rotate_rad_ip( + self: _TVec, angle: float, axis: Union[Sequence[float], _TVec] + ) -> None: ... + def rotate_ip_rad( + self: _TVec, angle: float, axis: Union[Sequence[float], _TVec] + ) -> None: ... + def rotate_x(self: _TVec, angle: float) -> _TVec: ... + def rotate_x_rad(self: _TVec, angle: float) -> _TVec: ... + def rotate_x_ip(self, angle: float) -> None: ... + def rotate_x_rad_ip(self, angle: float) -> None: ... + def rotate_x_ip_rad(self, angle: float) -> None: ... + def rotate_y(self: _TVec, angle: float) -> _TVec: ... + def rotate_y_rad(self: _TVec, angle: float) -> _TVec: ... + def rotate_y_ip(self, angle: float) -> None: ... + def rotate_y_rad_ip(self, angle: float) -> None: ... + def rotate_y_ip_rad(self, angle: float) -> None: ... + def rotate_z(self: _TVec, angle: float) -> _TVec: ... + def rotate_z_rad(self: _TVec, angle: float) -> _TVec: ... + def rotate_z_ip(self, angle: float) -> None: ... + def rotate_z_rad_ip(self, angle: float) -> None: ... + def rotate_z_ip_rad(self, angle: float) -> None: ... + def as_spherical(self) -> Tuple[float, float, float]: ... + @overload + def update( + self: _TVec, + x: Union[str, float, Sequence[float], _TVec] = 0, + ) -> None: ... + @overload + def update(self, x: int, y: int, z: int) -> None: ... + +# typehints for deprecated functions, to be removed in a future version +def enable_swizzling() -> None: ... +def disable_swizzling() -> None: ... diff --git a/.venv/Lib/site-packages/pygame/midi.py b/.venv/Lib/site-packages/pygame/midi.py new file mode 100644 index 00000000..24830f65 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/midi.py @@ -0,0 +1,717 @@ +"""pygame.midi +pygame module for interacting with midi input and output. + +The midi module can send output to midi devices, and get input +from midi devices. It can also list midi devices on the system. + +Including real midi devices, and virtual ones. + +It uses the portmidi library. Is portable to which ever platforms +portmidi supports (currently windows, OSX, and linux). + +This uses pyportmidi for now, but may use its own bindings at some +point in the future. The pyportmidi bindings are included with pygame. + +New in pygame 1.9.0. +""" + +# TODO: finish writing tests. +# - likely as interactive tests... so you'd need to plug in +# a midi device. +# TODO: create a background thread version for input threads. +# - that can automatically inject input into the event queue +# once the input object is running. Like joysticks. + +import math +import atexit + +import pygame +import pygame.locals + +import pygame.pypm as _pypm + +# For backward compatibility. +MIDIIN = pygame.locals.MIDIIN +MIDIOUT = pygame.locals.MIDIOUT + +__all__ = [ + "Input", + "MIDIIN", + "MIDIOUT", + "MidiException", + "Output", + "get_count", + "get_default_input_id", + "get_default_output_id", + "get_device_info", + "init", + "midis2events", + "quit", + "get_init", + "time", + "frequency_to_midi", + "midi_to_frequency", + "midi_to_ansi_note", +] + +__theclasses__ = ["Input", "Output"] + + +def _module_init(state=None): + # this is a sneaky dodge to store module level state in a non-public + # function. Helps us dodge using globals. + if state is not None: + _module_init.value = state + return state + + try: + _module_init.value + except AttributeError: + return False + return _module_init.value + + +def init(): + """initialize the midi module + pygame.midi.init(): return None + + Call the initialisation function before using the midi module. + + It is safe to call this more than once. + """ + if not _module_init(): + _pypm.Initialize() + _module_init(True) + atexit.register(quit) + + +def quit(): # pylint: disable=redefined-builtin + """uninitialize the midi module + pygame.midi.quit(): return None + + + Called automatically atexit if you don't call it. + + It is safe to call this function more than once. + """ + if _module_init(): + # TODO: find all Input and Output classes and close them first? + _pypm.Terminate() + _module_init(False) + + +def get_init(): + """returns True if the midi module is currently initialized + pygame.midi.get_init(): return bool + + Returns True if the pygame.midi module is currently initialized. + + New in pygame 1.9.5. + """ + return _module_init() + + +def _check_init(): + if not _module_init(): + raise RuntimeError("pygame.midi not initialised.") + + +def get_count(): + """gets the number of devices. + pygame.midi.get_count(): return num_devices + + + Device ids range from 0 to get_count() -1 + """ + _check_init() + return _pypm.CountDevices() + + +def get_default_input_id(): + """gets default input device number + pygame.midi.get_default_input_id(): return default_id + + + Return the default device ID or -1 if there are no devices. + The result can be passed to the Input()/Output() class. + + On the PC, the user can specify a default device by + setting an environment variable. For example, to use device #1. + + set PM_RECOMMENDED_INPUT_DEVICE=1 + + The user should first determine the available device ID by using + the supplied application "testin" or "testout". + + In general, the registry is a better place for this kind of info, + and with USB devices that can come and go, using integers is not + very reliable for device identification. Under Windows, if + PM_RECOMMENDED_OUTPUT_DEVICE (or PM_RECOMMENDED_INPUT_DEVICE) is + *NOT* found in the environment, then the default device is obtained + by looking for a string in the registry under: + HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Input_Device + and HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Output_Device + for a string. The number of the first device with a substring that + matches the string exactly is returned. For example, if the string + in the registry is "USB", and device 1 is named + "In USB MidiSport 1x1", then that will be the default + input because it contains the string "USB". + + In addition to the name, get_device_info() returns "interf", which + is the interface name. (The "interface" is the underlying software + system or API used by PortMidi to access devices. Examples are + MMSystem, DirectX (not implemented), ALSA, OSS (not implemented), etc.) + At present, the only Win32 interface is "MMSystem", the only Linux + interface is "ALSA", and the only Max OS X interface is "CoreMIDI". + To specify both the interface and the device name in the registry, + separate the two with a comma and a space, e.g.: + MMSystem, In USB MidiSport 1x1 + In this case, the string before the comma must be a substring of + the "interf" string, and the string after the space must be a + substring of the "name" name string in order to match the device. + + Note: in the current release, the default is simply the first device + (the input or output device with the lowest PmDeviceID). + """ + _check_init() + return _pypm.GetDefaultInputDeviceID() + + +def get_default_output_id(): + """gets default output device number + pygame.midi.get_default_output_id(): return default_id + + + Return the default device ID or -1 if there are no devices. + The result can be passed to the Input()/Output() class. + + On the PC, the user can specify a default device by + setting an environment variable. For example, to use device #1. + + set PM_RECOMMENDED_OUTPUT_DEVICE=1 + + The user should first determine the available device ID by using + the supplied application "testin" or "testout". + + In general, the registry is a better place for this kind of info, + and with USB devices that can come and go, using integers is not + very reliable for device identification. Under Windows, if + PM_RECOMMENDED_OUTPUT_DEVICE (or PM_RECOMMENDED_INPUT_DEVICE) is + *NOT* found in the environment, then the default device is obtained + by looking for a string in the registry under: + HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Input_Device + and HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Output_Device + for a string. The number of the first device with a substring that + matches the string exactly is returned. For example, if the string + in the registry is "USB", and device 1 is named + "In USB MidiSport 1x1", then that will be the default + input because it contains the string "USB". + + In addition to the name, get_device_info() returns "interf", which + is the interface name. (The "interface" is the underlying software + system or API used by PortMidi to access devices. Examples are + MMSystem, DirectX (not implemented), ALSA, OSS (not implemented), etc.) + At present, the only Win32 interface is "MMSystem", the only Linux + interface is "ALSA", and the only Max OS X interface is "CoreMIDI". + To specify both the interface and the device name in the registry, + separate the two with a comma and a space, e.g.: + MMSystem, In USB MidiSport 1x1 + In this case, the string before the comma must be a substring of + the "interf" string, and the string after the space must be a + substring of the "name" name string in order to match the device. + + Note: in the current release, the default is simply the first device + (the input or output device with the lowest PmDeviceID). + """ + _check_init() + return _pypm.GetDefaultOutputDeviceID() + + +def get_device_info(an_id): + """returns information about a midi device + pygame.midi.get_device_info(an_id): return (interf, name, + input, output, + opened) + + interf - a byte string describing the device interface, eg b'ALSA'. + name - a byte string for the name of the device, eg b'Midi Through Port-0' + input - 0, or 1 if the device is an input device. + output - 0, or 1 if the device is an output device. + opened - 0, or 1 if the device is opened. + + If the id is out of range, the function returns None. + """ + _check_init() + return _pypm.GetDeviceInfo(an_id) + + +class Input: + """Input is used to get midi input from midi devices. + Input(device_id) + Input(device_id, buffer_size) + + buffer_size - the number of input events to be buffered waiting to + be read using Input.read() + """ + + def __init__(self, device_id, buffer_size=4096): + """ + The buffer_size specifies the number of input events to be buffered + waiting to be read using Input.read(). + """ + _check_init() + + if device_id == -1: + raise MidiException( + "Device id is -1, not a valid output id. " + "-1 usually means there were no default " + "Output devices." + ) + + try: + result = get_device_info(device_id) + except TypeError: + raise TypeError("an integer is required") + except OverflowError: + raise OverflowError("long int too large to convert to int") + + # and now some nasty looking error checking, to provide nice error + # messages to the kind, lovely, midi using people of wherever. + if result: + _, _, is_input, is_output, _ = result + if is_input: + try: + self._input = _pypm.Input(device_id, buffer_size) + except TypeError: + raise TypeError("an integer is required") + self.device_id = device_id + + elif is_output: + raise MidiException( + "Device id given is not a valid input id, it is an output id." + ) + else: + raise MidiException("Device id given is not a valid input id.") + else: + raise MidiException("Device id invalid, out of range.") + + def _check_open(self): + if self._input is None: + raise MidiException("midi not open.") + + def close(self): + """closes a midi stream, flushing any pending buffers. + Input.close(): return None + + PortMidi attempts to close open streams when the application + exits -- this is particularly difficult under Windows. + """ + _check_init() + if self._input is not None: + self._input.Close() + self._input = None + + def read(self, num_events): + """reads num_events midi events from the buffer. + Input.read(num_events): return midi_event_list + + Reads from the Input buffer and gives back midi events. + [[[status,data1,data2,data3],timestamp], + [[status,data1,data2,data3],timestamp],...] + """ + _check_init() + self._check_open() + return self._input.Read(num_events) + + def poll(self): + """returns true if there's data, or false if not. + Input.poll(): return Bool + + raises a MidiException on error. + """ + _check_init() + self._check_open() + + result = self._input.Poll() + if result == _pypm.TRUE: + return True + + if result == _pypm.FALSE: + return False + + err_text = _pypm.GetErrorText(result) + raise MidiException((result, err_text)) + + +class Output: + """Output is used to send midi to an output device + Output(device_id) + Output(device_id, latency = 0) + Output(device_id, buffer_size = 4096) + Output(device_id, latency, buffer_size) + + The buffer_size specifies the number of output events to be + buffered waiting for output. (In some cases -- see below -- + PortMidi does not buffer output at all and merely passes data + to a lower-level API, in which case buffersize is ignored.) + + latency is the delay in milliseconds applied to timestamps to determine + when the output should actually occur. (If latency is < 0, 0 is + assumed.) + + If latency is zero, timestamps are ignored and all output is delivered + immediately. If latency is greater than zero, output is delayed until + the message timestamp plus the latency. (NOTE: time is measured + relative to the time source indicated by time_proc. Timestamps are + absolute, not relative delays or offsets.) In some cases, PortMidi + can obtain better timing than your application by passing timestamps + along to the device driver or hardware. Latency may also help you + to synchronize midi data to audio data by matching midi latency to + the audio buffer latency. + + """ + + def __init__(self, device_id, latency=0, buffer_size=256): + """Output(device_id) + Output(device_id, latency = 0) + Output(device_id, buffer_size = 4096) + Output(device_id, latency, buffer_size) + + The buffer_size specifies the number of output events to be + buffered waiting for output. (In some cases -- see below -- + PortMidi does not buffer output at all and merely passes data + to a lower-level API, in which case buffersize is ignored.) + + latency is the delay in milliseconds applied to timestamps to determine + when the output should actually occur. (If latency is < 0, 0 is + assumed.) + + If latency is zero, timestamps are ignored and all output is delivered + immediately. If latency is greater than zero, output is delayed until + the message timestamp plus the latency. (NOTE: time is measured + relative to the time source indicated by time_proc. Timestamps are + absolute, not relative delays or offsets.) In some cases, PortMidi + can obtain better timing than your application by passing timestamps + along to the device driver or hardware. Latency may also help you + to synchronize midi data to audio data by matching midi latency to + the audio buffer latency. + """ + + _check_init() + self._aborted = 0 + + if device_id == -1: + raise MidiException( + "Device id is -1, not a valid output id." + " -1 usually means there were no default " + "Output devices." + ) + + try: + result = get_device_info(device_id) + except TypeError: + raise TypeError("an integer is required") + except OverflowError: + raise OverflowError("long int too large to convert to int") + + # and now some nasty looking error checking, to provide nice error + # messages to the kind, lovely, midi using people of wherever. + if result: + _, _, is_input, is_output, _ = result + if is_output: + try: + self._output = _pypm.Output(device_id, latency, buffer_size) + except TypeError: + raise TypeError("an integer is required") + self.device_id = device_id + + elif is_input: + raise MidiException( + "Device id given is not a valid output id, it is an input id." + ) + else: + raise MidiException("Device id given is not a valid output id.") + else: + raise MidiException("Device id invalid, out of range.") + + def _check_open(self): + if self._output is None: + raise MidiException("midi not open.") + + if self._aborted: + raise MidiException("midi aborted.") + + def close(self): + """closes a midi stream, flushing any pending buffers. + Output.close(): return None + + PortMidi attempts to close open streams when the application + exits -- this is particularly difficult under Windows. + """ + _check_init() + if self._output is not None: + self._output.Close() + self._output = None + + def abort(self): + """terminates outgoing messages immediately + Output.abort(): return None + + The caller should immediately close the output port; + this call may result in transmission of a partial midi message. + There is no abort for Midi input because the user can simply + ignore messages in the buffer and close an input device at + any time. + """ + + _check_init() + if self._output: + self._output.Abort() + self._aborted = 1 + + def write(self, data): + """writes a list of midi data to the Output + Output.write(data) + + writes series of MIDI information in the form of a list: + write([[[status <,data1><,data2><,data3>],timestamp], + [[status <,data1><,data2><,data3>],timestamp],...]) + fields are optional + example: choose program change 1 at time 20000 and + send note 65 with velocity 100 500 ms later. + write([[[0xc0,0,0],20000],[[0x90,60,100],20500]]) + notes: + 1. timestamps will be ignored if latency = 0. + 2. To get a note to play immediately, send MIDI info with + timestamp read from function Time. + 3. understanding optional data fields: + write([[[0xc0,0,0],20000]]) is equivalent to + write([[[0xc0],20000]]) + + Can send up to 1024 elements in your data list, otherwise an + IndexError exception is raised. + """ + _check_init() + self._check_open() + + self._output.Write(data) + + def write_short(self, status, data1=0, data2=0): + """write_short(status <, data1><, data2>) + Output.write_short(status) + Output.write_short(status, data1 = 0, data2 = 0) + + output MIDI information of 3 bytes or less. + data fields are optional + status byte could be: + 0xc0 = program change + 0x90 = note on + etc. + data bytes are optional and assumed 0 if omitted + example: note 65 on with velocity 100 + write_short(0x90,65,100) + """ + _check_init() + self._check_open() + self._output.WriteShort(status, data1, data2) + + def write_sys_ex(self, when, msg): + """writes a timestamped system-exclusive midi message. + Output.write_sys_ex(when, msg) + + msg - can be a *list* or a *string* + when - a timestamp in milliseconds + example: + (assuming o is an onput MIDI stream) + o.write_sys_ex(0,'\\xF0\\x7D\\x10\\x11\\x12\\x13\\xF7') + is equivalent to + o.write_sys_ex(pygame.midi.time(), + [0xF0,0x7D,0x10,0x11,0x12,0x13,0xF7]) + """ + _check_init() + self._check_open() + self._output.WriteSysEx(when, msg) + + def note_on(self, note, velocity, channel=0): + """turns a midi note on. Note must be off. + Output.note_on(note, velocity, channel=0) + + note is an integer from 0 to 127 + velocity is an integer from 0 to 127 + channel is an integer from 0 to 15 + + Turn a note on in the output stream. The note must already + be off for this to work correctly. + """ + if not 0 <= channel <= 15: + raise ValueError("Channel not between 0 and 15.") + + self.write_short(0x90 + channel, note, velocity) + + def note_off(self, note, velocity=0, channel=0): + """turns a midi note off. Note must be on. + Output.note_off(note, velocity=0, channel=0) + + note is an integer from 0 to 127 + velocity is an integer from 0 to 127 (release velocity) + channel is an integer from 0 to 15 + + Turn a note off in the output stream. The note must already + be on for this to work correctly. + """ + if not 0 <= channel <= 15: + raise ValueError("Channel not between 0 and 15.") + + self.write_short(0x80 + channel, note, velocity) + + def set_instrument(self, instrument_id, channel=0): + """select an instrument for a channel, with a value between 0 and 127 + Output.set_instrument(instrument_id, channel=0) + + Also called "patch change" or "program change". + """ + if not 0 <= instrument_id <= 127: + raise ValueError(f"Undefined instrument id: {instrument_id}") + + if not 0 <= channel <= 15: + raise ValueError("Channel not between 0 and 15.") + + self.write_short(0xC0 + channel, instrument_id) + + def pitch_bend(self, value=0, channel=0): + """modify the pitch of a channel. + Output.pitch_bend(value=0, channel=0) + + Adjust the pitch of a channel. The value is a signed integer + from -8192 to +8191. For example, 0 means "no change", +4096 is + typically a semitone higher, and -8192 is 1 whole tone lower (though + the musical range corresponding to the pitch bend range can also be + changed in some synthesizers). + + If no value is given, the pitch bend is returned to "no change". + """ + if not 0 <= channel <= 15: + raise ValueError("Channel not between 0 and 15.") + + if not -8192 <= value <= 8191: + raise ValueError( + f"Pitch bend value must be between -8192 and +8191, not {value}." + ) + + # "The 14 bit value of the pitch bend is defined so that a value of + # 0x2000 is the center corresponding to the normal pitch of the note + # (no pitch change)." so value=0 should send 0x2000 + value = value + 0x2000 + lsb = value & 0x7F # keep least 7 bits + msb = value >> 7 + self.write_short(0xE0 + channel, lsb, msb) + + +# MIDI commands +# +# 0x80 Note Off (note_off) +# 0x90 Note On (note_on) +# 0xA0 Aftertouch +# 0xB0 Continuous controller +# 0xC0 Patch change (set_instrument?) +# 0xD0 Channel Pressure +# 0xE0 Pitch bend +# 0xF0 (non-musical commands) + + +def time(): + """returns the current time in ms of the PortMidi timer + pygame.midi.time(): return time + + The time is reset to 0, when the module is inited. + """ + _check_init() + return _pypm.Time() + + +def midis2events(midis, device_id): + """converts midi events to pygame events + pygame.midi.midis2events(midis, device_id): return [Event, ...] + + Takes a sequence of midi events and returns list of pygame events. + """ + evs = [] + for midi in midis: + ((status, data1, data2, data3), timestamp) = midi + + event = pygame.event.Event( + MIDIIN, + status=status, + data1=data1, + data2=data2, + data3=data3, + timestamp=timestamp, + vice_id=device_id, + ) + evs.append(event) + + return evs + + +class MidiException(Exception): + """exception that pygame.midi functions and classes can raise + MidiException(errno) + """ + + def __init__(self, value): + super().__init__(value) + self.parameter = value + + def __str__(self): + return repr(self.parameter) + + +def frequency_to_midi(frequency): + """converts a frequency into a MIDI note. + + Rounds to the closest midi note. + + ::Examples:: + + >>> frequency_to_midi(27.5) + 21 + >>> frequency_to_midi(36.7) + 26 + >>> frequency_to_midi(4186.0) + 108 + """ + return int(round(69 + (12 * math.log(frequency / 440.0)) / math.log(2))) + + +def midi_to_frequency(midi_note): + """Converts a midi note to a frequency. + + ::Examples:: + + >>> midi_to_frequency(21) + 27.5 + >>> midi_to_frequency(26) + 36.7 + >>> midi_to_frequency(108) + 4186.0 + """ + return round(440.0 * 2 ** ((midi_note - 69) * (1.0 / 12.0)), 1) + + +def midi_to_ansi_note(midi_note): + """returns the Ansi Note name for a midi number. + + ::Examples:: + + >>> midi_to_ansi_note(21) + 'A0' + >>> midi_to_ansi_note(102) + 'F#7' + >>> midi_to_ansi_note(108) + 'C8' + """ + notes = ["A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"] + num_notes = 12 + note_name = notes[int((midi_note - 21) % num_notes)] + note_number = (midi_note - 12) // num_notes + return f"{note_name}{note_number}" diff --git a/.venv/Lib/site-packages/pygame/midi.pyi b/.venv/Lib/site-packages/pygame/midi.pyi new file mode 100644 index 00000000..ee383ef6 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/midi.pyi @@ -0,0 +1,49 @@ +from typing import List, Sequence, Tuple, Union + +from pygame.event import Event + +MIDIIN: int +MIDIOUT: int + +class MidiException(Exception): + def __init__(self, value: str) -> None: ... + +def init() -> None: ... +def quit() -> None: ... +def get_init() -> bool: ... +def get_count() -> int: ... +def get_default_input_id() -> int: ... +def get_default_output_id() -> int: ... +def get_device_info(an_id: int) -> Tuple[bytes, bytes, int, int, int]: ... +def midis2events( + midis: Sequence[Sequence[Union[Sequence[int], int]]], device_id: int +) -> List[Event]: ... +def time() -> int: ... +def frequency_to_midi(frequency: float) -> int: ... +def midi_to_frequency(midi_note: int) -> float: ... +def midi_to_ansi_note(midi_note: int) -> str: ... + +class Input: + device_id: int + def __init__(self, device_id: int, buffer_size: int = 4096) -> None: ... + def close(self) -> None: ... + def poll(self) -> bool: ... + def read(self, num_events: int) -> List[List[Union[List[int], int]]]: ... + +class Output: + device_id: int + def __init__( + self, + device_id: int, + latency: int = 0, + buffer_size: int = 256, + ) -> None: ... + def abort(self) -> None: ... + def close(self) -> None: ... + def note_off(self, note: int, velocity: int = 0, channel: int = 0) -> None: ... + def note_on(self, note: int, velocity: int, channel: int = 0) -> None: ... + def set_instrument(self, instrument_id: int, channel: int = 0) -> None: ... + def pitch_bend(self, value: int = 0, channel: int = 0) -> None: ... + def write(self, data: List[List[Union[List[int], int]]]) -> None: ... + def write_short(self, status: int, data1: int = 0, data2: int = 0) -> None: ... + def write_sys_ex(self, when: int, msg: Union[List[int], str]) -> None: ... diff --git a/.venv/Lib/site-packages/pygame/mixer.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/mixer.cp311-win_amd64.pyd new file mode 100644 index 00000000..1712dbfc Binary files /dev/null and b/.venv/Lib/site-packages/pygame/mixer.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/mixer.pyi b/.venv/Lib/site-packages/pygame/mixer.pyi new file mode 100644 index 00000000..5b0e13a3 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/mixer.pyi @@ -0,0 +1,98 @@ +from typing import Any, Dict, Optional, Tuple, Union, final, overload + +import numpy + +from pygame.event import Event + +from . import mixer_music +from ._common import FileArg + +# export mixer_music as mixer.music +music = mixer_music + +def init( + frequency: int = 44100, + size: int = -16, + channels: int = 2, + buffer: int = 512, + devicename: Optional[str] = None, + allowedchanges: int = 5, +) -> None: ... +def pre_init( + frequency: int = 44100, + size: int = -16, + channels: int = 2, + buffer: int = 512, + devicename: Optional[str] = None, + allowedchanges: int = 5, +) -> None: ... +def quit() -> None: ... +def get_init() -> Tuple[int, int, int]: ... +def stop() -> None: ... +def pause() -> None: ... +def unpause() -> None: ... +def fadeout(time: int) -> None: ... +def set_num_channels(count: int) -> None: ... +def get_num_channels() -> int: ... +def set_reserved(count: int) -> int: ... +def find_channel(force: bool = False) -> Channel: ... +def get_busy() -> bool: ... +def get_sdl_mixer_version(linked: bool = True) -> Tuple[int, int, int]: ... + +class Sound: + @overload + def __init__(self, file: FileArg) -> None: ... + @overload + def __init__( + self, buffer: Any + ) -> None: ... # Buffer protocol is still not implemented in typing + @overload + def __init__( + self, array: numpy.ndarray + ) -> None: ... # Buffer protocol is still not implemented in typing + def play( + self, + loops: int = 0, + maxtime: int = 0, + fade_ms: int = 0, + ) -> Channel: ... + # possibly going to be deprecated/removed soon, in which case these + # typestubs must be removed too + __array_interface__: Dict[str, Any] + __array_struct__: Any + def stop(self) -> None: ... + def fadeout(self, time: int) -> None: ... + def set_volume(self, value: float) -> None: ... + def get_volume(self) -> float: ... + def get_num_channels(self) -> int: ... + def get_length(self) -> float: ... + def get_raw(self) -> bytes: ... + +@final +class Channel: + def __init__(self, id: int) -> None: ... + def play( + self, + sound: Sound, + loops: int = 0, + maxtime: int = 0, + fade_ms: int = 0, + ) -> None: ... + def stop(self) -> None: ... + def pause(self) -> None: ... + def unpause(self) -> None: ... + def fadeout(self, time: int) -> None: ... + def queue(self, sound: Sound) -> None: ... + @overload + def set_volume(self, value: float) -> None: ... + @overload + def set_volume(self, left: float, right: float) -> None: ... + def get_volume(self) -> float: ... + def get_busy(self) -> bool: ... + def get_sound(self) -> Sound: ... + def get_queue(self) -> Sound: ... + def set_endevent(self, type: Union[int, Event] = 0) -> None: ... + def get_endevent(self) -> int: ... + +SoundType = Sound +ChannelType = Channel diff --git a/.venv/Lib/site-packages/pygame/mixer_music.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/mixer_music.cp311-win_amd64.pyd new file mode 100644 index 00000000..0494cab5 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/mixer_music.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/mixer_music.pyi b/.venv/Lib/site-packages/pygame/mixer_music.pyi new file mode 100644 index 00000000..0c0f1983 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/mixer_music.pyi @@ -0,0 +1,20 @@ +from typing import Optional + +from ._common import FileArg + +def load(filename: FileArg, namehint: Optional[str] = "") -> None: ... +def unload() -> None: ... +def play(loops: int = 0, start: float = 0.0, fade_ms: int = 0) -> None: ... +def rewind() -> None: ... +def stop() -> None: ... +def pause() -> None: ... +def unpause() -> None: ... +def fadeout(time: int) -> None: ... +def set_volume(volume: float) -> None: ... +def get_volume() -> float: ... +def get_busy() -> bool: ... +def set_pos(pos: float) -> None: ... +def get_pos() -> int: ... +def queue(filename: FileArg, namehint: str = "", loops: int = 0) -> None: ... +def set_endevent(event_type: int) -> None: ... +def get_endevent() -> int: ... diff --git a/.venv/Lib/site-packages/pygame/mouse.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/mouse.cp311-win_amd64.pyd new file mode 100644 index 00000000..8ab23bfa Binary files /dev/null and b/.venv/Lib/site-packages/pygame/mouse.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/mouse.pyi b/.venv/Lib/site-packages/pygame/mouse.pyi new file mode 100644 index 00000000..088f2ea2 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/mouse.pyi @@ -0,0 +1,35 @@ +from typing import Sequence, Tuple, Union, overload +from typing_extensions import Literal +from pygame.cursors import Cursor +from pygame.surface import Surface + +@overload +def get_pressed(num_buttons: Literal[3] = 3) -> Tuple[bool, bool, bool]: ... +@overload +def get_pressed(num_buttons: Literal[5]) -> Tuple[bool, bool, bool, bool, bool]: ... +def get_pos() -> Tuple[int, int]: ... +def get_rel() -> Tuple[int, int]: ... +@overload +def set_pos(pos: Union[Sequence[float], Tuple[float, float]]) -> None: ... +@overload +def set_pos(x: float, y: float) -> None: ... +def set_visible(value: bool | Literal[0] | Literal[1]) -> int: ... +def get_visible() -> bool: ... +def get_focused() -> bool: ... +@overload +def set_cursor(cursor: Cursor) -> None: ... +@overload +def set_cursor(constant: int) -> None: ... +@overload +def set_cursor( + size: Union[Tuple[int, int], Sequence[int]], + hotspot: Union[Tuple[int, int], Sequence[int]], + xormasks: Sequence[int], + andmasks: Sequence[int], +) -> None: ... +@overload +def set_cursor( + hotspot: Union[Tuple[int, int], Sequence[int]], surface: Surface +) -> None: ... +def get_cursor() -> Cursor: ... +def set_system_cursor(cursor: int) -> None: ... diff --git a/.venv/Lib/site-packages/pygame/newbuffer.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/newbuffer.cp311-win_amd64.pyd new file mode 100644 index 00000000..82b0508a Binary files /dev/null and b/.venv/Lib/site-packages/pygame/newbuffer.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/pixelarray.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/pixelarray.cp311-win_amd64.pyd new file mode 100644 index 00000000..749902a4 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/pixelarray.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/pixelarray.pyi b/.venv/Lib/site-packages/pygame/pixelarray.pyi new file mode 100644 index 00000000..a4c3e9df --- /dev/null +++ b/.venv/Lib/site-packages/pygame/pixelarray.pyi @@ -0,0 +1,41 @@ +from typing import Any, Dict, Sequence, Tuple + +from pygame.surface import Surface + +from ._common import ColorValue + +class PixelArray: + surface: Surface + itemsize: int + ndim: int + shape: Tuple[int, ...] + strides: Tuple[int, ...] + # possibly going to be deprecated/removed soon, in which case these + # typestubs must be removed too + __array_interface__: Dict[str, Any] + __array_struct__: Any + def __init__(self, surface: Surface) -> None: ... + def __enter__(self) -> PixelArray: ... + def __exit__(self, *args, **kwargs) -> None: ... + def make_surface(self) -> Surface: ... + def replace( + self, + color: ColorValue, + repcolor: ColorValue, + distance: float = 0, + weights: Sequence[float] = (0.299, 0.587, 0.114), + ) -> None: ... + def extract( + self, + color: ColorValue, + distance: float = 0, + weights: Sequence[float] = (0.299, 0.587, 0.114), + ) -> PixelArray: ... + def compare( + self, + array: PixelArray, + distance: float = 0, + weights: Sequence[float] = (0.299, 0.587, 0.114), + ) -> PixelArray: ... + def transpose(self) -> PixelArray: ... + def close(self) -> PixelArray: ... diff --git a/.venv/Lib/site-packages/pygame/pixelcopy.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/pixelcopy.cp311-win_amd64.pyd new file mode 100644 index 00000000..03a8e406 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/pixelcopy.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/pixelcopy.pyi b/.venv/Lib/site-packages/pygame/pixelcopy.pyi new file mode 100644 index 00000000..8bcbe89c --- /dev/null +++ b/.venv/Lib/site-packages/pygame/pixelcopy.pyi @@ -0,0 +1,20 @@ +import numpy + +from pygame.surface import Surface + +from ._common import Literal + +_kind = Literal["P", "p", "R", "r", "G", "g", "B", "b", "A", "a", "C", "c"] + +def surface_to_array( + array: numpy.ndarray, + surface: Surface, + kind: _kind = "P", + opaque: int = 255, + clear: int = 0, +) -> None: ... +def array_to_surface(surface: Surface, array: numpy.ndarray) -> None: ... +def map_array( + array1: numpy.ndarray, array2: numpy.ndarray, surface: Surface +) -> None: ... +def make_surface(array: numpy.ndarray) -> Surface: ... diff --git a/.venv/Lib/site-packages/pygame/pkgdata.py b/.venv/Lib/site-packages/pygame/pkgdata.py new file mode 100644 index 00000000..767cbdf7 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/pkgdata.py @@ -0,0 +1,77 @@ +""" +pkgdata is a simple, extensible way for a package to acquire data file +resources. + +The getResource function is equivalent to the standard idioms, such as +the following minimal implementation: + + import sys, os + + def getResource(identifier, pkgname=__name__): + pkgpath = os.path.dirname(sys.modules[pkgname].__file__) + path = os.path.join(pkgpath, identifier) + return file(os.path.normpath(path), mode='rb') + +When a __loader__ is present on the module given by __name__, it will defer +getResource to its get_data implementation and return it as a file-like +object (such as StringIO). +""" + +__all__ = ["getResource"] +import sys +import os + +try: + from pkg_resources import resource_stream, resource_exists +except ImportError: + + def resource_exists(_package_or_requirement, _resource_name): + """ + A stub for when we fail to import this function. + + :return: Always returns False + """ + return False + + def resource_stream(_package_of_requirement, _resource_name): + """ + A stub for when we fail to import this function. + + Always raises a NotImplementedError when called. + """ + raise NotImplementedError + + +def getResource(identifier, pkgname=__name__): + """ + Acquire a readable object for a given package name and identifier. + An IOError will be raised if the resource can not be found. + + For example: + mydata = getResource('mypkgdata.jpg').read() + + Note that the package name must be fully qualified, if given, such + that it would be found in sys.modules. + + In some cases, getResource will return a real file object. In that + case, it may be useful to use its name attribute to get the path + rather than use it as a file-like object. For example, you may + be handing data off to a C API. + """ + + # When pyinstaller (or similar tools) are used, resource_exists may raise + # NotImplemented error + try: + if resource_exists(pkgname, identifier): + return resource_stream(pkgname, identifier) + except NotImplementedError: + pass + + mod = sys.modules[pkgname] + path_to_file = getattr(mod, "__file__", None) + if path_to_file is None: + raise OSError(f"{repr(mod)} has no __file__!") + path = os.path.join(os.path.dirname(path_to_file), identifier) + + # pylint: disable=consider-using-with + return open(os.path.normpath(path), "rb") diff --git a/.venv/Lib/site-packages/pygame/portmidi.dll b/.venv/Lib/site-packages/pygame/portmidi.dll new file mode 100644 index 00000000..cc4361c3 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/portmidi.dll differ diff --git a/.venv/Lib/site-packages/pygame/py.typed b/.venv/Lib/site-packages/pygame/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/.venv/Lib/site-packages/pygame/pygame.ico b/.venv/Lib/site-packages/pygame/pygame.ico new file mode 100644 index 00000000..06f699e0 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/pygame.ico differ diff --git a/.venv/Lib/site-packages/pygame/pygame_icon.bmp b/.venv/Lib/site-packages/pygame/pygame_icon.bmp new file mode 100644 index 00000000..74aea77d Binary files /dev/null and b/.venv/Lib/site-packages/pygame/pygame_icon.bmp differ diff --git a/.venv/Lib/site-packages/pygame/pygame_icon.icns b/.venv/Lib/site-packages/pygame/pygame_icon.icns new file mode 100644 index 00000000..44a67bbd Binary files /dev/null and b/.venv/Lib/site-packages/pygame/pygame_icon.icns differ diff --git a/.venv/Lib/site-packages/pygame/pygame_icon_mac.bmp b/.venv/Lib/site-packages/pygame/pygame_icon_mac.bmp new file mode 100644 index 00000000..7b58bb10 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/pygame_icon_mac.bmp differ diff --git a/.venv/Lib/site-packages/pygame/pypm.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/pypm.cp311-win_amd64.pyd new file mode 100644 index 00000000..3ce7b196 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/pypm.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/rect.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/rect.cp311-win_amd64.pyd new file mode 100644 index 00000000..40e1c7bd Binary files /dev/null and b/.venv/Lib/site-packages/pygame/rect.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/rect.pyi b/.venv/Lib/site-packages/pygame/rect.pyi new file mode 100644 index 00000000..52f79cc3 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/rect.pyi @@ -0,0 +1,208 @@ +import sys +from typing import ( + Dict, + Iterator, + List, + Sequence, + Tuple, + TypeVar, + Union, + overload, + Callable, + Any, + Optional, +) + +from ._common import Coordinate, Literal, RectValue + +if sys.version_info >= (3, 9): + from collections.abc import Collection +else: + from typing import Collection + +_K = TypeVar("_K") +_V = TypeVar("_V") +_T = TypeVar("_T") + +# Rect confirms to the Collection ABC, since it also confirms to +# Sized, Iterable and Container ABCs +class Rect(Collection[int]): + x: int + y: int + top: int + left: int + bottom: int + right: int + topleft: Tuple[int, int] + bottomleft: Tuple[int, int] + topright: Tuple[int, int] + bottomright: Tuple[int, int] + midtop: Tuple[int, int] + midleft: Tuple[int, int] + midbottom: Tuple[int, int] + midright: Tuple[int, int] + center: Tuple[int, int] + centerx: int + centery: int + size: Tuple[int, int] + width: int + height: int + w: int + h: int + __hash__: None # type: ignore + __safe_for_unpickling__: Literal[True] + @overload + def __init__( + self, left: float, top: float, width: float, height: float + ) -> None: ... + @overload + def __init__(self, left_top: Coordinate, width_height: Coordinate) -> None: ... + @overload + def __init__(self, single_arg: RectValue) -> None: ... + def __len__(self) -> Literal[4]: ... + def __iter__(self) -> Iterator[int]: ... + @overload + def __getitem__(self, i: int) -> int: ... + @overload + def __getitem__(self, s: slice) -> List[int]: ... + @overload + def __setitem__(self, key: int, value: int) -> None: ... + @overload + def __setitem__(self, key: slice, value: Union[int, Rect]) -> None: ... + def __copy__(self) -> Rect: ... + copy = __copy__ + @overload + def move(self, x: float, y: float) -> Rect: ... + @overload + def move(self, move_by: Coordinate) -> Rect: ... + @overload + def move_ip(self, x: float, y: float) -> None: ... + @overload + def move_ip(self, move_by: Coordinate) -> None: ... + @overload + def inflate(self, x: float, y: float) -> Rect: ... + @overload + def inflate(self, inflate_by: Coordinate) -> Rect: ... + @overload + def inflate_ip(self, x: float, y: float) -> None: ... + @overload + def inflate_ip(self, inflate_by: Coordinate) -> None: ... + @overload + def scale_by(self, x: float, y: float) -> Rect: ... + @overload + def scale_by(self, scale_by: Coordinate) -> Rect: ... + @overload + def scale_by_ip(self, x: float, y: float) -> None: ... + @overload + def scale_by_ip(self, scale_by: Coordinate) -> None: ... + @overload + def update(self, left: float, top: float, width: float, height: float) -> None: ... + @overload + def update(self, left_top: Coordinate, width_height: Coordinate) -> None: ... + @overload + def update(self, single_arg: RectValue) -> None: ... + @overload + def clamp(self, rect: RectValue) -> Rect: ... + @overload + def clamp(self, left_top: Coordinate, width_height: Coordinate) -> Rect: ... + @overload + def clamp(self, left: float, top: float, width: float, height: float) -> Rect: ... + @overload + def clamp_ip(self, rect: RectValue) -> None: ... + @overload + def clamp_ip(self, left_top: Coordinate, width_height: Coordinate) -> None: ... + @overload + def clamp_ip( + self, left: float, top: float, width: float, height: float + ) -> None: ... + @overload + def clip(self, rect: RectValue) -> Rect: ... + @overload + def clip(self, left_top: Coordinate, width_height: Coordinate) -> Rect: ... + @overload + def clip(self, left: float, top: float, width: float, height: float) -> Rect: ... + @overload + def clipline( + self, x1: float, x2: float, x3: float, x4: float + ) -> Union[Tuple[Tuple[int, int], Tuple[int, int]], Tuple[()]]: ... + @overload + def clipline( + self, first_coordinate: Coordinate, second_coordinate: Coordinate + ) -> Union[Tuple[Tuple[int, int], Tuple[int, int]], Tuple[()]]: ... + @overload + def clipline( + self, rect_arg: RectValue + ) -> Union[Tuple[Tuple[int, int], Tuple[int, int]], Tuple[()]]: ... + @overload + def union(self, rect: RectValue) -> Rect: ... + @overload + def union(self, left_top: Coordinate, width_height: Coordinate) -> Rect: ... + @overload + def union(self, left: float, top: float, width: float, height: float) -> Rect: ... + @overload + def union_ip(self, rect: RectValue) -> None: ... + @overload + def union_ip(self, left_top: Coordinate, width_height: Coordinate) -> None: ... + @overload + def union_ip( + self, left: float, top: float, width: float, height: float + ) -> None: ... + def unionall(self, rect: Sequence[RectValue]) -> Rect: ... + def unionall_ip(self, rects: Sequence[RectValue]) -> None: ... + @overload + def fit(self, rect: RectValue) -> Rect: ... + @overload + def fit(self, left_top: Coordinate, width_height: Coordinate) -> Rect: ... + @overload + def fit(self, left: float, top: float, width: float, height: float) -> Rect: ... + def normalize(self) -> None: ... + def __contains__(self, rect: Union[RectValue, int]) -> bool: ... # type: ignore[override] + @overload + def contains(self, rect: RectValue) -> bool: ... + @overload + def contains(self, left_top: Coordinate, width_height: Coordinate) -> bool: ... + @overload + def contains( + self, left: float, top: float, width: float, height: float + ) -> bool: ... + @overload + def collidepoint(self, x: float, y: float) -> bool: ... + @overload + def collidepoint(self, x_y: Coordinate) -> bool: ... + @overload + def colliderect(self, rect: RectValue) -> bool: ... + @overload + def colliderect(self, left_top: Coordinate, width_height: Coordinate) -> bool: ... + @overload + def colliderect( + self, left: float, top: float, width: float, height: float + ) -> bool: ... + def collidelist(self, rects: Sequence[RectValue]) -> int: ... + def collidelistall(self, rects: Sequence[RectValue]) -> List[int]: ... + def collideobjectsall( + self, objects: Sequence[_T], key: Optional[Callable[[_T], RectValue]] = None + ) -> List[_T]: ... + def collideobjects( + self, objects: Sequence[_T], key: Optional[Callable[[_T], RectValue]] = None + ) -> Optional[_T]: ... + # Also undocumented: the dict collision methods take a 'values' argument + # that defaults to False. If it is False, the keys in rect_dict must be + # Rect-like; otherwise, the values must be Rects. + @overload + def collidedict( + self, rect_dict: Dict[RectValue, _V], values: bool = ... + ) -> Tuple[RectValue, _V]: ... + @overload + def collidedict( + self, rect_dict: Dict[_K, "Rect"], values: bool + ) -> Tuple[_K, "Rect"]: ... + @overload + def collidedictall( + self, rect_dict: Dict[RectValue, _V], values: bool = ... + ) -> List[Tuple[RectValue, _V]]: ... + @overload + def collidedictall( + self, rect_dict: Dict[_K, "Rect"], values: bool + ) -> List[Tuple[_K, "Rect"]]: ... + +RectType = Rect diff --git a/.venv/Lib/site-packages/pygame/rwobject.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/rwobject.cp311-win_amd64.pyd new file mode 100644 index 00000000..b3b771e6 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/rwobject.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/rwobject.pyi b/.venv/Lib/site-packages/pygame/rwobject.pyi new file mode 100644 index 00000000..3688279b --- /dev/null +++ b/.venv/Lib/site-packages/pygame/rwobject.pyi @@ -0,0 +1,18 @@ +from typing import Any, Optional, overload, Type + +from ._common import AnyPath + +def encode_string( + obj: Optional[AnyPath], + encoding: Optional[str] = "unicode_escape", + errors: Optional[str] = "backslashreplace", + etype: Optional[Type[Exception]] = UnicodeEncodeError, +) -> bytes: ... +@overload +def encode_file_path( + obj: Optional[AnyPath], etype: Optional[Type[Exception]] = UnicodeEncodeError +) -> bytes: ... +@overload +def encode_file_path( + obj: Any, etype: Optional[Type[Exception]] = UnicodeEncodeError +) -> bytes: ... diff --git a/.venv/Lib/site-packages/pygame/scrap.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/scrap.cp311-win_amd64.pyd new file mode 100644 index 00000000..42c1fd9e Binary files /dev/null and b/.venv/Lib/site-packages/pygame/scrap.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/scrap.pyi b/.venv/Lib/site-packages/pygame/scrap.pyi new file mode 100644 index 00000000..762c36fb --- /dev/null +++ b/.venv/Lib/site-packages/pygame/scrap.pyi @@ -0,0 +1,11 @@ +from typing import List, Optional +from collections.abc import ByteString + +def init() -> None: ... +def get_init() -> bool: ... +def get(data_type: str) -> Optional[bytes]: ... +def get_types() -> List[str]: ... +def put(data_type: str, data: ByteString) -> None: ... +def contains(data_type: str) -> bool: ... +def lost() -> bool: ... +def set_mode(mode: int) -> None: ... diff --git a/.venv/Lib/site-packages/pygame/sndarray.py b/.venv/Lib/site-packages/pygame/sndarray.py new file mode 100644 index 00000000..99ac4c7f --- /dev/null +++ b/.venv/Lib/site-packages/pygame/sndarray.py @@ -0,0 +1,139 @@ +## pygame - Python Game Library +## Copyright (C) 2008 Marcus von Appen +## +## This library is free software; you can redistribute it and/or +## modify it under the terms of the GNU Library General Public +## License as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## Library General Public License for more details. +## +## You should have received a copy of the GNU Library General Public +## License along with this library; if not, write to the Free +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## +## Marcus von Appen +## mva@sysfault.org + +"""pygame module for accessing sound sample data + +Functions to convert between NumPy arrays and Sound objects. This module +will only be functional when pygame can use the external NumPy package. +If NumPy can't be imported, surfarray becomes a MissingModule object. + +Sound data is made of thousands of samples per second, and each sample +is the amplitude of the wave at a particular moment in time. For +example, in 22-kHz format, element number 5 of the array is the +amplitude of the wave after 5/22000 seconds. + +Each sample is an 8-bit or 16-bit integer, depending on the data format. +A stereo sound file has two values per sample, while a mono sound file +only has one. + +Sounds with 16-bit data will be treated as unsigned integers, +if the sound sample type requests this. +""" + +from pygame import mixer +import numpy + +import warnings + + +__all__ = [ + "array", + "samples", + "make_sound", + "use_arraytype", + "get_arraytype", + "get_arraytypes", +] + + +def array(sound): + """pygame.sndarray.array(Sound): return array + + Copy Sound samples into an array. + + Creates a new array for the sound data and copies the samples. The + array will always be in the format returned from + pygame.mixer.get_init(). + """ + + return numpy.array(sound, copy=True) + + +def samples(sound): + """pygame.sndarray.samples(Sound): return array + + Reference Sound samples into an array. + + Creates a new array that directly references the samples in a Sound + object. Modifying the array will change the Sound. The array will + always be in the format returned from pygame.mixer.get_init(). + """ + + return numpy.array(sound, copy=False) + + +def make_sound(array): + """pygame.sndarray.make_sound(array): return Sound + + Convert an array into a Sound object. + + Create a new playable Sound object from an array. The mixer module + must be initialized and the array format must be similar to the mixer + audio format. + """ + + return mixer.Sound(array=array) + + +def use_arraytype(arraytype): + """pygame.sndarray.use_arraytype(arraytype): return None + + DEPRECATED - only numpy arrays are now supported. + """ + warnings.warn( + DeprecationWarning( + "only numpy arrays are now supported, " + "this function will be removed in a " + "future version of the module" + ) + ) + arraytype = arraytype.lower() + if arraytype != "numpy": + raise ValueError("invalid array type") + + +def get_arraytype(): + """pygame.sndarray.get_arraytype(): return str + + DEPRECATED - only numpy arrays are now supported. + """ + warnings.warn( + DeprecationWarning( + "only numpy arrays are now supported, " + "this function will be removed in a " + "future version of the module" + ) + ) + return "numpy" + + +def get_arraytypes(): + """pygame.sndarray.get_arraytypes(): return tuple + + DEPRECATED - only numpy arrays are now supported. + """ + warnings.warn( + DeprecationWarning( + "only numpy arrays are now supported, " + "this function will be removed in a " + "future version of the module" + ) + ) + return ("numpy",) diff --git a/.venv/Lib/site-packages/pygame/sndarray.pyi b/.venv/Lib/site-packages/pygame/sndarray.pyi new file mode 100644 index 00000000..8b0dd653 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/sndarray.pyi @@ -0,0 +1,12 @@ +from typing import Tuple + +import numpy + +from pygame.mixer import Sound + +def array(sound: Sound) -> numpy.ndarray: ... +def samples(sound: Sound) -> numpy.ndarray: ... +def make_sound(array: numpy.ndarray) -> Sound: ... +def use_arraytype(arraytype: str) -> Sound: ... +def get_arraytype() -> str: ... +def get_arraytypes() -> Tuple[str]: ... diff --git a/.venv/Lib/site-packages/pygame/sprite.py b/.venv/Lib/site-packages/pygame/sprite.py new file mode 100644 index 00000000..b268759b --- /dev/null +++ b/.venv/Lib/site-packages/pygame/sprite.py @@ -0,0 +1,1812 @@ +# pygame - Python Game Library +# Copyright (C) 2000-2003, 2007 Pete Shinners +# (C) 2004 Joe Wreschnig +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Pete Shinners +# pete@shinners.org + +"""pygame module with basic game object classes + +This module contains several simple classes to be used within games. There +are the main Sprite class and several Group classes that contain Sprites. +The use of these classes is entirely optional when using Pygame. The classes +are fairly lightweight and only provide a starting place for the code +that is common to most games. + +The Sprite class is intended to be used as a base class for the different +types of objects in the game. There is also a base Group class that simply +stores sprites. A game could create new types of Group classes that operate +on specially customized Sprite instances they contain. + +The basic Sprite class can draw the Sprites it contains to a Surface. The +Group.draw() method requires that each Sprite have a Surface.image attribute +and a Surface.rect. The Group.clear() method requires these same attributes +and can be used to erase all the Sprites with background. There are also +more advanced Groups: pygame.sprite.RenderUpdates() and +pygame.sprite.OrderedUpdates(). + +Lastly, this module contains several collision functions. These help find +sprites inside multiple groups that have intersecting bounding rectangles. +To find the collisions, the Sprites are required to have a Surface.rect +attribute assigned. + +The groups are designed for high efficiency in removing and adding Sprites +to them. They also allow cheap testing to see if a Sprite already exists in +a Group. A given Sprite can exist in any number of groups. A game could use +some groups to control object rendering, and a completely separate set of +groups to control interaction or player movement. Instead of adding type +attributes or bools to a derived Sprite class, consider keeping the +Sprites inside organized Groups. This will allow for easier lookup later +in the game. + +Sprites and Groups manage their relationships with the add() and remove() +methods. These methods can accept a single or multiple group arguments for +membership. The default initializers for these classes also take a +single group or list of groups as arguments for initial membership. It is safe +to repeatedly add and remove the same Sprite from a Group. + +While it is possible to design sprite and group classes that don't derive +from the Sprite and AbstractGroup classes below, it is strongly recommended +that you extend those when you create a new Sprite or Group class. + +Sprites are not thread safe, so lock them yourself if using threads. + +""" + +# TODO: a group that holds only the 'n' most recent elements. +# sort of like the GroupSingle class, but holding more +# than one sprite +# +# drawing groups that can 'automatically' store the area +# underneath so they can "clear" without needing a background +# function. obviously a little slower than normal, but nice +# to use in many situations. (also remember it must "clear" +# in the reverse order that it draws :]) +# +# the drawing groups should also be able to take a background +# function, instead of just a background surface. the function +# would take a surface and a rectangle on that surface to erase. +# +# perhaps more types of collision functions? the current two +# should handle just about every need, but perhaps more optimized +# specific ones that aren't quite so general but fit into common +# specialized cases. + +from weakref import WeakSet +from warnings import warn + +import pygame + +from pygame.rect import Rect +from pygame.time import get_ticks +from pygame.mask import from_surface + + +class Sprite: + """simple base class for visible game objects + + pygame.sprite.Sprite(*groups): return Sprite + + The base class for visible game objects. Derived classes will want to + override the Sprite.update() method and assign Sprite.image and Sprite.rect + attributes. The initializer can accept any number of Group instances that + the Sprite will become a member of. + + When subclassing the Sprite class, be sure to call the base initializer + before adding the Sprite to Groups. + + """ + + def __init__(self, *groups): + self.__g = set() # The groups the sprite is in + if groups: + self.add(*groups) + + def add(self, *groups): + """add the sprite to groups + + Sprite.add(*groups): return None + + Any number of Group instances can be passed as arguments. The + Sprite will be added to the Groups it is not already a member of. + + """ + has = self.__g.__contains__ + for group in groups: + if hasattr(group, "_spritegroup"): + if not has(group): + group.add_internal(self) + self.add_internal(group) + else: + self.add(*group) + + def remove(self, *groups): + """remove the sprite from groups + + Sprite.remove(*groups): return None + + Any number of Group instances can be passed as arguments. The Sprite + will be removed from the Groups it is currently a member of. + + """ + has = self.__g.__contains__ + for group in groups: + if hasattr(group, "_spritegroup"): + if has(group): + group.remove_internal(self) + self.remove_internal(group) + else: + self.remove(*group) + + def add_internal(self, group): + """ + For adding this sprite to a group internally. + + :param group: The group we are adding to. + """ + self.__g.add(group) + + def remove_internal(self, group): + """ + For removing this sprite from a group internally. + + :param group: The group we are removing from. + """ + self.__g.remove(group) + + def update(self, *args, **kwargs): + """method to control sprite behavior + + Sprite.update(*args, **kwargs): + + The default implementation of this method does nothing; it's just a + convenient "hook" that you can override. This method is called by + Group.update() with whatever arguments you give it. + + There is no need to use this method if not using the convenience + method by the same name in the Group class. + + """ + + def kill(self): + """remove the Sprite from all Groups + + Sprite.kill(): return None + + The Sprite is removed from all the Groups that contain it. This won't + change anything about the state of the Sprite. It is possible to + continue to use the Sprite after this method has been called, including + adding it to Groups. + + """ + for group in self.__g: + group.remove_internal(self) + self.__g.clear() + + def groups(self): + """list of Groups that contain this Sprite + + Sprite.groups(): return group_list + + Returns a list of all the Groups that contain this Sprite. + + """ + return list(self.__g) + + def alive(self): + """does the sprite belong to any groups + + Sprite.alive(): return bool + + Returns True when the Sprite belongs to one or more Groups. + """ + return bool(self.__g) + + def __repr__(self): + return f"<{self.__class__.__name__} Sprite(in {len(self.__g)} groups)>" + + @property + def layer(self): + """ + Dynamic, read only property for protected _layer attribute. + This will get the _layer variable if it exists. + + If you try to get it before it is set it will raise an attribute error. + + Layer property can only be set before the sprite is added to a group, + after that it is read only and a sprite's layer in a group should be + set via the group's change_layer() method. + + :return: layer as an int, or raise AttributeError. + """ + return self._layer + + @layer.setter + def layer(self, value): + if not self.alive(): + self._layer = value + else: + raise AttributeError( + "Can't set layer directly after " + "adding to group. Use " + "group.change_layer(sprite, new_layer) " + "instead." + ) + + +class WeakSprite(Sprite): + """A subclass of Sprite that references its Groups weakly. This + means that any group this belongs to that is not referenced anywhere + else is garbage collected automatically. + """ + + def __init__(self, *groups): + super().__init__(*groups) + self.__dict__["_Sprite__g"] = WeakSet(self._Sprite__g) + + +class DirtySprite(Sprite): + """a more featureful subclass of Sprite with more attributes + + pygame.sprite.DirtySprite(*groups): return DirtySprite + + Extra DirtySprite attributes with their default values: + + dirty = 1 + If set to 1, it is repainted and then set to 0 again. + If set to 2, it is always dirty (repainted each frame; + flag is not reset). + If set to 0, it is not dirty and therefore not repainted again. + + blendmode = 0 + It's the special_flags argument of Surface.blit; see the blendmodes in + the Surface.blit documentation + + source_rect = None + This is the source rect to use. Remember that it is relative to the top + left corner (0, 0) of self.image. + + visible = 1 + Normally this is 1. If set to 0, it will not be repainted. (If you + change visible to 1, you must set dirty to 1 for it to be erased from + the screen.) + + _layer = 0 + 0 is the default value but this is able to be set differently + when subclassing. + + """ + + def __init__(self, *groups): + self.dirty = 1 + + # referred to as special_flags in the documentation of Surface.blit + self.blendmode = 0 + self._visible = 1 + + # Default 0 unless initialized differently. + self._layer = getattr(self, "_layer", 0) + self.source_rect = None + Sprite.__init__(self, *groups) + + def _set_visible(self, val): + """set the visible value (0 or 1) and makes the sprite dirty""" + self._visible = val + if self.dirty < 2: + self.dirty = 1 + + def _get_visible(self): + """return the visible value of that sprite""" + return self._visible + + @property + def visible(self): + """ + You can make this sprite disappear without removing it from the group + assign 0 for invisible and 1 for visible + """ + return self._get_visible() + + @visible.setter + def visible(self, value): + self._set_visible(value) + + @property + def layer(self): + """ + Layer property can only be set before the sprite is added to a group, + after that it is read only and a sprite's layer in a group should be + set via the group's change_layer() method. + + Overwrites dynamic property from sprite class for speed. + """ + return self._layer + + @layer.setter + def layer(self, value): + if not self.alive(): + self._layer = value + else: + raise AttributeError( + "Can't set layer directly after " + "adding to group. Use " + "group.change_layer(sprite, new_layer) " + "instead." + ) + + def __repr__(self): + return ( + f"<{self.__class__.__name__} DirtySprite(in {len(self.groups())} groups)>" + ) + + +class WeakDirtySprite(WeakSprite, DirtySprite): + """A subclass of WeakSprite and DirtySprite that combines the benefits + of both classes. + """ + + +class AbstractGroup: + """base class for containers of sprites + + AbstractGroup does everything needed to behave as a normal group. You can + easily subclass a new group class from this or the other groups below if + you want to add more features. + + Any AbstractGroup-derived sprite groups act like sequences and support + iteration, len, and so on. + + """ + + # dummy val to identify sprite groups, and avoid infinite recursion + _spritegroup = True + + def __init__(self): + self.spritedict = {} + self.lostsprites = [] + + def sprites(self): + """get a list of sprites in the group + + Group.sprites(): return list + + Returns an object that can be looped over with a 'for' loop. (For now, + it is always a list, but this could change in a future version of + pygame.) Alternatively, you can get the same information by iterating + directly over the sprite group, e.g. 'for sprite in group'. + + """ + return list(self.spritedict) + + def add_internal( + self, + sprite, + layer=None, # noqa pylint: disable=unused-argument; supporting legacy derived classes that override in non-pythonic way + ): + """ + For adding a sprite to this group internally. + + :param sprite: The sprite we are adding. + :param layer: the layer to add to, if the group type supports layers + """ + self.spritedict[sprite] = None + + def remove_internal(self, sprite): + """ + For removing a sprite from this group internally. + + :param sprite: The sprite we are removing. + """ + lost_rect = self.spritedict[sprite] + if lost_rect: + self.lostsprites.append(lost_rect) + del self.spritedict[sprite] + + def has_internal(self, sprite): + """ + For checking if a sprite is in this group internally. + + :param sprite: The sprite we are checking. + """ + return sprite in self.spritedict + + def copy(self): + """copy a group with all the same sprites + + Group.copy(): return Group + + Returns a copy of the group that is an instance of the same class + and has the same sprites in it. + + """ + return self.__class__( # noqa pylint: disable=too-many-function-args + self.sprites() # Needed because copy() won't work on AbstractGroup + ) + + def __iter__(self): + return iter(self.sprites()) + + def __contains__(self, sprite): + return self.has(sprite) + + def add(self, *sprites): + """add sprite(s) to group + + Group.add(sprite, list, group, ...): return None + + Adds a sprite or sequence of sprites to a group. + + """ + for sprite in sprites: + # It's possible that some sprite is also an iterator. + # If this is the case, we should add the sprite itself, + # and not the iterator object. + if isinstance(sprite, Sprite): + if not self.has_internal(sprite): + self.add_internal(sprite) + sprite.add_internal(self) + else: + try: + # See if sprite is an iterator, like a list or sprite + # group. + self.add(*sprite) + except (TypeError, AttributeError): + # Not iterable. This is probably a sprite that is not an + # instance of the Sprite class or is not an instance of a + # subclass of the Sprite class. Alternately, it could be an + # old-style sprite group. + if hasattr(sprite, "_spritegroup"): + for spr in sprite.sprites(): + if not self.has_internal(spr): + self.add_internal(spr) + spr.add_internal(self) + elif not self.has_internal(sprite): + self.add_internal(sprite) + sprite.add_internal(self) + + def remove(self, *sprites): + """remove sprite(s) from group + + Group.remove(sprite, list, or group, ...): return None + + Removes a sprite or sequence of sprites from a group. + + """ + # This function behaves essentially the same as Group.add. It first + # tries to handle each argument as an instance of the Sprite class. If + # that fails, then it tries to handle the argument as an iterable + # object. If that fails, then it tries to handle the argument as an + # old-style sprite group. Lastly, if that fails, it assumes that the + # normal Sprite methods should be used. + for sprite in sprites: + if isinstance(sprite, Sprite): + if self.has_internal(sprite): + self.remove_internal(sprite) + sprite.remove_internal(self) + else: + try: + self.remove(*sprite) + except (TypeError, AttributeError): + if hasattr(sprite, "_spritegroup"): + for spr in sprite.sprites(): + if self.has_internal(spr): + self.remove_internal(spr) + spr.remove_internal(self) + elif self.has_internal(sprite): + self.remove_internal(sprite) + sprite.remove_internal(self) + + def has(self, *sprites): + """ask if group has a sprite or sprites + + Group.has(sprite or group, ...): return bool + + Returns True if the given sprite or sprites are contained in the + group. Alternatively, you can get the same information using the + 'in' operator, e.g. 'sprite in group', 'subgroup in group'. + + """ + if not sprites: + return False # return False if no sprites passed in + + for sprite in sprites: + if isinstance(sprite, Sprite): + # Check for Sprite instance's membership in this group + if not self.has_internal(sprite): + return False + else: + try: + if not self.has(*sprite): + return False + except (TypeError, AttributeError): + if hasattr(sprite, "_spritegroup"): + for spr in sprite.sprites(): + if not self.has_internal(spr): + return False + else: + if not self.has_internal(sprite): + return False + + return True + + def update(self, *args, **kwargs): + """call the update method of every member sprite + + Group.update(*args, **kwargs): return None + + Calls the update method of every member sprite. All arguments that + were passed to this method are passed to the Sprite update function. + + """ + for sprite in self.sprites(): + sprite.update(*args, **kwargs) + + def draw( + self, surface, bgsurf=None, special_flags=0 + ): # noqa pylint: disable=unused-argument; bgsurf arg used in LayeredDirty + """draw all sprites onto the surface + + Group.draw(surface, special_flags=0): return Rect_list + + Draws all of the member sprites onto the given surface. + + """ + sprites = self.sprites() + if hasattr(surface, "blits"): + self.spritedict.update( + zip( + sprites, + surface.blits( + (spr.image, spr.rect, None, special_flags) for spr in sprites + ), + ) + ) + else: + for spr in sprites: + self.spritedict[spr] = surface.blit( + spr.image, spr.rect, None, special_flags + ) + self.lostsprites = [] + dirty = self.lostsprites + + return dirty + + def clear(self, surface, bgd): + """erase the previous position of all sprites + + Group.clear(surface, bgd): return None + + Clears the area under every drawn sprite in the group. The bgd + argument should be Surface which is the same dimensions as the + screen surface. The bgd could also be a function which accepts + the given surface and the area to be cleared as arguments. + + """ + if callable(bgd): + for lost_clear_rect in self.lostsprites: + bgd(surface, lost_clear_rect) + for clear_rect in self.spritedict.values(): + if clear_rect: + bgd(surface, clear_rect) + else: + surface_blit = surface.blit + for lost_clear_rect in self.lostsprites: + surface_blit(bgd, lost_clear_rect, lost_clear_rect) + for clear_rect in self.spritedict.values(): + if clear_rect: + surface_blit(bgd, clear_rect, clear_rect) + + def empty(self): + """remove all sprites + + Group.empty(): return None + + Removes all the sprites from the group. + + """ + for sprite in self.sprites(): + self.remove_internal(sprite) + sprite.remove_internal(self) + + def __bool__(self): + return bool(self.sprites()) + + def __len__(self): + """return number of sprites in group + + Group.len(group): return int + + Returns the number of sprites contained in the group. + + """ + return len(self.sprites()) + + def __repr__(self): + return f"<{self.__class__.__name__}({len(self)} sprites)>" + + +class Group(AbstractGroup): + """container class for many Sprites + + pygame.sprite.Group(*sprites): return Group + + A simple container for Sprite objects. This class can be subclassed to + create containers with more specific behaviors. The constructor takes any + number of Sprite arguments to add to the Group. The group supports the + following standard Python operations: + + in test if a Sprite is contained + len the number of Sprites contained + bool test if any Sprites are contained + iter iterate through all the Sprites + + The Sprites in the Group are not ordered, so the Sprites are drawn and + iterated over in no particular order. + + """ + + def __init__(self, *sprites): + AbstractGroup.__init__(self) + self.add(*sprites) + + +RenderPlain = Group +RenderClear = Group + + +class RenderUpdates(Group): + """Group class that tracks dirty updates + + pygame.sprite.RenderUpdates(*sprites): return RenderUpdates + + This class is derived from pygame.sprite.Group(). It has an enhanced draw + method that tracks the changed areas of the screen. + + """ + + def draw(self, surface, bgsurf=None, special_flags=0): + surface_blit = surface.blit + dirty = self.lostsprites + self.lostsprites = [] + dirty_append = dirty.append + for sprite in self.sprites(): + old_rect = self.spritedict[sprite] + new_rect = surface_blit(sprite.image, sprite.rect, None, special_flags) + if old_rect: + if new_rect.colliderect(old_rect): + dirty_append(new_rect.union(old_rect)) + else: + dirty_append(new_rect) + dirty_append(old_rect) + else: + dirty_append(new_rect) + self.spritedict[sprite] = new_rect + return dirty + + +class OrderedUpdates(RenderUpdates): + """RenderUpdates class that draws Sprites in order of addition + + pygame.sprite.OrderedUpdates(*sprites): return OrderedUpdates + + This class derives from pygame.sprite.RenderUpdates(). It maintains + the order in which the Sprites were added to the Group for rendering. + This makes adding and removing Sprites from the Group a little + slower than regular Groups. + + """ + + def __init__(self, *sprites): + self._spritelist = [] + RenderUpdates.__init__(self, *sprites) + + def sprites(self): + return self._spritelist.copy() + + def add_internal(self, sprite, layer=None): + RenderUpdates.add_internal(self, sprite) + self._spritelist.append(sprite) + + def remove_internal(self, sprite): + RenderUpdates.remove_internal(self, sprite) + self._spritelist.remove(sprite) + + +class LayeredUpdates(AbstractGroup): + """LayeredUpdates Group handles layers, which are drawn like OrderedUpdates + + pygame.sprite.LayeredUpdates(*sprites, **kwargs): return LayeredUpdates + + This group is fully compatible with pygame.sprite.Sprite. + New in pygame 1.8.0 + + """ + + _init_rect = Rect(0, 0, 0, 0) + + def __init__(self, *sprites, **kwargs): + """initialize an instance of LayeredUpdates with the given attributes + + You can set the default layer through kwargs using 'default_layer' + and an integer for the layer. The default layer is 0. + + If the sprite you add has an attribute _layer, then that layer will be + used. If **kwarg contains 'layer', then the passed sprites will be + added to that layer (overriding the sprite._layer attribute). If + neither the sprite nor **kwarg has a 'layer', then the default layer is + used to add the sprites. + + """ + self._spritelayers = {} + self._spritelist = [] + AbstractGroup.__init__(self) + self._default_layer = kwargs.get("default_layer", 0) + + self.add(*sprites, **kwargs) + + def add_internal(self, sprite, layer=None): + """Do not use this method directly. + + It is used by the group to add a sprite internally. + + """ + self.spritedict[sprite] = self._init_rect + + if layer is None: + try: + layer = sprite.layer + except AttributeError: + layer = self._default_layer + setattr(sprite, "_layer", layer) + elif hasattr(sprite, "_layer"): + setattr(sprite, "_layer", layer) + + sprites = self._spritelist # speedup + sprites_layers = self._spritelayers + sprites_layers[sprite] = layer + + # add the sprite at the right position + # bisect algorithmus + leng = len(sprites) + low = mid = 0 + high = leng - 1 + while low <= high: + mid = low + (high - low) // 2 + if sprites_layers[sprites[mid]] <= layer: + low = mid + 1 + else: + high = mid - 1 + # linear search to find final position + while mid < leng and sprites_layers[sprites[mid]] <= layer: + mid += 1 + sprites.insert(mid, sprite) + + def add(self, *sprites, **kwargs): + """add a sprite or sequence of sprites to a group + + LayeredUpdates.add(*sprites, **kwargs): return None + + If the sprite you add has an attribute _layer, then that layer will be + used. If **kwarg contains 'layer', then the passed sprites will be + added to that layer (overriding the sprite._layer attribute). If + neither the sprite nor **kwarg has a 'layer', then the default layer is + used to add the sprites. + + """ + + if not sprites: + return + layer = kwargs["layer"] if "layer" in kwargs else None + for sprite in sprites: + # It's possible that some sprite is also an iterator. + # If this is the case, we should add the sprite itself, + # and not the iterator object. + if isinstance(sprite, Sprite): + if not self.has_internal(sprite): + self.add_internal(sprite, layer) + sprite.add_internal(self) + else: + try: + # See if sprite is an iterator, like a list or sprite + # group. + self.add(*sprite, **kwargs) + except (TypeError, AttributeError): + # Not iterable. This is probably a sprite that is not an + # instance of the Sprite class or is not an instance of a + # subclass of the Sprite class. Alternately, it could be an + # old-style sprite group. + if hasattr(sprite, "_spritegroup"): + for spr in sprite.sprites(): + if not self.has_internal(spr): + self.add_internal(spr, layer) + spr.add_internal(self) + elif not self.has_internal(sprite): + self.add_internal(sprite, layer) + sprite.add_internal(self) + + def remove_internal(self, sprite): + """Do not use this method directly. + + The group uses it to add a sprite. + + """ + self._spritelist.remove(sprite) + # these dirty rects are suboptimal for one frame + old_rect = self.spritedict[sprite] + if old_rect is not self._init_rect: + self.lostsprites.append(old_rect) # dirty rect + if hasattr(sprite, "rect"): + self.lostsprites.append(sprite.rect) # dirty rect + + del self.spritedict[sprite] + del self._spritelayers[sprite] + + def sprites(self): + """return a ordered list of sprites (first back, last top). + + LayeredUpdates.sprites(): return sprites + + """ + return self._spritelist.copy() + + def draw(self, surface, bgsurf=None, special_flags=0): + """draw all sprites in the right order onto the passed surface + + LayeredUpdates.draw(surface, special_flags=0): return Rect_list + + """ + spritedict = self.spritedict + surface_blit = surface.blit + dirty = self.lostsprites + self.lostsprites = [] + dirty_append = dirty.append + init_rect = self._init_rect + for spr in self.sprites(): + rec = spritedict[spr] + newrect = surface_blit(spr.image, spr.rect, None, special_flags) + if rec is init_rect: + dirty_append(newrect) + else: + if newrect.colliderect(rec): + dirty_append(newrect.union(rec)) + else: + dirty_append(newrect) + dirty_append(rec) + spritedict[spr] = newrect + return dirty + + def get_sprites_at(self, pos): + """return a list with all sprites at that position + + LayeredUpdates.get_sprites_at(pos): return colliding_sprites + + Bottom sprites are listed first; the top ones are listed last. + + """ + _sprites = self._spritelist + rect = Rect(pos, (1, 1)) + colliding_idx = rect.collidelistall(_sprites) + return [_sprites[i] for i in colliding_idx] + + def get_sprite(self, idx): + """return the sprite at the index idx from the groups sprites + + LayeredUpdates.get_sprite(idx): return sprite + + Raises IndexOutOfBounds if the idx is not within range. + + """ + return self._spritelist[idx] + + def remove_sprites_of_layer(self, layer_nr): + """remove all sprites from a layer and return them as a list + + LayeredUpdates.remove_sprites_of_layer(layer_nr): return sprites + + """ + sprites = self.get_sprites_from_layer(layer_nr) + self.remove(*sprites) + return sprites + + # layer methods + def layers(self): + """return a list of unique defined layers defined. + + LayeredUpdates.layers(): return layers + + """ + return sorted(set(self._spritelayers.values())) + + def change_layer(self, sprite, new_layer): + """change the layer of the sprite + + LayeredUpdates.change_layer(sprite, new_layer): return None + + The sprite must have been added to the renderer already. This is not + checked. + + """ + sprites = self._spritelist # speedup + sprites_layers = self._spritelayers # speedup + + sprites.remove(sprite) + sprites_layers.pop(sprite) + + # add the sprite at the right position + # bisect algorithmus + leng = len(sprites) + low = mid = 0 + high = leng - 1 + while low <= high: + mid = low + (high - low) // 2 + if sprites_layers[sprites[mid]] <= new_layer: + low = mid + 1 + else: + high = mid - 1 + # linear search to find final position + while mid < leng and sprites_layers[sprites[mid]] <= new_layer: + mid += 1 + sprites.insert(mid, sprite) + if hasattr(sprite, "_layer"): + setattr(sprite, "_layer", new_layer) + + # add layer info + sprites_layers[sprite] = new_layer + + def get_layer_of_sprite(self, sprite): + """return the layer that sprite is currently in + + If the sprite is not found, then it will return the default layer. + + """ + return self._spritelayers.get(sprite, self._default_layer) + + def get_top_layer(self): + """return the top layer + + LayeredUpdates.get_top_layer(): return layer + + """ + return self._spritelayers[self._spritelist[-1]] + + def get_bottom_layer(self): + """return the bottom layer + + LayeredUpdates.get_bottom_layer(): return layer + + """ + return self._spritelayers[self._spritelist[0]] + + def move_to_front(self, sprite): + """bring the sprite to front layer + + LayeredUpdates.move_to_front(sprite): return None + + Brings the sprite to front by changing the sprite layer to the top-most + layer. The sprite is added at the end of the list of sprites in that + top-most layer. + + """ + self.change_layer(sprite, self.get_top_layer()) + + def move_to_back(self, sprite): + """move the sprite to the bottom layer + + LayeredUpdates.move_to_back(sprite): return None + + Moves the sprite to the bottom layer by moving it to a new layer below + the current bottom layer. + + """ + self.change_layer(sprite, self.get_bottom_layer() - 1) + + def get_top_sprite(self): + """return the topmost sprite + + LayeredUpdates.get_top_sprite(): return Sprite + + """ + return self._spritelist[-1] + + def get_sprites_from_layer(self, layer): + """return all sprites from a layer ordered as they where added + + LayeredUpdates.get_sprites_from_layer(layer): return sprites + + Returns all sprites from a layer. The sprites are ordered in the + sequence that they where added. (The sprites are not removed from the + layer. + + """ + sprites = [] + sprites_append = sprites.append + sprite_layers = self._spritelayers + for spr in self._spritelist: + if sprite_layers[spr] == layer: + sprites_append(spr) + elif sprite_layers[spr] > layer: + # break after because no other will + # follow with same layer + break + return sprites + + def switch_layer(self, layer1_nr, layer2_nr): + """switch the sprites from layer1_nr to layer2_nr + + LayeredUpdates.switch_layer(layer1_nr, layer2_nr): return None + + The layers number must exist. This method does not check for the + existence of the given layers. + + """ + sprites1 = self.remove_sprites_of_layer(layer1_nr) + for spr in self.get_sprites_from_layer(layer2_nr): + self.change_layer(spr, layer1_nr) + self.add(layer=layer2_nr, *sprites1) + + +class LayeredDirty(LayeredUpdates): + """LayeredDirty Group is for DirtySprites; subclasses LayeredUpdates + + pygame.sprite.LayeredDirty(*sprites, **kwargs): return LayeredDirty + + This group requires pygame.sprite.DirtySprite or any sprite that + has the following attributes: + image, rect, dirty, visible, blendmode (see doc of DirtySprite). + + It uses the dirty flag technique and is therefore faster than + pygame.sprite.RenderUpdates if you have many static sprites. It + also switches automatically between dirty rect updating and full + screen drawing, so you do no have to worry which would be faster. + + As with the pygame.sprite.Group, you can specify some additional attributes + through kwargs: + _use_update: True/False (default is False) + _default_layer: default layer where the sprites without a layer are + added + _time_threshold: threshold time for switching between dirty rect mode + and fullscreen mode; defaults to updating at 80 frames per second, + which is equal to 1000.0 / 80.0 + + New in pygame 1.8.0 + + """ + + def __init__(self, *sprites, **kwargs): + """initialize group. + + pygame.sprite.LayeredDirty(*sprites, **kwargs): return LayeredDirty + + You can specify some additional attributes through kwargs: + _use_update: True/False (default is False) + _default_layer: default layer where the sprites without a layer are + added + _time_threshold: threshold time for switching between dirty rect + mode and fullscreen mode; defaults to updating at 80 frames per + second, which is equal to 1000.0 / 80.0 + + """ + LayeredUpdates.__init__(self, *sprites, **kwargs) + self._clip = None + + self._use_update = False + + self._time_threshold = 1000.0 / 80.0 # 1000.0 / fps + + self._bgd = None + for key, val in kwargs.items(): + if key in ["_use_update", "_time_threshold", "_default_layer"] and hasattr( + self, key + ): + setattr(self, key, val) + + def add_internal(self, sprite, layer=None): + """Do not use this method directly. + + It is used by the group to add a sprite internally. + + """ + # check if all needed attributes are set + if not hasattr(sprite, "dirty"): + raise AttributeError() + if not hasattr(sprite, "visible"): + raise AttributeError() + if not hasattr(sprite, "blendmode"): + raise AttributeError() + + if not isinstance(sprite, DirtySprite): + raise TypeError() + + if sprite.dirty == 0: # set it dirty if it is not + sprite.dirty = 1 + + LayeredUpdates.add_internal(self, sprite, layer) + + def draw(self, surface, bgsurf=None, special_flags=None): + """draw all sprites in the right order onto the given surface + + LayeredDirty.draw(surface, bgsurf=None, special_flags=None): return Rect_list + + You can pass the background too. If a self.bgd is already set to some + value that is not None, then the bgsurf argument has no effect. + Passing a value to special_flags will pass that value as the + special_flags argument to pass to all Surface.blit calls, overriding + the sprite.blendmode attribute + + """ + # functions and classes assigned locally to speed up loops + orig_clip = surface.get_clip() + latest_clip = self._clip + if latest_clip is None: + latest_clip = orig_clip + + local_sprites = self._spritelist + local_old_rect = self.spritedict + local_update = self.lostsprites + rect_type = Rect + + surf_blit_func = surface.blit + if bgsurf is not None: + self._bgd = bgsurf + local_bgd = self._bgd + + surface.set_clip(latest_clip) + # ------- + # 0. decide whether to render with update or flip + start_time = get_ticks() + if self._use_update: # dirty rects mode + # 1. find dirty area on screen and put the rects into + # self.lostsprites still not happy with that part + self._find_dirty_area( + latest_clip, + local_old_rect, + rect_type, + local_sprites, + local_update, + local_update.append, + self._init_rect, + ) + # can it be done better? because that is an O(n**2) algorithm in + # worst case + + # clear using background + if local_bgd is not None: + flags = 0 if special_flags is None else special_flags + for rec in local_update: + surf_blit_func(local_bgd, rec, rec, flags) + + # 2. draw + self._draw_dirty_internal( + local_old_rect, + rect_type, + local_sprites, + surf_blit_func, + local_update, + special_flags, + ) + local_ret = list(local_update) + else: # flip, full screen mode + if local_bgd is not None: + flags = 0 if special_flags is None else special_flags + surf_blit_func(local_bgd, (0, 0), None, flags) + for spr in local_sprites: + if spr.visible: + flags = spr.blendmode if special_flags is None else special_flags + local_old_rect[spr] = surf_blit_func( + spr.image, spr.rect, spr.source_rect, flags + ) + # return only the part of the screen changed + local_ret = [rect_type(latest_clip)] + + # timing for switching modes + # How may a good threshold be found? It depends on the hardware. + end_time = get_ticks() + if end_time - start_time > self._time_threshold: + self._use_update = False + else: + self._use_update = True + + # empty dirty rects list + local_update[:] = [] + + # ------- + # restore original clip + surface.set_clip(orig_clip) + return local_ret + + @staticmethod + def _draw_dirty_internal( + _old_rect, _rect, _sprites, _surf_blit, _update, _special_flags + ): + for spr in _sprites: + flags = spr.blendmode if _special_flags is None else _special_flags + if spr.dirty < 1 and spr.visible: + # sprite not dirty; blit only the intersecting part + if spr.source_rect is not None: + # For possible future speed up, source_rect's data + # can be pre-fetched outside of this loop. + _spr_rect = _rect(spr.rect.topleft, spr.source_rect.size) + rect_offset_x = spr.source_rect[0] - _spr_rect[0] + rect_offset_y = spr.source_rect[1] - _spr_rect[1] + else: + _spr_rect = spr.rect + rect_offset_x = -_spr_rect[0] + rect_offset_y = -_spr_rect[1] + + _spr_rect_clip = _spr_rect.clip + + for idx in _spr_rect.collidelistall(_update): + # clip + clip = _spr_rect_clip(_update[idx]) + _surf_blit( + spr.image, + clip, + ( + clip[0] + rect_offset_x, + clip[1] + rect_offset_y, + clip[2], + clip[3], + ), + flags, + ) + else: # dirty sprite + if spr.visible: + _old_rect[spr] = _surf_blit( + spr.image, spr.rect, spr.source_rect, flags + ) + if spr.dirty == 1: + spr.dirty = 0 + + @staticmethod + def _find_dirty_area( + _clip, _old_rect, _rect, _sprites, _update, _update_append, init_rect + ): + for spr in _sprites: + if spr.dirty > 0: + # chose the right rect + if spr.source_rect: + _union_rect = _rect(spr.rect.topleft, spr.source_rect.size) + else: + _union_rect = _rect(spr.rect) + + _union_rect_collidelist = _union_rect.collidelist + _union_rect_union_ip = _union_rect.union_ip + i = _union_rect_collidelist(_update) + while i > -1: + _union_rect_union_ip(_update[i]) + del _update[i] + i = _union_rect_collidelist(_update) + _update_append(_union_rect.clip(_clip)) + + if _old_rect[spr] is not init_rect: + _union_rect = _rect(_old_rect[spr]) + _union_rect_collidelist = _union_rect.collidelist + _union_rect_union_ip = _union_rect.union_ip + i = _union_rect_collidelist(_update) + while i > -1: + _union_rect_union_ip(_update[i]) + del _update[i] + i = _union_rect_collidelist(_update) + _update_append(_union_rect.clip(_clip)) + + def clear(self, surface, bgd): + """use to set background + + Group.clear(surface, bgd): return None + + """ + self._bgd = bgd + + def repaint_rect(self, screen_rect): + """repaint the given area + + LayeredDirty.repaint_rect(screen_rect): return None + + screen_rect is in screen coordinates. + + """ + if self._clip: + self.lostsprites.append(screen_rect.clip(self._clip)) + else: + self.lostsprites.append(Rect(screen_rect)) + + def set_clip(self, screen_rect=None): + """clip the area where to draw; pass None (default) to reset the clip + + LayeredDirty.set_clip(screen_rect=None): return None + + """ + if screen_rect is None: + self._clip = pygame.display.get_surface().get_rect() + else: + self._clip = screen_rect + self._use_update = False + + def get_clip(self): + """get the area where drawing will occur + + LayeredDirty.get_clip(): return Rect + + """ + return self._clip + + def change_layer(self, sprite, new_layer): + """change the layer of the sprite + + LayeredUpdates.change_layer(sprite, new_layer): return None + + The sprite must have been added to the renderer already. This is not + checked. + + """ + LayeredUpdates.change_layer(self, sprite, new_layer) + if sprite.dirty == 0: + sprite.dirty = 1 + + def set_timing_treshold(self, time_ms): + """set the threshold in milliseconds + + set_timing_treshold(time_ms): return None + + Defaults to 1000.0 / 80.0. This means that the screen will be painted + using the flip method rather than the update method if the update + method is taking so long to update the screen that the frame rate falls + below 80 frames per second. + + Raises TypeError if time_ms is not int or float. + + """ + warn( + "This function will be removed, use set_timing_threshold function instead", + DeprecationWarning, + ) + self.set_timing_threshold(time_ms) + + def set_timing_threshold(self, time_ms): + """set the threshold in milliseconds + + set_timing_threshold(time_ms): return None + + Defaults to 1000.0 / 80.0. This means that the screen will be painted + using the flip method rather than the update method if the update + method is taking so long to update the screen that the frame rate falls + below 80 frames per second. + + Raises TypeError if time_ms is not int or float. + + """ + if isinstance(time_ms, (int, float)): + self._time_threshold = time_ms + else: + raise TypeError( + f"Expected numeric value, got {time_ms.__class__.__name__} instead" + ) + + +class GroupSingle(AbstractGroup): + """A group container that holds a single most recent item. + + This class works just like a regular group, but it only keeps a single + sprite in the group. Whatever sprite has been added to the group last will + be the only sprite in the group. + + You can access its one sprite as the .sprite attribute. Assigning to this + attribute will properly remove the old sprite and then add the new one. + + """ + + def __init__(self, sprite=None): + AbstractGroup.__init__(self) + self.__sprite = None + if sprite is not None: + self.add(sprite) + + def copy(self): + return GroupSingle(self.__sprite) + + def sprites(self): + if self.__sprite is not None: + return [self.__sprite] + return [] + + def add_internal(self, sprite, layer=None): + if self.__sprite is not None: + self.__sprite.remove_internal(self) + self.remove_internal(self.__sprite) + self.__sprite = sprite + + def __bool__(self): + return self.__sprite is not None + + def _get_sprite(self): + return self.__sprite + + def _set_sprite(self, sprite): + self.add_internal(sprite) + sprite.add_internal(self) + return sprite + + @property + def sprite(self): + """ + Property for the single sprite contained in this group + + :return: The sprite. + """ + return self._get_sprite() + + @sprite.setter + def sprite(self, sprite_to_set): + self._set_sprite(sprite_to_set) + + def remove_internal(self, sprite): + if sprite is self.__sprite: + self.__sprite = None + if sprite in self.spritedict: + AbstractGroup.remove_internal(self, sprite) + + def has_internal(self, sprite): + return self.__sprite is sprite + + # Optimizations... + def __contains__(self, sprite): + return self.__sprite is sprite + + +# Some different collision detection functions that could be used. +def collide_rect(left, right): + """collision detection between two sprites, using rects. + + pygame.sprite.collide_rect(left, right): return bool + + Tests for collision between two sprites. Uses the pygame.Rect colliderect + function to calculate the collision. It is intended to be passed as a + collided callback function to the *collide functions. Sprites must have + "rect" attributes. + + New in pygame 1.8.0 + + """ + return left.rect.colliderect(right.rect) + + +class collide_rect_ratio: # noqa pylint: disable=invalid-name; this is a function-like class + """A callable class that checks for collisions using scaled rects + + The class checks for collisions between two sprites using a scaled version + of the sprites' rects. Is created with a ratio; the instance is then + intended to be passed as a collided callback function to the *collide + functions. + + New in pygame 1.8.1 + + """ + + def __init__(self, ratio): + """create a new collide_rect_ratio callable + + Ratio is expected to be a floating point value used to scale + the underlying sprite rect before checking for collisions. + + """ + self.ratio = ratio + + def __repr__(self): + """ + Turn the class into a string. + """ + # pylint: disable=consider-using-f-string + return "<{klass} @{id:x} {attrs}>".format( + klass=self.__class__.__name__, + id=id(self) & 0xFFFFFF, + attrs=" ".join(f"{k}={v!r}" for k, v in self.__dict__.items()), + ) + + def __call__(self, left, right): + """detect collision between two sprites using scaled rects + + pygame.sprite.collide_rect_ratio(ratio)(left, right): return bool + + Tests for collision between two sprites. Uses the pygame.Rect + colliderect function to calculate the collision after scaling the rects + by the stored ratio. Sprites must have "rect" attributes. + + """ + + ratio = self.ratio + + leftrect = left.rect + width = leftrect.width + height = leftrect.height + leftrect = leftrect.inflate(width * ratio - width, height * ratio - height) + + rightrect = right.rect + width = rightrect.width + height = rightrect.height + rightrect = rightrect.inflate(width * ratio - width, height * ratio - height) + + return leftrect.colliderect(rightrect) + + +def collide_circle(left, right): + """detect collision between two sprites using circles + + pygame.sprite.collide_circle(left, right): return bool + + Tests for collision between two sprites by testing whether two circles + centered on the sprites overlap. If the sprites have a "radius" attribute, + then that radius is used to create the circle; otherwise, a circle is + created that is big enough to completely enclose the sprite's rect as + given by the "rect" attribute. This function is intended to be passed as + a collided callback function to the *collide functions. Sprites must have a + "rect" and an optional "radius" attribute. + + New in pygame 1.8.0 + + """ + + xdistance = left.rect.centerx - right.rect.centerx + ydistance = left.rect.centery - right.rect.centery + distancesquared = xdistance**2 + ydistance**2 + + try: + leftradius = left.radius + except AttributeError: + leftrect = left.rect + # approximating the radius of a square by using half of the diagonal, + # might give false positives (especially if its a long small rect) + leftradius = 0.5 * ((leftrect.width**2 + leftrect.height**2) ** 0.5) + # store the radius on the sprite for next time + left.radius = leftradius + + try: + rightradius = right.radius + except AttributeError: + rightrect = right.rect + # approximating the radius of a square by using half of the diagonal + # might give false positives (especially if its a long small rect) + rightradius = 0.5 * ((rightrect.width**2 + rightrect.height**2) ** 0.5) + # store the radius on the sprite for next time + right.radius = rightradius + return distancesquared <= (leftradius + rightradius) ** 2 + + +class collide_circle_ratio: # noqa pylint: disable=invalid-name; this is a function-like class + """detect collision between two sprites using scaled circles + + This callable class checks for collisions between two sprites using a + scaled version of a sprite's radius. It is created with a ratio as the + argument to the constructor. The instance is then intended to be passed as + a collided callback function to the *collide functions. + + New in pygame 1.8.1 + + """ + + def __init__(self, ratio): + """creates a new collide_circle_ratio callable instance + + The given ratio is expected to be a floating point value used to scale + the underlying sprite radius before checking for collisions. + + When the ratio is ratio=1.0, then it behaves exactly like the + collide_circle method. + + """ + self.ratio = ratio + + def __repr__(self): + """ + Turn the class into a string. + """ + # pylint: disable=consider-using-f-string + return "<{klass} @{id:x} {attrs}>".format( + klass=self.__class__.__name__, + id=id(self) & 0xFFFFFF, + attrs=" ".join(f"{k}={v!r}" for k, v in self.__dict__.items()), + ) + + def __call__(self, left, right): + """detect collision between two sprites using scaled circles + + pygame.sprite.collide_circle_radio(ratio)(left, right): return bool + + Tests for collision between two sprites by testing whether two circles + centered on the sprites overlap after scaling the circle's radius by + the stored ratio. If the sprites have a "radius" attribute, that is + used to create the circle; otherwise, a circle is created that is big + enough to completely enclose the sprite's rect as given by the "rect" + attribute. Intended to be passed as a collided callback function to the + *collide functions. Sprites must have a "rect" and an optional "radius" + attribute. + + """ + + ratio = self.ratio + xdistance = left.rect.centerx - right.rect.centerx + ydistance = left.rect.centery - right.rect.centery + distancesquared = xdistance**2 + ydistance**2 + + try: + leftradius = left.radius + except AttributeError: + leftrect = left.rect + leftradius = 0.5 * ((leftrect.width**2 + leftrect.height**2) ** 0.5) + # store the radius on the sprite for next time + left.radius = leftradius + leftradius *= ratio + + try: + rightradius = right.radius + except AttributeError: + rightrect = right.rect + rightradius = 0.5 * ((rightrect.width**2 + rightrect.height**2) ** 0.5) + # store the radius on the sprite for next time + right.radius = rightradius + rightradius *= ratio + + return distancesquared <= (leftradius + rightradius) ** 2 + + +def collide_mask(left, right): + """collision detection between two sprites, using masks. + + pygame.sprite.collide_mask(SpriteLeft, SpriteRight): bool + + Tests for collision between two sprites by testing if their bitmasks + overlap. If the sprites have a "mask" attribute, that is used as the mask; + otherwise, a mask is created from the sprite image. Intended to be passed + as a collided callback function to the *collide functions. Sprites must + have a "rect" and an optional "mask" attribute. + + New in pygame 1.8.0 + + """ + xoffset = right.rect[0] - left.rect[0] + yoffset = right.rect[1] - left.rect[1] + try: + leftmask = left.mask + except AttributeError: + leftmask = from_surface(left.image) + try: + rightmask = right.mask + except AttributeError: + rightmask = from_surface(right.image) + return leftmask.overlap(rightmask, (xoffset, yoffset)) + + +def spritecollide(sprite, group, dokill, collided=None): + """find Sprites in a Group that intersect another Sprite + + pygame.sprite.spritecollide(sprite, group, dokill, collided=None): + return Sprite_list + + Return a list containing all Sprites in a Group that intersect with another + Sprite. Intersection is determined by comparing the Sprite.rect attribute + of each Sprite. + + The dokill argument is a bool. If set to True, all Sprites that collide + will be removed from the Group. + + The collided argument is a callback function used to calculate if two + sprites are colliding. it should take two sprites as values, and return a + bool value indicating if they are colliding. If collided is not passed, all + sprites must have a "rect" value, which is a rectangle of the sprite area, + which will be used to calculate the collision. + + """ + # pull the default collision function in as a local variable outside + # the loop as this makes the loop run faster + default_sprite_collide_func = sprite.rect.colliderect + + if dokill: + crashed = [] + append = crashed.append + + for group_sprite in group.sprites(): + if collided is not None: + if collided(sprite, group_sprite): + group_sprite.kill() + append(group_sprite) + else: + if default_sprite_collide_func(group_sprite.rect): + group_sprite.kill() + append(group_sprite) + + return crashed + + if collided is not None: + return [ + group_sprite for group_sprite in group if collided(sprite, group_sprite) + ] + + return [ + group_sprite + for group_sprite in group + if default_sprite_collide_func(group_sprite.rect) + ] + + +def groupcollide(groupa, groupb, dokilla, dokillb, collided=None): + """detect collision between a group and another group + + pygame.sprite.groupcollide(groupa, groupb, dokilla, dokillb): + return dict + + Given two groups, this will find the intersections between all sprites in + each group. It returns a dictionary of all sprites in the first group that + collide. The value for each item in the dictionary is a list of the sprites + in the second group it collides with. The two dokill arguments control if + the sprites from either group will be automatically removed from all + groups. Collided is a callback function used to calculate if two sprites + are colliding. it should take two sprites as values, and return a bool + value indicating if they are colliding. If collided is not passed, all + sprites must have a "rect" value, which is a rectangle of the sprite area + that will be used to calculate the collision. + + """ + crashed = {} + # pull the collision function in as a local variable outside + # the loop as this makes the loop run faster + sprite_collide_func = spritecollide + if dokilla: + for group_a_sprite in groupa.sprites(): + collision = sprite_collide_func(group_a_sprite, groupb, dokillb, collided) + if collision: + crashed[group_a_sprite] = collision + group_a_sprite.kill() + else: + for group_a_sprite in groupa: + collision = sprite_collide_func(group_a_sprite, groupb, dokillb, collided) + if collision: + crashed[group_a_sprite] = collision + return crashed + + +def spritecollideany(sprite, group, collided=None): + """finds any sprites in a group that collide with the given sprite + + pygame.sprite.spritecollideany(sprite, group): return sprite + + Given a sprite and a group of sprites, this will return return any single + sprite that collides with with the given sprite. If there are no + collisions, then this returns None. + + If you don't need all the features of the spritecollide function, this + function will be a bit quicker. + + Collided is a callback function used to calculate if two sprites are + colliding. It should take two sprites as values and return a bool value + indicating if they are colliding. If collided is not passed, then all + sprites must have a "rect" value, which is a rectangle of the sprite area, + which will be used to calculate the collision. + + + """ + # pull the default collision function in as a local variable outside + # the loop as this makes the loop run faster + default_sprite_collide_func = sprite.rect.colliderect + + if collided is not None: + for group_sprite in group: + if collided(sprite, group_sprite): + return group_sprite + else: + # Special case old behaviour for speed. + for group_sprite in group: + if default_sprite_collide_func(group_sprite.rect): + return group_sprite + return None diff --git a/.venv/Lib/site-packages/pygame/sprite.pyi b/.venv/Lib/site-packages/pygame/sprite.pyi new file mode 100644 index 00000000..149f7a96 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/sprite.pyi @@ -0,0 +1,281 @@ +from typing import ( + Any, + Callable, + Dict, + Generic, + Iterable, + Iterator, + List, + Literal, + Optional, + SupportsFloat, + Tuple, + TypeVar, + Union, +) + +# Protocol added in python 3.8 +from typing_extensions import Protocol + +from pygame.rect import Rect +from pygame.surface import Surface +from pygame.mask import Mask + +from ._common import RectValue, Coordinate + +# non-generic Group, used in Sprite +_Group = AbstractGroup[_SpriteSupportsGroup] + +# protocol helps with structural subtyping for typevars in sprite group generics +class _SupportsSprite(Protocol): + @property + def layer(self) -> int: ... + @layer.setter + def layer(self, value: int) -> None: ... + def __init__(self, *groups: _Group) -> None: ... + def add_internal(self, group: _Group) -> None: ... + def remove_internal(self, group: _Group) -> None: ... + def update(self, *args: Any, **kwargs: Any) -> None: ... + def add(self, *groups: _Group) -> None: ... + def remove(self, *groups: _Group) -> None: ... + def kill(self) -> None: ... + def alive(self) -> bool: ... + def groups(self) -> List[_Group]: ... + +# also a protocol +class _SupportsDirtySprite(_SupportsSprite, Protocol): + dirty: int + blendmode: int + source_rect: Rect + visible: int + _layer: int + def _set_visible(self, val: int) -> None: ... + def _get_visible(self) -> int: ... + +# concrete sprite implementation class +class Sprite(_SupportsSprite): + @property + def layer(self) -> int: ... + @layer.setter + def layer(self, value: int) -> None: ... + def __init__(self, *groups: _Group) -> None: ... + def add_internal(self, group: _Group) -> None: ... + def remove_internal(self, group: _Group) -> None: ... + def update(self, *args: Any, **kwargs: Any) -> None: ... + def add(self, *groups: _Group) -> None: ... + def remove(self, *groups: _Group) -> None: ... + def kill(self) -> None: ... + def alive(self) -> bool: ... + def groups(self) -> List[_Group]: ... + +class WeakSprite(Sprite): + ... + +# concrete dirty sprite implementation class +class DirtySprite(_SupportsDirtySprite): + dirty: int + blendmode: int + source_rect: Rect + visible: int + _layer: int + def _set_visible(self, val: int) -> None: ... + def _get_visible(self) -> int: ... + +class WeakDirtySprite(WeakSprite, DirtySprite): + ... + +# used as a workaround for typing.Self because it is added in python 3.11 +_TGroup = TypeVar("_TGroup", bound=AbstractGroup) + +# define some useful protocols first, which sprite functions accept +# sprite functions don't need all sprite attributes to be present in the +# arguments passed, they only use a few which are marked in the below protocols +class _HasRect(Protocol): + rect: Rect + +# image in addition to rect +class _HasImageAndRect(_HasRect, Protocol): + image: Surface + +# mask in addition to rect +class _HasMaskAndRect(_HasRect, Protocol): + mask: Mask + +# radius in addition to rect +class _HasRadiusAndRect(_HasRect, Protocol): + radius: float + +class _SpriteSupportsGroup(_SupportsSprite, _HasImageAndRect, Protocol): ... +class _DirtySpriteSupportsGroup(_SupportsDirtySprite, _HasImageAndRect, Protocol): ... + +# typevar bound to Sprite, _SpriteSupportsGroup Protocol ensures sprite +# subclass passed to group has image and rect attributes +_TSprite = TypeVar("_TSprite", bound=_SpriteSupportsGroup) +_TSprite2 = TypeVar("_TSprite2", bound=_SpriteSupportsGroup) + +# almost the same as _TSprite but bound to DirtySprite +_TDirtySprite = TypeVar("_TDirtySprite", bound=_DirtySpriteSupportsGroup) + +# Below code demonstrates the advantages of the _SpriteSupportsGroup protocol + +# typechecker should error, regular Sprite does not support Group.draw due to +# missing image and rect attributes +# a = Group(Sprite()) + +# typechecker should error, other Sprite attibutes are also needed for Group +# class MySprite: +# image: Surface +# rect: Rect +# +# b = Group(MySprite()) + +# typechecker should pass +# class MySprite(Sprite): +# image: Surface +# rect: Rect +# +# b = Group(MySprite()) + +class AbstractGroup(Generic[_TSprite]): + spritedict: Dict[_TSprite, Optional[Rect]] + lostsprites: List[Rect] + def __init__(self) -> None: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[_TSprite]: ... + def __bool__(self) -> bool: ... + def __contains__(self, item: Any) -> bool: ... + def add_internal(self, sprite: _TSprite, layer: None = None) -> None: ... + def remove_internal(self, sprite: _TSprite) -> None: ... + def has_internal(self, sprite: _TSprite) -> bool: ... + def copy(self: _TGroup) -> _TGroup: ... # typing.Self is py3.11+ + def sprites(self) -> List[_TSprite]: ... + def add( + self, *sprites: Union[_TSprite, AbstractGroup[_TSprite], Iterable[_TSprite]] + ) -> None: ... + def remove( + self, *sprites: Union[_TSprite, AbstractGroup[_TSprite], Iterable[_TSprite]] + ) -> None: ... + def has( + self, *sprites: Union[_TSprite, AbstractGroup[_TSprite], Iterable[_TSprite]] + ) -> bool: ... + def update(self, *args: Any, **kwargs: Any) -> None: ... + def draw( + self, surface: Surface, bgsurf: Optional[Surface] = None, special_flags: int = 0 + ) -> List[Rect]: ... + def clear( + self, surface: Surface, bgd: Union[Surface, Callable[[Surface, Rect], Any]] + ) -> None: ... + def empty(self) -> None: ... + +class Group(AbstractGroup[_TSprite]): + def __init__( + self, *sprites: Union[_TSprite, AbstractGroup[_TSprite], Iterable[_TSprite]] + ) -> None: ... + +# these are aliased in the code too +RenderPlain = Group +RenderClear = Group + +class RenderUpdates(Group[_TSprite]): ... +class OrderedUpdates(RenderUpdates[_TSprite]): ... + +class LayeredUpdates(AbstractGroup[_TSprite]): + def __init__( + self, + *sprites: Union[ + _TSprite, + AbstractGroup[_TSprite], + Iterable[Union[_TSprite, AbstractGroup[_TSprite]]], + ], + **kwargs: Any + ) -> None: ... + def add( + self, + *sprites: Union[ + _TSprite, + AbstractGroup[_TSprite], + Iterable[Union[_TSprite, AbstractGroup[_TSprite]]], + ], + **kwargs: Any + ) -> None: ... + def get_sprites_at(self, pos: Coordinate) -> List[_TSprite]: ... + def get_sprite(self, idx: int) -> _TSprite: ... + def remove_sprites_of_layer(self, layer_nr: int) -> List[_TSprite]: ... + def layers(self) -> List[int]: ... + def change_layer(self, sprite: _TSprite, new_layer: int) -> None: ... + def get_layer_of_sprite(self, sprite: _TSprite) -> int: ... + def get_top_layer(self) -> int: ... + def get_bottom_layer(self) -> int: ... + def move_to_front(self, sprite: _TSprite) -> None: ... + def move_to_back(self, sprite: _TSprite) -> None: ... + def get_top_sprite(self) -> _TSprite: ... + def get_sprites_from_layer(self, layer: int) -> List[_TSprite]: ... + def switch_layer(self, layer1_nr: int, layer2_nr: int) -> None: ... + +class LayeredDirty(LayeredUpdates[_TDirtySprite]): + def __init__(self, *sprites: _TDirtySprite, **kwargs: Any) -> None: ... + def draw( + self, surface: Surface, bgsurf: Optional[Surface] = None, special_flags: Optional[int] = None + ) -> List[Rect]: ... + # clear breaks Liskov substitution principle in code + def clear(self, surface: Surface, bgd: Surface) -> None: ... # type: ignore[override] + def repaint_rect(self, screen_rect: RectValue) -> None: ... + def set_clip(self, screen_rect: Optional[RectValue] = None) -> None: ... + def get_clip(self) -> Rect: ... + def set_timing_threshold( + self, time_ms: SupportsFloat + ) -> None: ... # This actually accept any value + # deprecated alias + set_timing_treshold = set_timing_threshold + +class GroupSingle(AbstractGroup[_TSprite]): + sprite: _TSprite + def __init__(self, sprite: Optional[_TSprite] = None) -> None: ... + +# argument to collide_rect must have rect attribute +def collide_rect(left: _HasRect, right: _HasRect) -> bool: ... + +class collide_rect_ratio: + ratio: float + def __init__(self, ratio: float) -> None: ... + def __call__(self, left: _HasRect, right: _HasRect) -> bool: ... + +# must have rect attribute, may optionally have radius attribute +_SupportsCollideCircle = Union[_HasRect, _HasRadiusAndRect] + +def collide_circle( + left: _SupportsCollideCircle, right: _SupportsCollideCircle +) -> bool: ... + +class collide_circle_ratio: + ratio: float + def __init__(self, ratio: float) -> None: ... + def __call__( + self, left: _SupportsCollideCircle, right: _SupportsCollideCircle + ) -> bool: ... + +# argument to collide_mask must either have mask or have image attribute, in +# addtion to mandatorily having a rect attribute +_SupportsCollideMask = Union[_HasImageAndRect, _HasMaskAndRect] + +def collide_mask( + left: _SupportsCollideMask, right: _SupportsCollideMask +) -> Optional[Tuple[int, int]]: ... +def spritecollide( + sprite: _HasRect, + group: AbstractGroup[_TSprite], + dokill: bool | Literal[1] | Literal[0], + collided: Optional[Callable[[_HasRect, _TSprite], bool]] = None, +) -> List[_TSprite]: ... +def groupcollide( + groupa: AbstractGroup[_TSprite], + groupb: AbstractGroup[_TSprite2], + dokilla: bool | Literal[1] | Literal[0], + dokillb: bool | Literal[1] | Literal[0], + collided: Optional[Callable[[_TSprite, _TSprite2], bool]] = None, +) -> Dict[_TSprite, List[_TSprite2]]: ... +def spritecollideany( + sprite: _HasRect, + group: AbstractGroup[_TSprite], + collided: Optional[Callable[[_HasRect, _TSprite], bool]] = None, +) -> Optional[_TSprite]: ... diff --git a/.venv/Lib/site-packages/pygame/surface.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/surface.cp311-win_amd64.pyd new file mode 100644 index 00000000..ad499b6a Binary files /dev/null and b/.venv/Lib/site-packages/pygame/surface.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/surface.pyi b/.venv/Lib/site-packages/pygame/surface.pyi new file mode 100644 index 00000000..b1facbaf --- /dev/null +++ b/.venv/Lib/site-packages/pygame/surface.pyi @@ -0,0 +1,149 @@ +from typing import Any, List, Optional, Sequence, Tuple, Union, overload + +from pygame.bufferproxy import BufferProxy +from pygame.color import Color +from pygame.rect import Rect + +from ._common import ColorValue, Coordinate, Literal, RectValue, RGBAOutput + +_ViewKind = Literal[ + "0", + "1", + "2", + "3", + b"0", + b"1", + b"2", + b"3", + "r", + "g", + "b", + "a", + "R", + "G", + "B", + "A", + b"r", + b"g", + b"b", + b"a", + b"R", + b"G", + b"B", + b"A", +] + +class Surface: + _pixels_address: int + @overload + def __init__( + self, + size: Coordinate, + flags: int = 0, + depth: int = 0, + masks: Optional[ColorValue] = None, + ) -> None: ... + @overload + def __init__( + self, + size: Coordinate, + flags: int = 0, + surface: Surface = ..., + ) -> None: ... + def __copy__(self) -> Surface: ... + copy = __copy__ + def blit( + self, + source: Surface, + dest: Union[Coordinate, RectValue], + area: Optional[RectValue] = None, + special_flags: int = 0, + ) -> Rect: ... + def blits( + self, + blit_sequence: Sequence[ + Union[ + Tuple[Surface, Union[Coordinate, RectValue]], + Tuple[Surface, Union[Coordinate, RectValue], Union[RectValue, int]], + Tuple[Surface, Union[Coordinate, RectValue], RectValue, int], + ] + ], + doreturn: Union[int, bool] = 1, + ) -> Union[List[Rect], None]: ... + @overload + def convert(self, surface: Surface) -> Surface: ... + @overload + def convert(self, depth: int, flags: int = 0) -> Surface: ... + @overload + def convert(self, masks: ColorValue, flags: int = 0) -> Surface: ... + @overload + def convert(self) -> Surface: ... + @overload + def convert_alpha(self, surface: Surface) -> Surface: ... + @overload + def convert_alpha(self) -> Surface: ... + def fill( + self, + color: ColorValue, + rect: Optional[RectValue] = None, + special_flags: int = 0, + ) -> Rect: ... + def scroll(self, dx: int = 0, dy: int = 0) -> None: ... + @overload + def set_colorkey(self, color: ColorValue, flags: int = 0) -> None: ... + @overload + def set_colorkey(self, color: None) -> None: ... + def get_colorkey(self) -> Optional[RGBAOutput]: ... + @overload + def set_alpha(self, value: int, flags: int = 0) -> None: ... + @overload + def set_alpha(self, value: None) -> None: ... + def get_alpha(self) -> Optional[int]: ... + def lock(self) -> None: ... + def unlock(self) -> None: ... + def mustlock(self) -> bool: ... + def get_locked(self) -> bool: ... + def get_locks(self) -> Tuple[Any, ...]: ... + def get_at(self, x_y: Sequence[int]) -> Color: ... + def set_at(self, x_y: Sequence[int], color: ColorValue) -> None: ... + def get_at_mapped(self, x_y: Sequence[int]) -> int: ... + def get_palette(self) -> List[Color]: ... + def get_palette_at(self, index: int) -> Color: ... + def set_palette(self, palette: Sequence[ColorValue]) -> None: ... + def set_palette_at(self, index: int, color: ColorValue) -> None: ... + def map_rgb(self, color: ColorValue) -> int: ... + def unmap_rgb(self, mapped_int: int) -> Color: ... + def set_clip(self, rect: Optional[RectValue]) -> None: ... + def get_clip(self) -> Rect: ... + @overload + def subsurface(self, rect: RectValue) -> Surface: ... + @overload + def subsurface(self, left_top: Coordinate, width_height: Coordinate) -> Surface: ... + @overload + def subsurface( + self, left: float, top: float, width: float, height: float + ) -> Surface: ... + def get_parent(self) -> Surface: ... + def get_abs_parent(self) -> Surface: ... + def get_offset(self) -> Tuple[int, int]: ... + def get_abs_offset(self) -> Tuple[int, int]: ... + def get_size(self) -> Tuple[int, int]: ... + def get_width(self) -> int: ... + def get_height(self) -> int: ... + def get_rect(self, **kwargs: Any) -> Rect: ... + def get_bitsize(self) -> int: ... + def get_bytesize(self) -> int: ... + def get_flags(self) -> int: ... + def get_pitch(self) -> int: ... + def get_masks(self) -> RGBAOutput: ... + def set_masks(self, color: ColorValue) -> None: ... + def get_shifts(self) -> RGBAOutput: ... + def set_shifts(self, color: ColorValue) -> None: ... + def get_losses(self) -> RGBAOutput: ... + def get_bounding_rect(self, min_alpha: int = 1) -> Rect: ... + def get_view(self, kind: _ViewKind = "2") -> BufferProxy: ... + def get_buffer(self) -> BufferProxy: ... + def get_blendmode(self) -> int: ... + def premul_alpha(self) -> Surface: ... + +SurfaceType = Surface diff --git a/.venv/Lib/site-packages/pygame/surfarray.py b/.venv/Lib/site-packages/pygame/surfarray.py new file mode 100644 index 00000000..82434602 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/surfarray.py @@ -0,0 +1,447 @@ +## pygame - Python Game Library +## Copyright (C) 2007 Marcus von Appen +## +## This library is free software; you can redistribute it and/or +## modify it under the terms of the GNU Library General Public +## License as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## Library General Public License for more details. +## +## You should have received a copy of the GNU Library General Public +## License along with this library; if not, write to the Free +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## +## Marcus von Appen +## mva@sysfault.org + +"""pygame module for accessing surface pixel data using array interfaces + +Functions to convert between NumPy arrays and Surface objects. This module +will only be functional when pygame can use the external NumPy package. +If NumPy can't be imported, surfarray becomes a MissingModule object. + +Every pixel is stored as a single integer value to represent the red, +green, and blue colors. The 8bit images use a value that looks into a +colormap. Pixels with higher depth use a bit packing process to place +three or four values into a single number. + +The arrays are indexed by the X axis first, followed by the Y +axis. Arrays that treat the pixels as a single integer are referred to +as 2D arrays. This module can also separate the red, green, and blue +color values into separate indices. These types of arrays are referred +to as 3D arrays, and the last index is 0 for red, 1 for green, and 2 for +blue. +""" + + +from pygame.pixelcopy import ( + array_to_surface, + surface_to_array, + map_array as pix_map_array, + make_surface as pix_make_surface, +) +import numpy +from numpy import ( + array as numpy_array, + empty as numpy_empty, + uint32 as numpy_uint32, + ndarray as numpy_ndarray, +) + +import warnings # will be removed in the future + + +# float96 not available on all numpy versions. +numpy_floats = [] +for type_name in "float32 float64 float96".split(): + if hasattr(numpy, type_name): + numpy_floats.append(getattr(numpy, type_name)) +# Added below due to deprecation of numpy.float. See issue #2814 +numpy_floats.append(float) + +# Pixel sizes corresponding to NumPy supported integer sizes, and therefore +# permissible for 2D reference arrays. +_pixel2d_bitdepths = {8, 16, 32} + + +__all__ = [ + "array2d", + "array3d", + "array_alpha", + "array_blue", + "array_colorkey", + "array_green", + "array_red", + "array_to_surface", + "blit_array", + "get_arraytype", + "get_arraytypes", + "make_surface", + "map_array", + "pixels2d", + "pixels3d", + "pixels_alpha", + "pixels_blue", + "pixels_green", + "pixels_red", + "surface_to_array", + "use_arraytype", +] + + +def blit_array(surface, array): + """pygame.surfarray.blit_array(Surface, array): return None + + Blit directly from a array values. + + Directly copy values from an array into a Surface. This is faster than + converting the array into a Surface and blitting. The array must be the + same dimensions as the Surface and will completely replace all pixel + values. Only integer, ascii character and record arrays are accepted. + + This function will temporarily lock the Surface as the new values are + copied. + """ + if isinstance(array, numpy_ndarray) and array.dtype in numpy_floats: + array = array.round(0).astype(numpy_uint32) + return array_to_surface(surface, array) + + +def make_surface(array): + """pygame.surfarray.make_surface (array): return Surface + + Copy an array to a new surface. + + Create a new Surface that best resembles the data and format on the + array. The array can be 2D or 3D with any sized integer values. + """ + if isinstance(array, numpy_ndarray) and array.dtype in numpy_floats: + array = array.round(0).astype(numpy_uint32) + return pix_make_surface(array) + + +def array2d(surface): + """pygame.surfarray.array2d(Surface): return array + + copy pixels into a 2d array + + Copy the pixels from a Surface into a 2D array. The bit depth of the + surface will control the size of the integer values, and will work + for any type of pixel format. + + This function will temporarily lock the Surface as pixels are copied + (see the Surface.lock - lock the Surface memory for pixel access + method). + """ + bpp = surface.get_bytesize() + try: + dtype = (numpy.uint8, numpy.uint16, numpy.int32, numpy.int32)[bpp - 1] + except IndexError: + raise ValueError(f"unsupported bit depth {bpp * 8} for 2D array") + size = surface.get_size() + array = numpy.empty(size, dtype) + surface_to_array(array, surface) + return array + + +def pixels2d(surface): + """pygame.surfarray.pixels2d(Surface): return array + + reference pixels into a 2d array + + Create a new 2D array that directly references the pixel values in a + Surface. Any changes to the array will affect the pixels in the + Surface. This is a fast operation since no data is copied. + + Pixels from a 24-bit Surface cannot be referenced, but all other + Surface bit depths can. + + The Surface this references will remain locked for the lifetime of + the array (see the Surface.lock - lock the Surface memory for pixel + access method). + """ + if surface.get_bitsize() not in _pixel2d_bitdepths: + raise ValueError("unsupported bit depth for 2D reference array") + try: + return numpy_array(surface.get_view("2"), copy=False) + except (ValueError, TypeError): + raise ValueError( + f"bit depth {surface.get_bitsize()} unsupported for 2D reference array" + ) + + +def array3d(surface): + """pygame.surfarray.array3d(Surface): return array + + copy pixels into a 3d array + + Copy the pixels from a Surface into a 3D array. The bit depth of the + surface will control the size of the integer values, and will work + for any type of pixel format. + + This function will temporarily lock the Surface as pixels are copied + (see the Surface.lock - lock the Surface memory for pixel access + method). + """ + width, height = surface.get_size() + array = numpy.empty((width, height, 3), numpy.uint8) + surface_to_array(array, surface) + return array + + +def pixels3d(surface): + """pygame.surfarray.pixels3d(Surface): return array + + reference pixels into a 3d array + + Create a new 3D array that directly references the pixel values in a + Surface. Any changes to the array will affect the pixels in the + Surface. This is a fast operation since no data is copied. + + This will only work on Surfaces that have 24-bit or 32-bit + formats. Lower pixel formats cannot be referenced. + + The Surface this references will remain locked for the lifetime of + the array (see the Surface.lock - lock the Surface memory for pixel + access method). + """ + return numpy_array(surface.get_view("3"), copy=False) + + +def array_alpha(surface): + """pygame.surfarray.array_alpha(Surface): return array + + copy pixel alphas into a 2d array + + Copy the pixel alpha values (degree of transparency) from a Surface + into a 2D array. This will work for any type of Surface + format. Surfaces without a pixel alpha will return an array with all + opaque values. + + This function will temporarily lock the Surface as pixels are copied + (see the Surface.lock - lock the Surface memory for pixel access + method). + """ + size = surface.get_size() + array = numpy.empty(size, numpy.uint8) + surface_to_array(array, surface, "A") + return array + + +def pixels_alpha(surface): + """pygame.surfarray.pixels_alpha(Surface): return array + + reference pixel alphas into a 2d array + + Create a new 2D array that directly references the alpha values + (degree of transparency) in a Surface. Any changes to the array will + affect the pixels in the Surface. This is a fast operation since no + data is copied. + + This can only work on 32-bit Surfaces with a per-pixel alpha value. + + The Surface this array references will remain locked for the + lifetime of the array. + """ + return numpy.array(surface.get_view("A"), copy=False) + + +def pixels_red(surface): + """pygame.surfarray.pixels_red(Surface): return array + + Reference pixel red into a 2d array. + + Create a new 2D array that directly references the red values + in a Surface. Any changes to the array will affect the pixels + in the Surface. This is a fast operation since no data is copied. + + This can only work on 24-bit or 32-bit Surfaces. + + The Surface this array references will remain locked for the + lifetime of the array. + """ + return numpy.array(surface.get_view("R"), copy=False) + + +def array_red(surface): + """pygame.surfarray.array_red(Surface): return array + + copy pixel red into a 2d array + + Copy the pixel red values from a Surface into a 2D array. This will work + for any type of Surface format. + + This function will temporarily lock the Surface as pixels are copied + (see the Surface.lock - lock the Surface memory for pixel access + method). + """ + size = surface.get_size() + array = numpy.empty(size, numpy.uint8) + surface_to_array(array, surface, "R") + return array + + +def pixels_green(surface): + """pygame.surfarray.pixels_green(Surface): return array + + Reference pixel green into a 2d array. + + Create a new 2D array that directly references the green values + in a Surface. Any changes to the array will affect the pixels + in the Surface. This is a fast operation since no data is copied. + + This can only work on 24-bit or 32-bit Surfaces. + + The Surface this array references will remain locked for the + lifetime of the array. + """ + return numpy.array(surface.get_view("G"), copy=False) + + +def array_green(surface): + """pygame.surfarray.array_green(Surface): return array + + copy pixel green into a 2d array + + Copy the pixel green values from a Surface into a 2D array. This will work + for any type of Surface format. + + This function will temporarily lock the Surface as pixels are copied + (see the Surface.lock - lock the Surface memory for pixel access + method). + """ + size = surface.get_size() + array = numpy.empty(size, numpy.uint8) + surface_to_array(array, surface, "G") + return array + + +def pixels_blue(surface): + """pygame.surfarray.pixels_blue(Surface): return array + + Reference pixel blue into a 2d array. + + Create a new 2D array that directly references the blue values + in a Surface. Any changes to the array will affect the pixels + in the Surface. This is a fast operation since no data is copied. + + This can only work on 24-bit or 32-bit Surfaces. + + The Surface this array references will remain locked for the + lifetime of the array. + """ + return numpy.array(surface.get_view("B"), copy=False) + + +def array_blue(surface): + """pygame.surfarray.array_blue(Surface): return array + + copy pixel blue into a 2d array + + Copy the pixel blue values from a Surface into a 2D array. This will work + for any type of Surface format. + + This function will temporarily lock the Surface as pixels are copied + (see the Surface.lock - lock the Surface memory for pixel access + method). + """ + size = surface.get_size() + array = numpy.empty(size, numpy.uint8) + surface_to_array(array, surface, "B") + return array + + +def array_colorkey(surface): + """pygame.surfarray.array_colorkey(Surface): return array + + copy the colorkey values into a 2d array + + Create a new array with the colorkey transparency value from each + pixel. If the pixel matches the colorkey it will be fully + transparent; otherwise it will be fully opaque. + + This will work on any type of Surface format. If the image has no + colorkey a solid opaque array will be returned. + + This function will temporarily lock the Surface as pixels are + copied. + """ + size = surface.get_size() + array = numpy.empty(size, numpy.uint8) + surface_to_array(array, surface, "C") + return array + + +def map_array(surface, array): + """pygame.surfarray.map_array(Surface, array3d): return array2d + + map a 3d array into a 2d array + + Convert a 3D array into a 2D array. This will use the given Surface + format to control the conversion. + + Note: arrays do not need to be 3D, as long as the minor axis has + three elements giving the component colours, any array shape can be + used (for example, a single colour can be mapped, or an array of + colours). The array shape is limited to eleven dimensions maximum, + including the three element minor axis. + """ + if array.ndim == 0: + raise ValueError("array must have at least 1 dimension") + shape = array.shape + if shape[-1] != 3: + raise ValueError("array must be a 3d array of 3-value color data") + target = numpy_empty(shape[:-1], numpy.int32) + pix_map_array(target, array, surface) + return target + + +def use_arraytype(arraytype): + """pygame.surfarray.use_arraytype(arraytype): return None + + DEPRECATED - only numpy arrays are now supported. + """ + warnings.warn( + DeprecationWarning( + "only numpy arrays are now supported, " + "this function will be removed in a " + "future version of the module" + ) + ) + arraytype = arraytype.lower() + if arraytype != "numpy": + raise ValueError("invalid array type") + + +def get_arraytype(): + """pygame.surfarray.get_arraytype(): return str + + DEPRECATED - only numpy arrays are now supported. + """ + warnings.warn( + DeprecationWarning( + "only numpy arrays are now supported, " + "this function will be removed in a " + "future version of the module" + ) + ) + return "numpy" + + +def get_arraytypes(): + """pygame.surfarray.get_arraytypes(): return tuple + + DEPRECATED - only numpy arrays are now supported. + """ + warnings.warn( + DeprecationWarning( + "only numpy arrays are now supported, " + "this function will be removed in a " + "future version of the module" + ) + ) + return ("numpy",) diff --git a/.venv/Lib/site-packages/pygame/surfarray.pyi b/.venv/Lib/site-packages/pygame/surfarray.pyi new file mode 100644 index 00000000..bf157587 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/surfarray.pyi @@ -0,0 +1,31 @@ +from typing import Tuple + +import numpy + +from pygame.surface import Surface + +# importing this way exports the functions in the typestubs +from pygame.pixelcopy import ( + array_to_surface as array_to_surface, + surface_to_array as surface_to_array, +) + +def array2d(surface: Surface) -> numpy.ndarray: ... +def pixels2d(surface: Surface) -> numpy.ndarray: ... +def array3d(surface: Surface) -> numpy.ndarray: ... +def pixels3d(surface: Surface) -> numpy.ndarray: ... +def array_alpha(surface: Surface) -> numpy.ndarray: ... +def pixels_alpha(surface: Surface) -> numpy.ndarray: ... +def array_red(surface: Surface) -> numpy.ndarray: ... +def pixels_red(surface: Surface) -> numpy.ndarray: ... +def array_green(surface: Surface) -> numpy.ndarray: ... +def pixels_green(surface: Surface) -> numpy.ndarray: ... +def array_blue(surface: Surface) -> numpy.ndarray: ... +def pixels_blue(surface: Surface) -> numpy.ndarray: ... +def array_colorkey(surface: Surface) -> numpy.ndarray: ... +def make_surface(array: numpy.ndarray) -> Surface: ... +def blit_array(surface: Surface, array: numpy.ndarray) -> None: ... +def map_array(surface: Surface, array: numpy.ndarray) -> numpy.ndarray: ... +def use_arraytype(arraytype: str) -> None: ... +def get_arraytype() -> str: ... +def get_arraytypes() -> Tuple[str]: ... diff --git a/.venv/Lib/site-packages/pygame/surflock.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/surflock.cp311-win_amd64.pyd new file mode 100644 index 00000000..cca6975e Binary files /dev/null and b/.venv/Lib/site-packages/pygame/surflock.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/surflock.pyi b/.venv/Lib/site-packages/pygame/surflock.pyi new file mode 100644 index 00000000..cd91285d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/surflock.pyi @@ -0,0 +1,2 @@ +# surflock is a private pygame module that does not export any public API +# this file is kept here to make stubtest happy diff --git a/.venv/Lib/site-packages/pygame/sysfont.py b/.venv/Lib/site-packages/pygame/sysfont.py new file mode 100644 index 00000000..cc79a2e0 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/sysfont.py @@ -0,0 +1,517 @@ +# pygame - Python Game Library +# Copyright (C) 2000-2003 Pete Shinners +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Pete Shinners +# pete@shinners.org +"""sysfont, used in the font module to find system fonts""" + +import os +import sys +import warnings +from os.path import basename, dirname, exists, join, splitext + +from pygame.font import Font + +if sys.platform != "emscripten": + if os.name == "nt": + import winreg as _winreg + import subprocess + + +OpenType_extensions = frozenset((".ttf", ".ttc", ".otf")) +Sysfonts = {} +Sysalias = {} + +is_init = False + + +def _simplename(name): + """create simple version of the font name""" + # return alphanumeric characters of a string (converted to lowercase) + return "".join(c.lower() for c in name if c.isalnum()) + + +def _addfont(name, bold, italic, font, fontdict): + """insert a font and style into the font dictionary""" + if name not in fontdict: + fontdict[name] = {} + fontdict[name][bold, italic] = font + + +def initsysfonts_win32(): + """initialize fonts dictionary on Windows""" + + fontdir = join(os.environ.get("WINDIR", "C:\\Windows"), "Fonts") + fonts = {} + + # add fonts entered in the registry + microsoft_font_dirs = [ + "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts", + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts", + ] + + for domain in [_winreg.HKEY_LOCAL_MACHINE, _winreg.HKEY_CURRENT_USER]: + for font_dir in microsoft_font_dirs: + try: + key = _winreg.OpenKey(domain, font_dir) + except FileNotFoundError: + continue + + for i in range(_winreg.QueryInfoKey(key)[1]): + try: + # name is the font's name e.g. Times New Roman (TrueType) + # font is the font's filename e.g. times.ttf + name, font, _ = _winreg.EnumValue(key, i) + except OSError: + break + + if splitext(font)[1].lower() not in OpenType_extensions: + continue + if not dirname(font): + font = join(fontdir, font) + + # Some are named A & B, both names should be processed separately + # Ex: the main Cambria file is marked as "Cambria & Cambria Math" + for name in name.split("&"): + _parse_font_entry_win(name, font, fonts) + + return fonts + + +def _parse_font_entry_win(name, font, fonts): + """ + Parse out a simpler name and the font style from the initial file name. + + :param name: The font name + :param font: The font file path + :param fonts: The pygame font dictionary + """ + true_type_suffix = "(TrueType)" + mods = ("demibold", "narrow", "light", "unicode", "bt", "mt") + if name.endswith(true_type_suffix): + name = name.rstrip(true_type_suffix).rstrip() + name = name.lower().split() + bold = italic = False + for mod in mods: + if mod in name: + name.remove(mod) + if "bold" in name: + name.remove("bold") + bold = True + if "italic" in name: + name.remove("italic") + italic = True + name = "".join(name) + name = _simplename(name) + + _addfont(name, bold, italic, font, fonts) + + +def _parse_font_entry_darwin(name, filepath, fonts): + """ + Parses a font entry for macOS + + :param name: The filepath without extensions or directories + :param filepath: The full path to the font + :param fonts: The pygame font dictionary to add the parsed font data to. + """ + + name = _simplename(name) + + mods = ("regular",) + + for mod in mods: + if mod in name: + name = name.replace(mod, "") + + bold = italic = False + if "bold" in name: + name = name.replace("bold", "") + bold = True + if "italic" in name: + name = name.replace("italic", "") + italic = True + + _addfont(name, bold, italic, filepath, fonts) + + +def _font_finder_darwin(): + locations = [ + "/Library/Fonts", + "/Network/Library/Fonts", + "/System/Library/Fonts", + "/System/Library/Fonts/Supplemental", + ] + + username = os.getenv("USER") + if username: + locations.append(f"/Users/{username}/Library/Fonts") + + strange_root = "/System/Library/Assets/com_apple_MobileAsset_Font3" + if exists(strange_root): + strange_locations = os.listdir(strange_root) + for loc in strange_locations: + locations.append(f"{strange_root}/{loc}/AssetData") + + fonts = {} + + for location in locations: + if not exists(location): + continue + + files = os.listdir(location) + for file in files: + name, extension = splitext(file) + if extension in OpenType_extensions: + _parse_font_entry_darwin(name, join(location, file), fonts) + + return fonts + + +def initsysfonts_darwin(): + """Read the fonts on MacOS, and OS X.""" + # fc-list is not likely to be there on pre 10.4.x, or MacOS 10.10+ + fonts = {} + + fclist_locations = [ + "/usr/X11/bin/fc-list", # apple x11 + "/usr/X11R6/bin/fc-list", # apple x11 + ] + for bin_location in fclist_locations: + if exists(bin_location): + fonts = initsysfonts_unix(bin_location) + break + + if len(fonts) == 0: + fonts = _font_finder_darwin() + + return fonts + + +# read the fonts on unix +def initsysfonts_unix(path="fc-list"): + """use the fc-list from fontconfig to get a list of fonts""" + fonts = {} + + if sys.platform == "emscripten": + return fonts + + try: + proc = subprocess.run( + [path, ":", "file", "family", "style"], + stdout=subprocess.PIPE, # capture stdout + stderr=subprocess.PIPE, # capture stderr + check=True, # so that errors raise python exception which is handled below + timeout=1, # so that we don't hang the program waiting + ) + + except FileNotFoundError: + warnings.warn( + f"'{path}' is missing, system fonts cannot be loaded on your platform" + ) + + except subprocess.TimeoutExpired: + warnings.warn( + f"Process running '{path}' timed-out! System fonts cannot be loaded on " + "your platform" + ) + + except subprocess.CalledProcessError as e: + warnings.warn( + f"'{path}' failed with error code {e.returncode}! System fonts cannot be " + f"loaded on your platform. Error log is:\n{e.stderr}" + ) + + else: + for entry in proc.stdout.decode("ascii", "ignore").splitlines(): + try: + _parse_font_entry_unix(entry, fonts) + except ValueError: + # try the next one. + pass + + return fonts + + +def _parse_font_entry_unix(entry, fonts): + """ + Parses an entry in the unix font data to add to the pygame font + dictionary. + + :param entry: A entry from the unix font list. + :param fonts: The pygame font dictionary to add the parsed font data to. + + """ + filename, family, style = entry.split(":", 2) + if splitext(filename)[1].lower() in OpenType_extensions: + bold = "Bold" in style + italic = "Italic" in style + oblique = "Oblique" in style + for name in family.strip().split(","): + if name: + break + else: + name = splitext(basename(filename))[0] + + _addfont(_simplename(name), bold, italic or oblique, filename, fonts) + + +def create_aliases(): + """Map common fonts that are absent from the system to similar fonts + that are installed in the system + """ + alias_groups = ( + ( + "monospace", + "misc-fixed", + "courier", + "couriernew", + "console", + "fixed", + "mono", + "freemono", + "bitstreamverasansmono", + "verasansmono", + "monotype", + "lucidaconsole", + "consolas", + "dejavusansmono", + "liberationmono", + ), + ( + "sans", + "arial", + "helvetica", + "swiss", + "freesans", + "bitstreamverasans", + "verasans", + "verdana", + "tahoma", + "calibri", + "gillsans", + "segoeui", + "trebuchetms", + "ubuntu", + "dejavusans", + "liberationsans", + ), + ( + "serif", + "times", + "freeserif", + "bitstreamveraserif", + "roman", + "timesroman", + "timesnewroman", + "dutch", + "veraserif", + "georgia", + "cambria", + "constantia", + "dejavuserif", + "liberationserif", + ), + ("wingdings", "wingbats"), + ("comicsansms", "comicsans"), + ) + for alias_set in alias_groups: + for name in alias_set: + if name in Sysfonts: + found = Sysfonts[name] + break + else: + continue + for name in alias_set: + if name not in Sysfonts: + Sysalias[name] = found + + +def initsysfonts(): + """ + Initialise the sysfont module, called once. Locates the installed fonts + and creates some aliases for common font categories. + + Has different initialisation functions for different platforms. + """ + global is_init + if is_init: + # no need to re-init + return + + if sys.platform == "win32": + fonts = initsysfonts_win32() + elif sys.platform == "darwin": + fonts = initsysfonts_darwin() + else: + fonts = initsysfonts_unix() + + Sysfonts.update(fonts) + create_aliases() + is_init = True + + +def font_constructor(fontpath, size, bold, italic): + """ + pygame.font specific declarations + + :param fontpath: path to a font. + :param size: size of a font. + :param bold: bold style, True or False. + :param italic: italic style, True or False. + + :return: A font.Font object. + """ + + font = Font(fontpath, size) + if bold: + font.set_bold(True) + if italic: + font.set_italic(True) + + return font + + +# the exported functions + + +def SysFont(name, size, bold=False, italic=False, constructor=None): + """pygame.font.SysFont(name, size, bold=False, italic=False, constructor=None) -> Font + Create a pygame Font from system font resources. + + This will search the system fonts for the given font + name. You can also enable bold or italic styles, and + the appropriate system font will be selected if available. + + This will always return a valid Font object, and will + fallback on the builtin pygame font if the given font + is not found. + + Name can also be an iterable of font names, a string of + comma-separated font names, or a bytes of comma-separated + font names, in which case the set of names will be searched + in order. Pygame uses a small set of common font aliases. If the + specific font you ask for is not available, a reasonable + alternative may be used. + + If optional constructor is provided, it must be a function with + signature constructor(fontpath, size, bold, italic) which returns + a Font instance. If None, a pygame.font.Font object is created. + """ + if constructor is None: + constructor = font_constructor + + initsysfonts() + + gotbold = gotitalic = False + fontname = None + if name: + if isinstance(name, (str, bytes)): + name = name.split(b"," if isinstance(name, bytes) else ",") + for single_name in name: + if isinstance(single_name, bytes): + single_name = single_name.decode() + + single_name = _simplename(single_name) + styles = Sysfonts.get(single_name) + if not styles: + styles = Sysalias.get(single_name) + if styles: + plainname = styles.get((False, False)) + fontname = styles.get((bold, italic)) + if not (fontname or plainname): + # Neither requested style, nor plain font exists, so + # return a font with the name requested, but an + # arbitrary style. + (style, fontname) = list(styles.items())[0] + # Attempt to style it as requested. This can't + # unbold or unitalicize anything, but it can + # fake bold and/or fake italicize. + if bold and style[0]: + gotbold = True + if italic and style[1]: + gotitalic = True + elif not fontname: + fontname = plainname + elif plainname != fontname: + gotbold = bold + gotitalic = italic + if fontname: + break + + set_bold = set_italic = False + if bold and not gotbold: + set_bold = True + if italic and not gotitalic: + set_italic = True + + return constructor(fontname, size, set_bold, set_italic) + + +def get_fonts(): + """pygame.font.get_fonts() -> list + get a list of system font names + + Returns the list of all found system fonts. Note that + the names of the fonts will be all lowercase with spaces + removed. This is how pygame internally stores the font + names for matching. + """ + initsysfonts() + return list(Sysfonts) + + +def match_font(name, bold=False, italic=False): + """pygame.font.match_font(name, bold=0, italic=0) -> name + find the filename for the named system font + + This performs the same font search as the SysFont() + function, only it returns the path to the TTF file + that would be loaded. The font name can also be an + iterable of font names or a string/bytes of comma-separated + font names to try. + + If no match is found, None is returned. + """ + initsysfonts() + + fontname = None + if isinstance(name, (str, bytes)): + name = name.split(b"," if isinstance(name, bytes) else ",") + + for single_name in name: + if isinstance(single_name, bytes): + single_name = single_name.decode() + + single_name = _simplename(single_name) + styles = Sysfonts.get(single_name) + if not styles: + styles = Sysalias.get(single_name) + if styles: + while not fontname: + fontname = styles.get((bold, italic)) + if italic: + italic = 0 + elif bold: + bold = 0 + elif not fontname: + fontname = list(styles.values())[0] + + if fontname: + break + + return fontname diff --git a/.venv/Lib/site-packages/pygame/tests/__init__.py b/.venv/Lib/site-packages/pygame/tests/__init__.py new file mode 100644 index 00000000..dd265869 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/__init__.py @@ -0,0 +1,40 @@ +"""Pygame unit test suite package + +Exports function run() + +A quick way to run the test suite package from the command line +is by importing the go submodule: + +python -m "import pygame.tests" [] + +Command line option --help displays a usage message. Available options +correspond to the pygame.tests.run arguments. + +The xxxx_test submodules of the tests package are unit test suites for +individual parts of Pygame. Each can also be run as a main program. This is +useful if the test, such as cdrom_test, is interactive. + +For Pygame development the test suite can be run from a Pygame distribution +root directory using run_tests.py. Alternately, test/__main__.py can be run +directly. + +""" + +if __name__ == "pygame.tests": + from pygame.tests.test_utils.run_tests import run +elif __name__ == "__main__": + import os + import sys + + pkg_dir = os.path.split(os.path.abspath(__file__))[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) + + if is_pygame_pkg: + import pygame.tests.__main__ + else: + import test.__main__ +else: + from test.test_utils.run_tests import run diff --git a/.venv/Lib/site-packages/pygame/tests/__main__.py b/.venv/Lib/site-packages/pygame/tests/__main__.py new file mode 100644 index 00000000..9b4cedde --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/__main__.py @@ -0,0 +1,143 @@ +"""Load and run the Pygame test suite + +python -c "import pygame.tests.go" [] + +or + +python test/go.py [] + +Command line option --help displays a command line usage message. + +run_tests.py in the main distribution directory is an alternative to test.go + +""" + +import sys + +if __name__ == "__main__": + import os + + pkg_dir = os.path.split(os.path.abspath(__file__))[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +if is_pygame_pkg: + from pygame.tests.test_utils.run_tests import run_and_exit + from pygame.tests.test_utils.test_runner import opt_parser +else: + from test.test_utils.run_tests import run_and_exit + from test.test_utils.test_runner import opt_parser + +if is_pygame_pkg: + test_pkg_name = "pygame.tests" +else: + test_pkg_name = "test" +program_name = sys.argv[0] +if program_name == "-c": + program_name = f'python -c "import {test_pkg_name}.go"' + +########################################################################### +# Set additional command line options +# +# Defined in test_runner.py as it shares options, added to here + +opt_parser.set_usage( + f""" + +Runs all or some of the {test_pkg_name}.xxxx_test tests. + +$ {program_name} sprite threads -sd + +Runs the sprite and threads module tests isolated in subprocesses, dumping +all failing tests info in the form of a dict. + +""" +) + +opt_parser.add_option( + "-d", "--dump", action="store_true", help="dump results as dict ready to eval" +) + +opt_parser.add_option("-F", "--file", help="dump results to a file") + +opt_parser.add_option( + "-m", + "--multi_thread", + metavar="THREADS", + type="int", + help="run subprocessed tests in x THREADS", +) + +opt_parser.add_option( + "-t", + "--time_out", + metavar="SECONDS", + type="int", + help="kill stalled subprocessed tests after SECONDS", +) + +opt_parser.add_option( + "-f", "--fake", metavar="DIR", help="run fake tests in run_tests__tests/$DIR" +) + +opt_parser.add_option( + "-p", + "--python", + metavar="PYTHON", + help="path to python executable to run subproccesed tests\n" + "default (sys.executable): %s" % sys.executable, +) + +opt_parser.add_option( + "-I", + "--interactive", + action="store_true", + help="include tests requiring user input", +) + +opt_parser.add_option("-S", "--seed", type="int", help="Randomisation seed") + +########################################################################### +# Set run() keyword arguments according to command line arguments. +# args will be the test module list, passed as positional argumemts. + +options, args = opt_parser.parse_args() +kwds = {} +if options.incomplete: + kwds["incomplete"] = True +if options.usesubprocess: + kwds["usesubprocess"] = True +else: + kwds["usesubprocess"] = False +if options.dump: + kwds["dump"] = True +if options.file: + kwds["file"] = options.file +if options.exclude: + kwds["exclude"] = options.exclude +if options.unbuffered: + kwds["unbuffered"] = True +if options.randomize: + kwds["randomize"] = True +if options.seed is not None: + kwds["seed"] = options.seed +if options.multi_thread is not None: + kwds["multi_thread"] = options.multi_thread +if options.time_out is not None: + kwds["time_out"] = options.time_out +if options.fake: + kwds["fake"] = options.fake +if options.python: + kwds["python"] = options.python +if options.interactive: + kwds["interactive"] = True +kwds["verbosity"] = options.verbosity if options.verbosity is not None else 1 + + +########################################################################### +# Run the test suite. +run_and_exit(*args, **kwds) diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 00000000..911aeb91 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/__main__.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/__main__.cpython-311.pyc new file mode 100644 index 00000000..ad65026e Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/__main__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/base_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/base_test.cpython-311.pyc new file mode 100644 index 00000000..7e3ee5f5 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/base_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/blit_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/blit_test.cpython-311.pyc new file mode 100644 index 00000000..207dcafe Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/blit_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/bufferproxy_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/bufferproxy_test.cpython-311.pyc new file mode 100644 index 00000000..72009a6b Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/bufferproxy_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/camera_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/camera_test.cpython-311.pyc new file mode 100644 index 00000000..e7d5a933 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/camera_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/color_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/color_test.cpython-311.pyc new file mode 100644 index 00000000..36b7bee2 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/color_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/constants_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/constants_test.cpython-311.pyc new file mode 100644 index 00000000..3fb1fc4d Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/constants_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/controller_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/controller_test.cpython-311.pyc new file mode 100644 index 00000000..0d1f43c0 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/controller_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/cursors_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/cursors_test.cpython-311.pyc new file mode 100644 index 00000000..95aea6b5 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/cursors_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/display_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/display_test.cpython-311.pyc new file mode 100644 index 00000000..9bbf9728 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/display_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/docs_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/docs_test.cpython-311.pyc new file mode 100644 index 00000000..f43070e0 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/docs_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/draw_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/draw_test.cpython-311.pyc new file mode 100644 index 00000000..aa06c102 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/draw_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/event_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/event_test.cpython-311.pyc new file mode 100644 index 00000000..a7f2eb12 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/event_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/font_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/font_test.cpython-311.pyc new file mode 100644 index 00000000..fa1cfe7d Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/font_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/freetype_tags.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/freetype_tags.cpython-311.pyc new file mode 100644 index 00000000..0a27e8a0 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/freetype_tags.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/freetype_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/freetype_test.cpython-311.pyc new file mode 100644 index 00000000..6e388610 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/freetype_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/ftfont_tags.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/ftfont_tags.cpython-311.pyc new file mode 100644 index 00000000..31491ee4 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/ftfont_tags.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/ftfont_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/ftfont_test.cpython-311.pyc new file mode 100644 index 00000000..c61b9dac Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/ftfont_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/gfxdraw_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/gfxdraw_test.cpython-311.pyc new file mode 100644 index 00000000..6da199c4 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/gfxdraw_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/image__save_gl_surface_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/image__save_gl_surface_test.cpython-311.pyc new file mode 100644 index 00000000..346a45d4 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/image__save_gl_surface_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/image_tags.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/image_tags.cpython-311.pyc new file mode 100644 index 00000000..28284564 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/image_tags.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/image_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/image_test.cpython-311.pyc new file mode 100644 index 00000000..2537f070 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/image_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/imageext_tags.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/imageext_tags.cpython-311.pyc new file mode 100644 index 00000000..2b06b649 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/imageext_tags.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/imageext_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/imageext_test.cpython-311.pyc new file mode 100644 index 00000000..ae2fccd2 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/imageext_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/joystick_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/joystick_test.cpython-311.pyc new file mode 100644 index 00000000..03f657c4 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/joystick_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/key_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/key_test.cpython-311.pyc new file mode 100644 index 00000000..87c9a232 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/key_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/locals_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/locals_test.cpython-311.pyc new file mode 100644 index 00000000..4227a941 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/locals_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/mask_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/mask_test.cpython-311.pyc new file mode 100644 index 00000000..5c50b1d4 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/mask_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/math_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/math_test.cpython-311.pyc new file mode 100644 index 00000000..7655a854 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/math_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/midi_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/midi_test.cpython-311.pyc new file mode 100644 index 00000000..e6dd7d1a Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/midi_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/mixer_music_tags.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/mixer_music_tags.cpython-311.pyc new file mode 100644 index 00000000..8c593e51 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/mixer_music_tags.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/mixer_music_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/mixer_music_test.cpython-311.pyc new file mode 100644 index 00000000..6ff415b2 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/mixer_music_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/mixer_tags.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/mixer_tags.cpython-311.pyc new file mode 100644 index 00000000..446d276b Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/mixer_tags.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/mixer_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/mixer_test.cpython-311.pyc new file mode 100644 index 00000000..ca794725 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/mixer_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/mouse_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/mouse_test.cpython-311.pyc new file mode 100644 index 00000000..3084142d Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/mouse_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/pixelarray_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/pixelarray_test.cpython-311.pyc new file mode 100644 index 00000000..717359be Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/pixelarray_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/pixelcopy_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/pixelcopy_test.cpython-311.pyc new file mode 100644 index 00000000..559c67cf Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/pixelcopy_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/rect_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/rect_test.cpython-311.pyc new file mode 100644 index 00000000..cb8e7eb5 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/rect_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/rwobject_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/rwobject_test.cpython-311.pyc new file mode 100644 index 00000000..aa06f9cb Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/rwobject_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/scrap_tags.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/scrap_tags.cpython-311.pyc new file mode 100644 index 00000000..e7fbf4cf Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/scrap_tags.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/scrap_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/scrap_test.cpython-311.pyc new file mode 100644 index 00000000..3cdb82e3 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/scrap_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/sndarray_tags.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/sndarray_tags.cpython-311.pyc new file mode 100644 index 00000000..aaed970f Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/sndarray_tags.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/sndarray_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/sndarray_test.cpython-311.pyc new file mode 100644 index 00000000..f760a0f3 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/sndarray_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/sprite_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/sprite_test.cpython-311.pyc new file mode 100644 index 00000000..18ffb713 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/sprite_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/surface_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/surface_test.cpython-311.pyc new file mode 100644 index 00000000..83660e6a Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/surface_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/surfarray_tags.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/surfarray_tags.cpython-311.pyc new file mode 100644 index 00000000..76618298 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/surfarray_tags.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/surfarray_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/surfarray_test.cpython-311.pyc new file mode 100644 index 00000000..1ca79868 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/surfarray_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/surflock_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/surflock_test.cpython-311.pyc new file mode 100644 index 00000000..b1360ec4 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/surflock_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/sysfont_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/sysfont_test.cpython-311.pyc new file mode 100644 index 00000000..6cca368b Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/sysfont_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/threads_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/threads_test.cpython-311.pyc new file mode 100644 index 00000000..94a39464 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/threads_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/time_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/time_test.cpython-311.pyc new file mode 100644 index 00000000..21aaefc3 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/time_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/touch_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/touch_test.cpython-311.pyc new file mode 100644 index 00000000..1ca9a329 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/touch_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/transform_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/transform_test.cpython-311.pyc new file mode 100644 index 00000000..3b4b29e9 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/transform_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/version_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/version_test.cpython-311.pyc new file mode 100644 index 00000000..fe725f44 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/version_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/__pycache__/video_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/__pycache__/video_test.cpython-311.pyc new file mode 100644 index 00000000..082d312c Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/__pycache__/video_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/base_test.py b/.venv/Lib/site-packages/pygame/tests/base_test.py new file mode 100644 index 00000000..b11d2d68 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/base_test.py @@ -0,0 +1,623 @@ +import sys +import unittest + +import platform + +IS_PYPY = "PyPy" == platform.python_implementation() + +try: + from pygame.tests.test_utils import arrinter +except NameError: + pass +import pygame + + +quit_count = 0 + + +def quit_hook(): + global quit_count + quit_count += 1 + + +class BaseModuleTest(unittest.TestCase): + def tearDown(self): + # Clean up after each test method. + pygame.quit() + + def test_get_sdl_byteorder(self): + """Ensure the SDL byte order is valid""" + byte_order = pygame.get_sdl_byteorder() + expected_options = (pygame.LIL_ENDIAN, pygame.BIG_ENDIAN) + + self.assertIn(byte_order, expected_options) + + def test_get_sdl_version(self): + """Ensure the SDL version is valid""" + self.assertEqual(len(pygame.get_sdl_version()), 3) + + class ExporterBase: + def __init__(self, shape, typechar, itemsize): + import ctypes + + ndim = len(shape) + self.ndim = ndim + self.shape = tuple(shape) + array_len = 1 + for d in shape: + array_len *= d + self.size = itemsize * array_len + self.parent = ctypes.create_string_buffer(self.size) + self.itemsize = itemsize + strides = [itemsize] * ndim + for i in range(ndim - 1, 0, -1): + strides[i - 1] = strides[i] * shape[i] + self.strides = tuple(strides) + self.data = ctypes.addressof(self.parent), False + if self.itemsize == 1: + byteorder = "|" + elif sys.byteorder == "big": + byteorder = ">" + else: + byteorder = "<" + self.typestr = byteorder + typechar + str(self.itemsize) + + def assertSame(self, proxy, obj): + self.assertEqual(proxy.length, obj.size) + iface = proxy.__array_interface__ + self.assertEqual(iface["typestr"], obj.typestr) + self.assertEqual(iface["shape"], obj.shape) + self.assertEqual(iface["strides"], obj.strides) + self.assertEqual(iface["data"], obj.data) + + def test_PgObject_GetBuffer_array_interface(self): + from pygame.bufferproxy import BufferProxy + + class Exporter(self.ExporterBase): + def get__array_interface__(self): + return { + "version": 3, + "typestr": self.typestr, + "shape": self.shape, + "strides": self.strides, + "data": self.data, + } + + __array_interface__ = property(get__array_interface__) + # Should be ignored by PgObject_GetBuffer + __array_struct__ = property(lambda self: None) + + _shape = [2, 3, 5, 7, 11] # Some prime numbers + for ndim in range(1, len(_shape)): + o = Exporter(_shape[0:ndim], "i", 2) + v = BufferProxy(o) + self.assertSame(v, o) + ndim = 2 + shape = _shape[0:ndim] + for typechar in ("i", "u"): + for itemsize in (1, 2, 4, 8): + o = Exporter(shape, typechar, itemsize) + v = BufferProxy(o) + self.assertSame(v, o) + for itemsize in (4, 8): + o = Exporter(shape, "f", itemsize) + v = BufferProxy(o) + self.assertSame(v, o) + + # Is the dict received from an exporting object properly released? + # The dict should be freed before PgObject_GetBuffer returns. + # When the BufferProxy v's length property is referenced, v calls + # PgObject_GetBuffer, which in turn references Exporter2 o's + # __array_interface__ property. The Exporter2 instance o returns a + # dict subclass for which it keeps both a regular reference and a + # weak reference. The regular reference should be the only + # remaining reference when PgObject_GetBuffer returns. This is + # verified by first checking the weak reference both before and + # after the regular reference held by o is removed. + + import weakref, gc + + class NoDictError(RuntimeError): + pass + + class WRDict(dict): + """Weak referenceable dict""" + + pass + + class Exporter2(Exporter): + def get__array_interface__2(self): + self.d = WRDict(Exporter.get__array_interface__(self)) + self.dict_ref = weakref.ref(self.d) + return self.d + + __array_interface__ = property(get__array_interface__2) + + def free_dict(self): + self.d = None + + def is_dict_alive(self): + try: + return self.dict_ref() is not None + except AttributeError: + raise NoDictError("__array_interface__ is unread") + + o = Exporter2((2, 4), "u", 4) + v = BufferProxy(o) + self.assertRaises(NoDictError, o.is_dict_alive) + length = v.length + self.assertTrue(o.is_dict_alive()) + o.free_dict() + gc.collect() + self.assertFalse(o.is_dict_alive()) + + def test_GetView_array_struct(self): + from pygame.bufferproxy import BufferProxy + + class Exporter(self.ExporterBase): + def __init__(self, shape, typechar, itemsize): + super().__init__(shape, typechar, itemsize) + self.view = BufferProxy(self.__dict__) + + def get__array_struct__(self): + return self.view.__array_struct__ + + __array_struct__ = property(get__array_struct__) + # Should not cause PgObject_GetBuffer to fail + __array_interface__ = property(lambda self: None) + + _shape = [2, 3, 5, 7, 11] # Some prime numbers + for ndim in range(1, len(_shape)): + o = Exporter(_shape[0:ndim], "i", 2) + v = BufferProxy(o) + self.assertSame(v, o) + ndim = 2 + shape = _shape[0:ndim] + for typechar in ("i", "u"): + for itemsize in (1, 2, 4, 8): + o = Exporter(shape, typechar, itemsize) + v = BufferProxy(o) + self.assertSame(v, o) + for itemsize in (4, 8): + o = Exporter(shape, "f", itemsize) + v = BufferProxy(o) + self.assertSame(v, o) + + # Check returned cobject/capsule reference count + try: + from sys import getrefcount + except ImportError: + # PyPy: no reference counting + pass + else: + o = Exporter(shape, typechar, itemsize) + self.assertEqual(getrefcount(o.__array_struct__), 1) + + if pygame.HAVE_NEWBUF: + from pygame.tests.test_utils import buftools + + def NEWBUF_assertSame(self, proxy, exp): + buftools = self.buftools + Importer = buftools.Importer + self.assertEqual(proxy.length, exp.len) + imp = Importer(proxy, buftools.PyBUF_RECORDS_RO) + self.assertEqual(imp.readonly, exp.readonly) + self.assertEqual(imp.format, exp.format) + self.assertEqual(imp.itemsize, exp.itemsize) + self.assertEqual(imp.ndim, exp.ndim) + self.assertEqual(imp.shape, exp.shape) + self.assertEqual(imp.strides, exp.strides) + self.assertTrue(imp.suboffsets is None) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + @unittest.skipIf(IS_PYPY, "pypy no likey") + def test_newbuf(self): + from pygame.bufferproxy import BufferProxy + + Exporter = self.buftools.Exporter + _shape = [2, 3, 5, 7, 11] # Some prime numbers + for ndim in range(1, len(_shape)): + o = Exporter(_shape[0:ndim], "=h") + v = BufferProxy(o) + self.NEWBUF_assertSame(v, o) + ndim = 2 + shape = _shape[0:ndim] + for format in [ + "b", + "B", + "=h", + "=H", + "=i", + "=I", + "=q", + "=Q", + "f", + "d", + "1h", + "=1h", + "x", + "1x", + "2x", + "3x", + "4x", + "5x", + "6x", + "7x", + "8x", + "9x", + ]: + o = Exporter(shape, format) + v = BufferProxy(o) + self.NEWBUF_assertSame(v, o) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + def test_bad_format(self): + from pygame.bufferproxy import BufferProxy + from pygame.newbuffer import BufferMixin + from ctypes import create_string_buffer, addressof + + buftools = self.buftools + Exporter = buftools.Exporter + Importer = buftools.Importer + PyBUF_FORMAT = buftools.PyBUF_FORMAT + + for format in [ + "", + "=", + "1", + " ", + "2h", + "=2h", + "0x", + "11x", + "=!", + "h ", + " h", + "hh", + "?", + ]: + exp = Exporter((1,), format, itemsize=2) + b = BufferProxy(exp) + self.assertRaises(ValueError, Importer, b, PyBUF_FORMAT) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + @unittest.skipIf(IS_PYPY, "fails on pypy") + def test_PgDict_AsBuffer_PyBUF_flags(self): + from pygame.bufferproxy import BufferProxy + + is_lil_endian = pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN + fsys, frev = ("<", ">") if is_lil_endian else (">", "<") + buftools = self.buftools + Importer = buftools.Importer + a = BufferProxy( + {"typestr": "|u4", "shape": (10, 2), "data": (9, False)} + ) # 9? No data accesses. + b = Importer(a, buftools.PyBUF_SIMPLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, 4) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, 9) + b = Importer(a, buftools.PyBUF_WRITABLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, 4) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, 9) + b = Importer(a, buftools.PyBUF_ND) + self.assertEqual(b.ndim, 2) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, 4) + self.assertEqual(b.shape, (10, 2)) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, 9) + a = BufferProxy( + { + "typestr": fsys + "i2", + "shape": (5, 10), + "strides": (24, 2), + "data": (42, False), + } + ) # 42? No data accesses. + b = Importer(a, buftools.PyBUF_STRIDES) + self.assertEqual(b.ndim, 2) + self.assertTrue(b.format is None) + self.assertEqual(b.len, 100) + self.assertEqual(b.itemsize, 2) + self.assertEqual(b.shape, (5, 10)) + self.assertEqual(b.strides, (24, 2)) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, 42) + b = Importer(a, buftools.PyBUF_FULL_RO) + self.assertEqual(b.ndim, 2) + self.assertEqual(b.format, "=h") + self.assertEqual(b.len, 100) + self.assertEqual(b.itemsize, 2) + self.assertEqual(b.shape, (5, 10)) + self.assertEqual(b.strides, (24, 2)) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, 42) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_CONTIG) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_CONTIG) + a = BufferProxy( + { + "typestr": frev + "i2", + "shape": (3, 5, 10), + "strides": (120, 24, 2), + "data": (1000000, True), + } + ) # 1000000? No data accesses. + b = Importer(a, buftools.PyBUF_FULL_RO) + self.assertEqual(b.ndim, 3) + self.assertEqual(b.format, frev + "h") + self.assertEqual(b.len, 300) + self.assertEqual(b.itemsize, 2) + self.assertEqual(b.shape, (3, 5, 10)) + self.assertEqual(b.strides, (120, 24, 2)) + self.assertTrue(b.suboffsets is None) + self.assertTrue(b.readonly) + self.assertEqual(b.buf, 1000000) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_FULL) + + @unittest.skipIf(IS_PYPY or (not pygame.HAVE_NEWBUF), "newbuf with ctypes") + def test_PgObject_AsBuffer_PyBUF_flags(self): + from pygame.bufferproxy import BufferProxy + import ctypes + + is_lil_endian = pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN + fsys, frev = ("<", ">") if is_lil_endian else (">", "<") + buftools = self.buftools + Importer = buftools.Importer + e = arrinter.Exporter( + (10, 2), typekind="f", itemsize=ctypes.sizeof(ctypes.c_double) + ) + a = BufferProxy(e) + b = Importer(a, buftools.PyBUF_SIMPLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertEqual(b.len, e.len) + self.assertEqual(b.itemsize, e.itemsize) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, e.data) + b = Importer(a, buftools.PyBUF_WRITABLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertEqual(b.len, e.len) + self.assertEqual(b.itemsize, e.itemsize) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, e.data) + b = Importer(a, buftools.PyBUF_ND) + self.assertEqual(b.ndim, e.nd) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, e.itemsize) + self.assertEqual(b.shape, e.shape) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, e.data) + e = arrinter.Exporter((5, 10), typekind="i", itemsize=2, strides=(24, 2)) + a = BufferProxy(e) + b = Importer(a, buftools.PyBUF_STRIDES) + self.assertEqual(b.ndim, e.nd) + self.assertTrue(b.format is None) + self.assertEqual(b.len, e.len) + self.assertEqual(b.itemsize, e.itemsize) + self.assertEqual(b.shape, e.shape) + self.assertEqual(b.strides, e.strides) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, e.data) + b = Importer(a, buftools.PyBUF_FULL_RO) + self.assertEqual(b.ndim, e.nd) + self.assertEqual(b.format, "=h") + self.assertEqual(b.len, e.len) + self.assertEqual(b.itemsize, e.itemsize) + self.assertEqual(b.shape, e.shape) + self.assertEqual(b.strides, e.strides) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, e.data) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_WRITABLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_WRITABLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_CONTIG) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_CONTIG) + e = arrinter.Exporter( + (3, 5, 10), + typekind="i", + itemsize=2, + strides=(120, 24, 2), + flags=arrinter.PAI_ALIGNED, + ) + a = BufferProxy(e) + b = Importer(a, buftools.PyBUF_FULL_RO) + self.assertEqual(b.ndim, e.nd) + self.assertEqual(b.format, frev + "h") + self.assertEqual(b.len, e.len) + self.assertEqual(b.itemsize, e.itemsize) + self.assertEqual(b.shape, e.shape) + self.assertEqual(b.strides, e.strides) + self.assertTrue(b.suboffsets is None) + self.assertTrue(b.readonly) + self.assertEqual(b.buf, e.data) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_FULL) + + def test_PgObject_GetBuffer_exception(self): + # For consistency with surfarray + from pygame.bufferproxy import BufferProxy + + bp = BufferProxy(1) + self.assertRaises(ValueError, getattr, bp, "length") + + def not_init_assertions(self): + self.assertFalse(pygame.get_init(), "pygame shouldn't be initialized") + self.assertFalse(pygame.display.get_init(), "display shouldn't be initialized") + + if "pygame.mixer" in sys.modules: + self.assertFalse(pygame.mixer.get_init(), "mixer shouldn't be initialized") + + if "pygame.font" in sys.modules: + self.assertFalse(pygame.font.get_init(), "init shouldn't be initialized") + + ## !!! TODO : Remove when scrap works for OS X + import platform + + if platform.system().startswith("Darwin"): + return + + try: + self.assertRaises(pygame.error, pygame.scrap.get) + except NotImplementedError: + # Scrap is optional. + pass + + # pygame.cdrom + # pygame.joystick + + def init_assertions(self): + self.assertTrue(pygame.get_init()) + self.assertTrue(pygame.display.get_init()) + + if "pygame.mixer" in sys.modules: + self.assertTrue(pygame.mixer.get_init()) + + if "pygame.font" in sys.modules: + self.assertTrue(pygame.font.get_init()) + + def test_quit__and_init(self): + # __doc__ (as of 2008-06-25) for pygame.base.quit: + + # pygame.quit(): return None + # uninitialize all pygame modules + + # Make sure everything is not init + self.not_init_assertions() + + # Initiate it + pygame.init() + + # Check + self.init_assertions() + + # Quit + pygame.quit() + + # All modules have quit + self.not_init_assertions() + + def test_register_quit(self): + """Ensure that a registered function is called on quit()""" + self.assertEqual(quit_count, 0) + + pygame.init() + pygame.register_quit(quit_hook) + pygame.quit() + + self.assertEqual(quit_count, 1) + + def test_get_error(self): + # __doc__ (as of 2008-08-02) for pygame.base.get_error: + + # pygame.get_error(): return errorstr + # get the current error message + # + # SDL maintains an internal error message. This message will usually + # be given to you when pygame.error is raised. You will rarely need to + # call this function. + # + + # The first error could be all sorts of nonsense or empty. + e = pygame.get_error() + pygame.set_error("hi") + self.assertEqual(pygame.get_error(), "hi") + pygame.set_error("") + self.assertEqual(pygame.get_error(), "") + + def test_set_error(self): + # The first error could be all sorts of nonsense or empty. + e = pygame.get_error() + pygame.set_error("hi") + self.assertEqual(pygame.get_error(), "hi") + pygame.set_error("") + self.assertEqual(pygame.get_error(), "") + + def test_unicode_error(self): + pygame.set_error("你好") + self.assertEqual("你好", pygame.get_error()) + + def test_init(self): + """Ensures init() works properly.""" + # Make sure nothing initialized. + self.not_init_assertions() + + # display and joystick must init, at minimum + expected_min_passes = 2 + + # All modules should pass. + expected_fails = 0 + + passes, fails = pygame.init() + + self.init_assertions() + self.assertGreaterEqual(passes, expected_min_passes) + self.assertEqual(fails, expected_fails) + + def test_get_init(self): + # Test if get_init() gets the init state. + self.assertFalse(pygame.get_init()) + + def test_get_init__after_init(self): + # Test if get_init() gets the init state after pygame.init() called. + pygame.init() + + self.assertTrue(pygame.get_init()) + + def test_get_init__after_quit(self): + # Test if get_init() gets the init state after pygame.quit() called. + pygame.init() + pygame.quit() + + self.assertFalse(pygame.get_init()) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/blit_test.py b/.venv/Lib/site-packages/pygame/tests/blit_test.py new file mode 100644 index 00000000..407945db --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/blit_test.py @@ -0,0 +1,153 @@ +import unittest + +import pygame +from pygame.locals import * + + +class BlitTest(unittest.TestCase): + def test_SRCALPHA(self): + """SRCALPHA tests.""" + # blend(s, 0, d) = d + s = pygame.Surface((1, 1), SRCALPHA, 32) + s.fill((255, 255, 255, 0)) + + d = pygame.Surface((1, 1), SRCALPHA, 32) + d.fill((0, 0, 255, 255)) + + s.blit(d, (0, 0)) + self.assertEqual(s.get_at((0, 0)), d.get_at((0, 0))) + + # blend(s, 255, d) = s + s = pygame.Surface((1, 1), SRCALPHA, 32) + s.fill((123, 0, 0, 255)) + s1 = pygame.Surface((1, 1), SRCALPHA, 32) + s1.fill((123, 0, 0, 255)) + d = pygame.Surface((1, 1), SRCALPHA, 32) + d.fill((10, 0, 0, 0)) + s.blit(d, (0, 0)) + self.assertEqual(s.get_at((0, 0)), s1.get_at((0, 0))) + + # TODO: these should be true too. + # blend(0, sA, 0) = 0 + # blend(255, sA, 255) = 255 + # blend(s, sA, d) <= 255 + + def test_BLEND(self): + """BLEND_ tests.""" + + # test that it doesn't overflow, and that it is saturated. + s = pygame.Surface((1, 1), SRCALPHA, 32) + s.fill((255, 255, 255, 0)) + + d = pygame.Surface((1, 1), SRCALPHA, 32) + d.fill((0, 0, 255, 255)) + + s.blit(d, (0, 0), None, BLEND_ADD) + + # print("d %s" % (d.get_at((0,0)),)) + # print(s.get_at((0,0))) + # self.assertEqual(s.get_at((0,0))[2], 255 ) + # self.assertEqual(s.get_at((0,0))[3], 0 ) + + s.blit(d, (0, 0), None, BLEND_RGBA_ADD) + # print(s.get_at((0,0))) + self.assertEqual(s.get_at((0, 0))[3], 255) + + # test adding works. + s.fill((20, 255, 255, 0)) + d.fill((10, 0, 255, 255)) + s.blit(d, (0, 0), None, BLEND_ADD) + self.assertEqual(s.get_at((0, 0))[2], 255) + + # test subbing works. + s.fill((20, 255, 255, 0)) + d.fill((10, 0, 255, 255)) + s.blit(d, (0, 0), None, BLEND_SUB) + self.assertEqual(s.get_at((0, 0))[0], 10) + + # no overflow in sub blend. + s.fill((20, 255, 255, 0)) + d.fill((30, 0, 255, 255)) + s.blit(d, (0, 0), None, BLEND_SUB) + self.assertEqual(s.get_at((0, 0))[0], 0) + + def make_blit_list(self, num_surfs): + blit_list = [] + for i in range(num_surfs): + dest = (i * 10, 0) + surf = pygame.Surface((10, 10), SRCALPHA, 32) + color = (i * 1, i * 1, i * 1) + surf.fill(color) + blit_list.append((surf, dest)) + return blit_list + + def test_blits(self): + NUM_SURFS = 255 + PRINT_TIMING = 0 + dst = pygame.Surface((NUM_SURFS * 10, 10), SRCALPHA, 32) + dst.fill((230, 230, 230)) + blit_list = self.make_blit_list(NUM_SURFS) + + def blits(blit_list): + for surface, dest in blit_list: + dst.blit(surface, dest) + + from time import time + + t0 = time() + results = blits(blit_list) + t1 = time() + if PRINT_TIMING: + print(f"python blits: {t1 - t0}") + + dst.fill((230, 230, 230)) + t0 = time() + results = dst.blits(blit_list) + t1 = time() + if PRINT_TIMING: + print(f"Surface.blits :{t1 - t0}") + + # check if we blit all the different colors in the correct spots. + for i in range(NUM_SURFS): + color = (i * 1, i * 1, i * 1) + self.assertEqual(dst.get_at((i * 10, 0)), color) + self.assertEqual(dst.get_at(((i * 10) + 5, 5)), color) + + self.assertEqual(len(results), NUM_SURFS) + + t0 = time() + results = dst.blits(blit_list, doreturn=0) + t1 = time() + if PRINT_TIMING: + print(f"Surface.blits doreturn=0: {t1 - t0}") + self.assertEqual(results, None) + + t0 = time() + results = dst.blits(((surf, dest) for surf, dest in blit_list)) + t1 = time() + if PRINT_TIMING: + print(f"Surface.blits generator: {t1 - t0}") + + def test_blits_not_sequence(self): + dst = pygame.Surface((100, 10), SRCALPHA, 32) + self.assertRaises(ValueError, dst.blits, None) + + def test_blits_wrong_length(self): + dst = pygame.Surface((100, 10), SRCALPHA, 32) + self.assertRaises( + ValueError, dst.blits, [pygame.Surface((10, 10), SRCALPHA, 32)] + ) + + def test_blits_bad_surf_args(self): + dst = pygame.Surface((100, 10), SRCALPHA, 32) + self.assertRaises(TypeError, dst.blits, [(None, None)]) + + def test_blits_bad_dest(self): + dst = pygame.Surface((100, 10), SRCALPHA, 32) + self.assertRaises( + TypeError, dst.blits, [(pygame.Surface((10, 10), SRCALPHA, 32), None)] + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/bufferproxy_test.py b/.venv/Lib/site-packages/pygame/tests/bufferproxy_test.py new file mode 100644 index 00000000..1282e35c --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/bufferproxy_test.py @@ -0,0 +1,504 @@ +import re +import weakref +import gc +import ctypes +import unittest + +import pygame +from pygame.bufferproxy import BufferProxy + + +try: + BufferError +except NameError: + from pygame import BufferError + + +class BufferProxyTest(unittest.TestCase): + view_keywords = { + "shape": (5, 4, 3), + "typestr": "|u1", + "data": (0, True), + "strides": (4, 20, 1), + } + + def test_module_name(self): + self.assertEqual(pygame.bufferproxy.__name__, "pygame.bufferproxy") + + def test_class_name(self): + self.assertEqual(BufferProxy.__name__, "BufferProxy") + + def test___array_struct___property(self): + kwds = self.view_keywords + v = BufferProxy(kwds) + d = pygame.get_array_interface(v) + self.assertEqual(len(d), 5) + self.assertEqual(d["version"], 3) + self.assertEqual(d["shape"], kwds["shape"]) + self.assertEqual(d["typestr"], kwds["typestr"]) + self.assertEqual(d["data"], kwds["data"]) + self.assertEqual(d["strides"], kwds["strides"]) + + def test___array_interface___property(self): + kwds = self.view_keywords + v = BufferProxy(kwds) + d = v.__array_interface__ + self.assertEqual(len(d), 5) + self.assertEqual(d["version"], 3) + self.assertEqual(d["shape"], kwds["shape"]) + self.assertEqual(d["typestr"], kwds["typestr"]) + self.assertEqual(d["data"], kwds["data"]) + self.assertEqual(d["strides"], kwds["strides"]) + + def test_parent_property(self): + kwds = dict(self.view_keywords) + p = [] + kwds["parent"] = p + v = BufferProxy(kwds) + + self.assertIs(v.parent, p) + + def test_before(self): + def callback(parent): + success.append(parent is p) + + class MyException(Exception): + pass + + def raise_exception(parent): + raise MyException("Just a test.") + + kwds = dict(self.view_keywords) + p = [] + kwds["parent"] = p + + # For array interface + success = [] + kwds["before"] = callback + v = BufferProxy(kwds) + self.assertEqual(len(success), 0) + d = v.__array_interface__ + self.assertEqual(len(success), 1) + self.assertTrue(success[0]) + d = v.__array_interface__ + self.assertEqual(len(success), 1) + d = v = None + gc.collect() + self.assertEqual(len(success), 1) + + # For array struct + success = [] + kwds["before"] = callback + v = BufferProxy(kwds) + self.assertEqual(len(success), 0) + c = v.__array_struct__ + self.assertEqual(len(success), 1) + self.assertTrue(success[0]) + c = v.__array_struct__ + self.assertEqual(len(success), 1) + c = v = None + gc.collect() + self.assertEqual(len(success), 1) + + # Callback raises an exception + kwds["before"] = raise_exception + v = BufferProxy(kwds) + self.assertRaises(MyException, lambda: v.__array_struct__) + + def test_after(self): + def callback(parent): + success.append(parent is p) + + kwds = dict(self.view_keywords) + p = [] + kwds["parent"] = p + + # For array interface + success = [] + kwds["after"] = callback + v = BufferProxy(kwds) + self.assertEqual(len(success), 0) + d = v.__array_interface__ + self.assertEqual(len(success), 0) + d = v.__array_interface__ + self.assertEqual(len(success), 0) + d = v = None + gc.collect() + self.assertEqual(len(success), 1) + self.assertTrue(success[0]) + + # For array struct + success = [] + kwds["after"] = callback + v = BufferProxy(kwds) + self.assertEqual(len(success), 0) + c = v.__array_struct__ + self.assertEqual(len(success), 0) + c = v.__array_struct__ + self.assertEqual(len(success), 0) + c = v = None + gc.collect() + self.assertEqual(len(success), 1) + self.assertTrue(success[0]) + + def test_attribute(self): + v = BufferProxy(self.view_keywords) + self.assertRaises(AttributeError, getattr, v, "undefined") + v.undefined = 12 + self.assertEqual(v.undefined, 12) + del v.undefined + self.assertRaises(AttributeError, getattr, v, "undefined") + + def test_weakref(self): + v = BufferProxy(self.view_keywords) + weak_v = weakref.ref(v) + + self.assertIs(weak_v(), v) + + v = None + gc.collect() + + self.assertIsNone(weak_v()) + + def test_gc(self): + """refcount agnostic check that contained objects are freed""" + + def before_callback(parent): + return r[0] + + def after_callback(parent): + return r[1] + + class Obj: + pass + + p = Obj() + a = Obj() + r = [Obj(), Obj()] + weak_p = weakref.ref(p) + weak_a = weakref.ref(a) + weak_r0 = weakref.ref(r[0]) + weak_r1 = weakref.ref(r[1]) + weak_before = weakref.ref(before_callback) + weak_after = weakref.ref(after_callback) + kwds = dict(self.view_keywords) + kwds["parent"] = p + kwds["before"] = before_callback + kwds["after"] = after_callback + v = BufferProxy(kwds) + v.some_attribute = a + weak_v = weakref.ref(v) + kwds = p = a = before_callback = after_callback = None + gc.collect() + self.assertTrue(weak_p() is not None) + self.assertTrue(weak_a() is not None) + self.assertTrue(weak_before() is not None) + self.assertTrue(weak_after() is not None) + v = None + [gc.collect() for x in range(4)] + self.assertTrue(weak_v() is None) + self.assertTrue(weak_p() is None) + self.assertTrue(weak_a() is None) + self.assertTrue(weak_before() is None) + self.assertTrue(weak_after() is None) + self.assertTrue(weak_r0() is not None) + self.assertTrue(weak_r1() is not None) + r = None + gc.collect() + self.assertTrue(weak_r0() is None) + self.assertTrue(weak_r1() is None) + + # Cycle removal + kwds = dict(self.view_keywords) + kwds["parent"] = [] + v = BufferProxy(kwds) + v.some_attribute = v + tracked = True + for o in gc.get_objects(): + if o is v: + break + else: + tracked = False + self.assertTrue(tracked) + kwds["parent"].append(v) + kwds = None + gc.collect() + n1 = len(gc.garbage) + v = None + gc.collect() + n2 = len(gc.garbage) + self.assertEqual(n2, n1) + + def test_c_api(self): + api = pygame.bufferproxy._PYGAME_C_API + api_type = type(pygame.base._PYGAME_C_API) + + self.assertIsInstance(api, api_type) + + def test_repr(self): + v = BufferProxy(self.view_keywords) + cname = BufferProxy.__name__ + oname, ovalue = re.findall(r"<([^)]+)\(([^)]+)\)>", repr(v))[0] + self.assertEqual(oname, cname) + self.assertEqual(v.length, int(ovalue)) + + def test_subclassing(self): + class MyBufferProxy(BufferProxy): + def __repr__(self): + return f"*{BufferProxy.__repr__(self)}*" + + kwds = dict(self.view_keywords) + kwds["parent"] = 0 + v = MyBufferProxy(kwds) + self.assertEqual(v.parent, 0) + r = repr(v) + self.assertEqual(r[:2], "*<") + self.assertEqual(r[-2:], ">*") + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + def NEWBUF_test_newbuf(self): + from ctypes import string_at + + from pygame.tests.test_utils import buftools + + Exporter = buftools.Exporter + Importer = buftools.Importer + exp = Exporter((10,), "B", readonly=True) + b = BufferProxy(exp) + self.assertEqual(b.length, exp.len) + self.assertEqual(b.raw, string_at(exp.buf, exp.len)) + d = b.__array_interface__ + try: + self.assertEqual(d["typestr"], "|u1") + self.assertEqual(d["shape"], exp.shape) + self.assertEqual(d["strides"], exp.strides) + self.assertEqual(d["data"], (exp.buf, True)) + finally: + d = None + exp = Exporter((3,), "=h") + b = BufferProxy(exp) + self.assertEqual(b.length, exp.len) + self.assertEqual(b.raw, string_at(exp.buf, exp.len)) + d = b.__array_interface__ + try: + lil_endian = pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN + f = f"{'<' if lil_endian else '>'}i{exp.itemsize}" + self.assertEqual(d["typestr"], f) + self.assertEqual(d["shape"], exp.shape) + self.assertEqual(d["strides"], exp.strides) + self.assertEqual(d["data"], (exp.buf, False)) + finally: + d = None + + exp = Exporter((10, 2), "=i") + b = BufferProxy(exp) + imp = Importer(b, buftools.PyBUF_RECORDS) + self.assertTrue(imp.obj is b) + self.assertEqual(imp.buf, exp.buf) + self.assertEqual(imp.ndim, exp.ndim) + self.assertEqual(imp.format, exp.format) + self.assertEqual(imp.readonly, exp.readonly) + self.assertEqual(imp.itemsize, exp.itemsize) + self.assertEqual(imp.len, exp.len) + self.assertEqual(imp.shape, exp.shape) + self.assertEqual(imp.strides, exp.strides) + self.assertTrue(imp.suboffsets is None) + + d = { + "typestr": "|u1", + "shape": (10,), + "strides": (1,), + "data": (9, True), + } # 9? Will not reading the data anyway. + b = BufferProxy(d) + imp = Importer(b, buftools.PyBUF_SIMPLE) + self.assertTrue(imp.obj is b) + self.assertEqual(imp.buf, 9) + self.assertEqual(imp.len, 10) + self.assertEqual(imp.format, None) + self.assertEqual(imp.itemsize, 1) + self.assertEqual(imp.ndim, 0) + self.assertTrue(imp.readonly) + self.assertTrue(imp.shape is None) + self.assertTrue(imp.strides is None) + self.assertTrue(imp.suboffsets is None) + + try: + pygame.bufferproxy.get_segcount + except AttributeError: + pass + else: + + def test_oldbuf_arg(self): + self.OLDBUF_test_oldbuf_arg() + + def OLDBUF_test_oldbuf_arg(self): + from pygame.bufferproxy import get_segcount, get_read_buffer, get_write_buffer + + content = b"\x01\x00\x00\x02" * 12 + memory = ctypes.create_string_buffer(content) + memaddr = ctypes.addressof(memory) + + def raise_exception(o): + raise ValueError("An exception") + + bf = BufferProxy( + { + "shape": (len(content),), + "typestr": "|u1", + "data": (memaddr, False), + "strides": (1,), + } + ) + seglen, segaddr = get_read_buffer(bf, 0) + self.assertEqual(segaddr, 0) + self.assertEqual(seglen, 0) + seglen, segaddr = get_write_buffer(bf, 0) + self.assertEqual(segaddr, 0) + self.assertEqual(seglen, 0) + segcount, buflen = get_segcount(bf) + self.assertEqual(segcount, 1) + self.assertEqual(buflen, len(content)) + seglen, segaddr = get_read_buffer(bf, 0) + self.assertEqual(segaddr, memaddr) + self.assertEqual(seglen, len(content)) + seglen, segaddr = get_write_buffer(bf, 0) + self.assertEqual(segaddr, memaddr) + self.assertEqual(seglen, len(content)) + + bf = BufferProxy( + { + "shape": (len(content),), + "typestr": "|u1", + "data": (memaddr, True), + "strides": (1,), + } + ) + segcount, buflen = get_segcount(bf) + self.assertEqual(segcount, 1) + self.assertEqual(buflen, len(content)) + seglen, segaddr = get_read_buffer(bf, 0) + self.assertEqual(segaddr, memaddr) + self.assertEqual(seglen, len(content)) + self.assertRaises(ValueError, get_write_buffer, bf, 0) + + bf = BufferProxy( + { + "shape": (len(content),), + "typestr": "|u1", + "data": (memaddr, True), + "strides": (1,), + "before": raise_exception, + } + ) + segcount, buflen = get_segcount(bf) + self.assertEqual(segcount, 0) + self.assertEqual(buflen, 0) + + bf = BufferProxy( + { + "shape": (3, 4), + "typestr": "|u4", + "data": (memaddr, True), + "strides": (12, 4), + } + ) + segcount, buflen = get_segcount(bf) + self.assertEqual(segcount, 3 * 4) + self.assertEqual(buflen, 3 * 4 * 4) + for i in range(0, 4): + seglen, segaddr = get_read_buffer(bf, i) + self.assertEqual(segaddr, memaddr + i * 4) + self.assertEqual(seglen, 4) + + +class BufferProxyLegacyTest(unittest.TestCase): + content = b"\x01\x00\x00\x02" * 12 + buffer = ctypes.create_string_buffer(content) + data = (ctypes.addressof(buffer), True) + + def test_length(self): + # __doc__ (as of 2008-08-02) for pygame.bufferproxy.BufferProxy.length: + + # The size of the buffer data in bytes. + bf = BufferProxy( + {"shape": (3, 4), "typestr": "|u4", "data": self.data, "strides": (12, 4)} + ) + self.assertEqual(bf.length, len(self.content)) + bf = BufferProxy( + {"shape": (3, 3), "typestr": "|u4", "data": self.data, "strides": (12, 4)} + ) + self.assertEqual(bf.length, 3 * 3 * 4) + + def test_raw(self): + # __doc__ (as of 2008-08-02) for pygame.bufferproxy.BufferProxy.raw: + + # The raw buffer data as string. The string may contain NUL bytes. + + bf = BufferProxy( + {"shape": (len(self.content),), "typestr": "|u1", "data": self.data} + ) + self.assertEqual(bf.raw, self.content) + bf = BufferProxy( + {"shape": (3, 4), "typestr": "|u4", "data": self.data, "strides": (4, 12)} + ) + self.assertEqual(bf.raw, self.content) + bf = BufferProxy( + {"shape": (3, 4), "typestr": "|u1", "data": self.data, "strides": (16, 4)} + ) + self.assertRaises(ValueError, getattr, bf, "raw") + + def test_write(self): + # __doc__ (as of 2008-08-02) for pygame.bufferproxy.BufferProxy.write: + + # B.write (bufferproxy, buffer, offset) -> None + # + # Writes raw data to the bufferproxy. + # + # Writes the raw data from buffer to the BufferProxy object, starting + # at the specified offset within the BufferProxy. + # If the length of the passed buffer exceeds the length of the + # BufferProxy (reduced by the offset), an IndexError will be raised. + from ctypes import c_byte, sizeof, addressof, string_at, memset + + nullbyte = b"\x00" + Buf = c_byte * 10 + data_buf = Buf(*range(1, 3 * sizeof(Buf) + 1, 3)) + data = string_at(data_buf, sizeof(data_buf)) + buf = Buf() + bp = BufferProxy( + {"typestr": "|u1", "shape": (sizeof(buf),), "data": (addressof(buf), False)} + ) + try: + self.assertEqual(bp.raw, nullbyte * sizeof(Buf)) + bp.write(data) + self.assertEqual(bp.raw, data) + memset(buf, 0, sizeof(buf)) + bp.write(data[:3], 2) + raw = bp.raw + self.assertEqual(raw[:2], nullbyte * 2) + self.assertEqual(raw[2:5], data[:3]) + self.assertEqual(raw[5:], nullbyte * (sizeof(Buf) - 5)) + bp.write(data[:3], bp.length - 3) + raw = bp.raw + self.assertEqual(raw[-3:], data[:3]) + self.assertRaises(IndexError, bp.write, data, 1) + self.assertRaises(IndexError, bp.write, data[:5], -1) + self.assertRaises(IndexError, bp.write, data[:5], bp.length) + self.assertRaises(TypeError, bp.write, 12) + bp = BufferProxy( + { + "typestr": "|u1", + "shape": (sizeof(buf),), + "data": (addressof(buf), True), + } + ) + self.assertRaises(pygame.BufferError, bp.write, b"123") + finally: + # Make sure bp is garbage collected before buf + bp = None + gc.collect() + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/camera_test.py b/.venv/Lib/site-packages/pygame/tests/camera_test.py new file mode 100644 index 00000000..79cf0f92 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/camera_test.py @@ -0,0 +1,5 @@ +import unittest + + +class CameraModuleTest(unittest.TestCase): + pass diff --git a/.venv/Lib/site-packages/pygame/tests/color_test.py b/.venv/Lib/site-packages/pygame/tests/color_test.py new file mode 100644 index 00000000..918c6982 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/color_test.py @@ -0,0 +1,1360 @@ +import math +import operator +import platform +import unittest +from collections.abc import Collection, Sequence + +import pygame +from pygame.colordict import THECOLORS + +IS_PYPY = "PyPy" == platform.python_implementation() +################################### CONSTANTS ################################## + +rgba_vals = [0, 1, 62, 63, 126, 127, 255] + +rgba_combinations = [ + (r, g, b, a) + for r in rgba_vals + for g in rgba_vals + for b in rgba_vals + for a in rgba_vals +] + +################################################################################ + + +def rgba_combos_Color_generator(): + for rgba in rgba_combinations: + yield pygame.Color(*rgba) + + +# Python gamma correct +def gamma_correct(rgba_0_255, gamma): + corrected = round(255.0 * math.pow(rgba_0_255 / 255.0, gamma)) + return max(min(int(corrected), 255), 0) + + +################################################################################ + +# TODO: add tests for +# correct_gamma() -- test against statically defined verified correct values +# coerce () -- ?? + + +def _assignr(x, y): + x.r = y + + +def _assigng(x, y): + x.g = y + + +def _assignb(x, y): + x.b = y + + +def _assigna(x, y): + x.a = y + + +def _assign_item(x, p, y): + x[p] = y + + +class ColorTypeTest(unittest.TestCase): + def test_new(self): + c = pygame.Color.__new__(pygame.Color) + self.assertEqual(c, pygame.Color(0, 0, 0, 255)) + self.assertEqual(len(c), 4) + + def test_init(self): + c = pygame.Color(10, 20, 30, 200) + self.assertEqual(c, (10, 20, 30, 200)) + c.set_length(3) + self.assertEqual(len(c), 3) + c.__init__(100, 110, 120, 128) + self.assertEqual(len(c), 4) + self.assertEqual(c, (100, 110, 120, 128)) + + def test_invalid_html_hex_codes(self): + # This was a problem with the way 2 digit hex numbers were + # calculated. The test_hex_digits test is related to the fix. + Color = pygame.color.Color + self.assertRaises(ValueError, lambda: Color("# f000000")) + self.assertRaises(ValueError, lambda: Color("#f 000000")) + self.assertRaises(ValueError, lambda: Color("#-f000000")) + + def test_hex_digits(self): + # This is an implementation specific test. + # Two digit hex numbers are calculated using table lookups + # for the upper and lower digits. + Color = pygame.color.Color + self.assertEqual(Color("#00000000").r, 0x00) + self.assertEqual(Color("#10000000").r, 0x10) + self.assertEqual(Color("#20000000").r, 0x20) + self.assertEqual(Color("#30000000").r, 0x30) + self.assertEqual(Color("#40000000").r, 0x40) + self.assertEqual(Color("#50000000").r, 0x50) + self.assertEqual(Color("#60000000").r, 0x60) + self.assertEqual(Color("#70000000").r, 0x70) + self.assertEqual(Color("#80000000").r, 0x80) + self.assertEqual(Color("#90000000").r, 0x90) + self.assertEqual(Color("#A0000000").r, 0xA0) + self.assertEqual(Color("#B0000000").r, 0xB0) + self.assertEqual(Color("#C0000000").r, 0xC0) + self.assertEqual(Color("#D0000000").r, 0xD0) + self.assertEqual(Color("#E0000000").r, 0xE0) + self.assertEqual(Color("#F0000000").r, 0xF0) + self.assertEqual(Color("#01000000").r, 0x01) + self.assertEqual(Color("#02000000").r, 0x02) + self.assertEqual(Color("#03000000").r, 0x03) + self.assertEqual(Color("#04000000").r, 0x04) + self.assertEqual(Color("#05000000").r, 0x05) + self.assertEqual(Color("#06000000").r, 0x06) + self.assertEqual(Color("#07000000").r, 0x07) + self.assertEqual(Color("#08000000").r, 0x08) + self.assertEqual(Color("#09000000").r, 0x09) + self.assertEqual(Color("#0A000000").r, 0x0A) + self.assertEqual(Color("#0B000000").r, 0x0B) + self.assertEqual(Color("#0C000000").r, 0x0C) + self.assertEqual(Color("#0D000000").r, 0x0D) + self.assertEqual(Color("#0E000000").r, 0x0E) + self.assertEqual(Color("#0F000000").r, 0x0F) + + def test_comparison(self): + Color = pygame.color.Color + + # Check valid comparisons + self.assertTrue(Color(255, 0, 0, 0) == Color(255, 0, 0, 0)) + self.assertTrue(Color(0, 255, 0, 0) == Color(0, 255, 0, 0)) + self.assertTrue(Color(0, 0, 255, 0) == Color(0, 0, 255, 0)) + self.assertTrue(Color(0, 0, 0, 255) == Color(0, 0, 0, 255)) + self.assertFalse(Color(0, 0, 0, 0) == Color(255, 0, 0, 0)) + self.assertFalse(Color(0, 0, 0, 0) == Color(0, 255, 0, 0)) + self.assertFalse(Color(0, 0, 0, 0) == Color(0, 0, 255, 0)) + self.assertFalse(Color(0, 0, 0, 0) == Color(0, 0, 0, 255)) + self.assertTrue(Color(0, 0, 0, 0) != Color(255, 0, 0, 0)) + self.assertTrue(Color(0, 0, 0, 0) != Color(0, 255, 0, 0)) + self.assertTrue(Color(0, 0, 0, 0) != Color(0, 0, 255, 0)) + self.assertTrue(Color(0, 0, 0, 0) != Color(0, 0, 0, 255)) + self.assertFalse(Color(255, 0, 0, 0) != Color(255, 0, 0, 0)) + self.assertFalse(Color(0, 255, 0, 0) != Color(0, 255, 0, 0)) + self.assertFalse(Color(0, 0, 255, 0) != Color(0, 0, 255, 0)) + self.assertFalse(Color(0, 0, 0, 255) != Color(0, 0, 0, 255)) + + self.assertTrue(Color(255, 0, 0, 0) == (255, 0, 0, 0)) + self.assertTrue(Color(0, 255, 0, 0) == (0, 255, 0, 0)) + self.assertTrue(Color(0, 0, 255, 0) == (0, 0, 255, 0)) + self.assertTrue(Color(0, 0, 0, 255) == (0, 0, 0, 255)) + self.assertFalse(Color(0, 0, 0, 0) == (255, 0, 0, 0)) + self.assertFalse(Color(0, 0, 0, 0) == (0, 255, 0, 0)) + self.assertFalse(Color(0, 0, 0, 0) == (0, 0, 255, 0)) + self.assertFalse(Color(0, 0, 0, 0) == (0, 0, 0, 255)) + self.assertTrue(Color(0, 0, 0, 0) != (255, 0, 0, 0)) + self.assertTrue(Color(0, 0, 0, 0) != (0, 255, 0, 0)) + self.assertTrue(Color(0, 0, 0, 0) != (0, 0, 255, 0)) + self.assertTrue(Color(0, 0, 0, 0) != (0, 0, 0, 255)) + self.assertFalse(Color(255, 0, 0, 0) != (255, 0, 0, 0)) + self.assertFalse(Color(0, 255, 0, 0) != (0, 255, 0, 0)) + self.assertFalse(Color(0, 0, 255, 0) != (0, 0, 255, 0)) + self.assertFalse(Color(0, 0, 0, 255) != (0, 0, 0, 255)) + + self.assertTrue((255, 0, 0, 0) == Color(255, 0, 0, 0)) + self.assertTrue((0, 255, 0, 0) == Color(0, 255, 0, 0)) + self.assertTrue((0, 0, 255, 0) == Color(0, 0, 255, 0)) + self.assertTrue((0, 0, 0, 255) == Color(0, 0, 0, 255)) + self.assertFalse((0, 0, 0, 0) == Color(255, 0, 0, 0)) + self.assertFalse((0, 0, 0, 0) == Color(0, 255, 0, 0)) + self.assertFalse((0, 0, 0, 0) == Color(0, 0, 255, 0)) + self.assertFalse((0, 0, 0, 0) == Color(0, 0, 0, 255)) + self.assertTrue((0, 0, 0, 0) != Color(255, 0, 0, 0)) + self.assertTrue((0, 0, 0, 0) != Color(0, 255, 0, 0)) + self.assertTrue((0, 0, 0, 0) != Color(0, 0, 255, 0)) + self.assertTrue((0, 0, 0, 0) != Color(0, 0, 0, 255)) + self.assertFalse((255, 0, 0, 0) != Color(255, 0, 0, 0)) + self.assertFalse((0, 255, 0, 0) != Color(0, 255, 0, 0)) + self.assertFalse((0, 0, 255, 0) != Color(0, 0, 255, 0)) + self.assertFalse((0, 0, 0, 255) != Color(0, 0, 0, 255)) + + class TupleSubclass(tuple): + pass + + self.assertTrue(Color(255, 0, 0, 0) == TupleSubclass((255, 0, 0, 0))) + self.assertTrue(TupleSubclass((255, 0, 0, 0)) == Color(255, 0, 0, 0)) + self.assertFalse(Color(255, 0, 0, 0) != TupleSubclass((255, 0, 0, 0))) + self.assertFalse(TupleSubclass((255, 0, 0, 0)) != Color(255, 0, 0, 0)) + + # These are not supported so will be unequal. + self.assertFalse(Color(255, 0, 0, 0) == "#ff000000") + self.assertTrue(Color(255, 0, 0, 0) != "#ff000000") + + self.assertFalse("#ff000000" == Color(255, 0, 0, 0)) + self.assertTrue("#ff000000" != Color(255, 0, 0, 0)) + + self.assertFalse(Color(255, 0, 0, 0) == 0xFF000000) + self.assertTrue(Color(255, 0, 0, 0) != 0xFF000000) + + self.assertFalse(0xFF000000 == Color(255, 0, 0, 0)) + self.assertTrue(0xFF000000 != Color(255, 0, 0, 0)) + + self.assertFalse(Color(255, 0, 0, 0) == [255, 0, 0, 0]) + self.assertTrue(Color(255, 0, 0, 0) != [255, 0, 0, 0]) + + self.assertFalse([255, 0, 0, 0] == Color(255, 0, 0, 0)) + self.assertTrue([255, 0, 0, 0] != Color(255, 0, 0, 0)) + + # Comparison is not implemented for invalid color values. + class Test: + def __eq__(self, other): + return -1 + + def __ne__(self, other): + return -2 + + class TestTuple(tuple): + def __eq__(self, other): + return -1 + + def __ne__(self, other): + return -2 + + t = Test() + t_tuple = TestTuple(("a", 0, 0, 0)) + black = Color("black") + self.assertEqual(black == t, -1) + self.assertEqual(t == black, -1) + self.assertEqual(black != t, -2) + self.assertEqual(t != black, -2) + self.assertEqual(black == t_tuple, -1) + self.assertEqual(black != t_tuple, -2) + self.assertEqual(t_tuple == black, -1) + self.assertEqual(t_tuple != black, -2) + + def test_ignore_whitespace(self): + self.assertEqual(pygame.color.Color("red"), pygame.color.Color(" r e d ")) + + def test_slice(self): + # """|tags: python3_ignore|""" + + # slicing a color gives you back a tuple. + # do all sorts of slice combinations. + c = pygame.Color(1, 2, 3, 4) + + self.assertEqual((1, 2, 3, 4), c[:]) + self.assertEqual((1, 2, 3), c[:-1]) + + self.assertEqual((), c[:-5]) + + self.assertEqual((1, 2, 3, 4), c[:4]) + self.assertEqual((1, 2, 3, 4), c[:5]) + self.assertEqual((1, 2), c[:2]) + self.assertEqual((1,), c[:1]) + self.assertEqual((), c[:0]) + + self.assertEqual((2,), c[1:-2]) + self.assertEqual((3, 4), c[-2:]) + self.assertEqual((4,), c[-1:]) + + # NOTE: assigning to a slice is currently unsupported. + + def test_unpack(self): + # should be able to unpack to r,g,b,a and r,g,b + c = pygame.Color(1, 2, 3, 4) + r, g, b, a = c + self.assertEqual((1, 2, 3, 4), (r, g, b, a)) + self.assertEqual(c, (r, g, b, a)) + + c.set_length(3) + r, g, b = c + self.assertEqual((1, 2, 3), (r, g, b)) + + # Checking if DeprecationWarning is triggered + # when function is called + for i in range(1, 5): + with self.assertWarns(DeprecationWarning): + c.set_length(i) + + def test_length(self): + # should be able to unpack to r,g,b,a and r,g,b + c = pygame.Color(1, 2, 3, 4) + self.assertEqual(len(c), 4) + + c.set_length(3) + self.assertEqual(len(c), 3) + + # it keeps the old alpha anyway... + self.assertEqual(c.a, 4) + + # however you can't get the alpha in this way: + self.assertRaises(IndexError, lambda x: c[x], 4) + + c.set_length(4) + self.assertEqual(len(c), 4) + self.assertEqual(len(c), 4) + + self.assertRaises(ValueError, c.set_length, 5) + self.assertRaises(ValueError, c.set_length, -1) + self.assertRaises(ValueError, c.set_length, 0) + self.assertRaises(ValueError, c.set_length, pow(2, 33)) + + def test_case_insensitivity_of_string_args(self): + self.assertEqual(pygame.color.Color("red"), pygame.color.Color("Red")) + + def test_color(self): + """Ensures Color objects can be created.""" + color = pygame.Color(0, 0, 0, 0) + + self.assertIsInstance(color, pygame.Color) + + def test_color__rgba_int_args(self): + """Ensures Color objects can be created using ints.""" + color = pygame.Color(10, 20, 30, 40) + + self.assertEqual(color.r, 10) + self.assertEqual(color.g, 20) + self.assertEqual(color.b, 30) + self.assertEqual(color.a, 40) + + def test_color__rgba_int_args_without_alpha(self): + """Ensures Color objects can be created without providing alpha.""" + color = pygame.Color(10, 20, 30) + + self.assertEqual(color.r, 10) + self.assertEqual(color.g, 20) + self.assertEqual(color.b, 30) + self.assertEqual(color.a, 255) + + def test_color__rgba_int_args_invalid_value(self): + """Ensures invalid values are detected when creating Color objects.""" + self.assertRaises(ValueError, pygame.Color, 257, 10, 105, 44) + self.assertRaises(ValueError, pygame.Color, 10, 257, 105, 44) + self.assertRaises(ValueError, pygame.Color, 10, 105, 257, 44) + self.assertRaises(ValueError, pygame.Color, 10, 105, 44, 257) + + def test_color__rgba_int_args_invalid_value_without_alpha(self): + """Ensures invalid values are detected when creating Color objects + without providing an alpha. + """ + self.assertRaises(ValueError, pygame.Color, 256, 10, 105) + self.assertRaises(ValueError, pygame.Color, 10, 256, 105) + self.assertRaises(ValueError, pygame.Color, 10, 105, 256) + + def test_color__color_object_arg(self): + """Ensures Color objects can be created using Color objects.""" + color_args = (10, 20, 30, 40) + color_obj = pygame.Color(*color_args) + + new_color_obj = pygame.Color(color_obj) + + self.assertIsInstance(new_color_obj, pygame.Color) + self.assertEqual(new_color_obj, color_obj) + self.assertEqual(new_color_obj.r, color_args[0]) + self.assertEqual(new_color_obj.g, color_args[1]) + self.assertEqual(new_color_obj.b, color_args[2]) + self.assertEqual(new_color_obj.a, color_args[3]) + + def test_color__name_str_arg(self): + """Ensures Color objects can be created using str names.""" + for name in ("aquamarine3", "AQUAMARINE3", "AqUAmArIne3"): + color = pygame.Color(name) + + self.assertEqual(color.r, 102) + self.assertEqual(color.g, 205) + self.assertEqual(color.b, 170) + self.assertEqual(color.a, 255) + + def test_color__name_str_arg_from_colordict(self): + """Ensures Color objects can be created using str names + from the THECOLORS dict.""" + for name, values in THECOLORS.items(): + color = pygame.Color(name) + + self.assertEqual(color.r, values[0]) + self.assertEqual(color.g, values[1]) + self.assertEqual(color.b, values[2]) + self.assertEqual(color.a, values[3]) + + def test_color__html_str_arg(self): + """Ensures Color objects can be created using html strings.""" + # See test_webstyle() for related tests. + color = pygame.Color("#a1B2c3D4") + + self.assertEqual(color.r, 0xA1) + self.assertEqual(color.g, 0xB2) + self.assertEqual(color.b, 0xC3) + self.assertEqual(color.a, 0xD4) + + def test_color__hex_str_arg(self): + """Ensures Color objects can be created using hex strings.""" + # See test_webstyle() for related tests. + color = pygame.Color("0x1a2B3c4D") + + self.assertEqual(color.r, 0x1A) + self.assertEqual(color.g, 0x2B) + self.assertEqual(color.b, 0x3C) + self.assertEqual(color.a, 0x4D) + + def test_color__int_arg(self): + """Ensures Color objects can be created using one int value.""" + for value in (0x0, 0xFFFFFFFF, 0xAABBCCDD): + color = pygame.Color(value) + + self.assertEqual(color.r, (value >> 24) & 0xFF) + self.assertEqual(color.g, (value >> 16) & 0xFF) + self.assertEqual(color.b, (value >> 8) & 0xFF) + self.assertEqual(color.a, value & 0xFF) + + def test_color__int_arg_invalid(self): + """Ensures invalid int values are detected when creating Color objects.""" + with self.assertRaises(ValueError): + color = pygame.Color(0x1FFFFFFFF) + + def test_color__sequence_arg(self): + """Ensures Color objects can be created using tuples/lists.""" + color_values = (33, 44, 55, 66) + for seq_type in (tuple, list): + color = pygame.Color(seq_type(color_values)) + + self.assertEqual(color.r, color_values[0]) + self.assertEqual(color.g, color_values[1]) + self.assertEqual(color.b, color_values[2]) + self.assertEqual(color.a, color_values[3]) + + def test_color__sequence_arg_without_alpha(self): + """Ensures Color objects can be created using tuples/lists + without providing an alpha value. + """ + color_values = (33, 44, 55) + for seq_type in (tuple, list): + color = pygame.Color(seq_type(color_values)) + + self.assertEqual(color.r, color_values[0]) + self.assertEqual(color.g, color_values[1]) + self.assertEqual(color.b, color_values[2]) + self.assertEqual(color.a, 255) + + def test_color__sequence_arg_invalid_value(self): + """Ensures invalid sequences are detected when creating Color objects.""" + cls = pygame.Color + for seq_type in (tuple, list): + self.assertRaises(ValueError, cls, seq_type((256, 90, 80, 70))) + self.assertRaises(ValueError, cls, seq_type((100, 256, 80, 70))) + self.assertRaises(ValueError, cls, seq_type((100, 90, 256, 70))) + self.assertRaises(ValueError, cls, seq_type((100, 90, 80, 256))) + + def test_color__sequence_arg_invalid_value_without_alpha(self): + """Ensures invalid sequences are detected when creating Color objects + without providing an alpha. + """ + cls = pygame.Color + for seq_type in (tuple, list): + self.assertRaises(ValueError, cls, seq_type((256, 90, 80))) + self.assertRaises(ValueError, cls, seq_type((100, 256, 80))) + self.assertRaises(ValueError, cls, seq_type((100, 90, 256))) + + def test_color__sequence_arg_invalid_format(self): + """Ensures invalid sequences are detected when creating Color objects + with the wrong number of values. + """ + cls = pygame.Color + for seq_type in (tuple, list): + self.assertRaises(ValueError, cls, seq_type((100,))) + self.assertRaises(ValueError, cls, seq_type((100, 90))) + self.assertRaises(ValueError, cls, seq_type((100, 90, 80, 70, 60))) + + def test_rgba(self): + c = pygame.Color(0) + self.assertEqual(c.r, 0) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 0) + self.assertEqual(c.a, 0) + + # Test simple assignments + c.r = 123 + self.assertEqual(c.r, 123) + self.assertRaises(ValueError, _assignr, c, 537) + self.assertEqual(c.r, 123) + self.assertRaises(ValueError, _assignr, c, -3) + self.assertEqual(c.r, 123) + + c.g = 55 + self.assertEqual(c.g, 55) + self.assertRaises(ValueError, _assigng, c, 348) + self.assertEqual(c.g, 55) + self.assertRaises(ValueError, _assigng, c, -44) + self.assertEqual(c.g, 55) + + c.b = 77 + self.assertEqual(c.b, 77) + self.assertRaises(ValueError, _assignb, c, 256) + self.assertEqual(c.b, 77) + self.assertRaises(ValueError, _assignb, c, -12) + self.assertEqual(c.b, 77) + + c.a = 255 + self.assertEqual(c.a, 255) + self.assertRaises(ValueError, _assigna, c, 312) + self.assertEqual(c.a, 255) + self.assertRaises(ValueError, _assigna, c, -10) + self.assertEqual(c.a, 255) + + def test_repr(self): + c = pygame.Color(68, 38, 26, 69) + t = "(68, 38, 26, 69)" + self.assertEqual(repr(c), t) + + def test_add(self): + c1 = pygame.Color(0) + self.assertEqual(c1.r, 0) + self.assertEqual(c1.g, 0) + self.assertEqual(c1.b, 0) + self.assertEqual(c1.a, 0) + + c2 = pygame.Color(20, 33, 82, 193) + self.assertEqual(c2.r, 20) + self.assertEqual(c2.g, 33) + self.assertEqual(c2.b, 82) + self.assertEqual(c2.a, 193) + + c3 = c1 + c2 + self.assertEqual(c3.r, 20) + self.assertEqual(c3.g, 33) + self.assertEqual(c3.b, 82) + self.assertEqual(c3.a, 193) + + c3 = c3 + c2 + self.assertEqual(c3.r, 40) + self.assertEqual(c3.g, 66) + self.assertEqual(c3.b, 164) + self.assertEqual(c3.a, 255) + + # Issue #286: Is type checking done for Python 3.x? + self.assertRaises(TypeError, operator.add, c1, None) + self.assertRaises(TypeError, operator.add, None, c1) + + def test_sub(self): + c1 = pygame.Color(0xFFFFFFFF) + self.assertEqual(c1.r, 255) + self.assertEqual(c1.g, 255) + self.assertEqual(c1.b, 255) + self.assertEqual(c1.a, 255) + + c2 = pygame.Color(20, 33, 82, 193) + self.assertEqual(c2.r, 20) + self.assertEqual(c2.g, 33) + self.assertEqual(c2.b, 82) + self.assertEqual(c2.a, 193) + + c3 = c1 - c2 + self.assertEqual(c3.r, 235) + self.assertEqual(c3.g, 222) + self.assertEqual(c3.b, 173) + self.assertEqual(c3.a, 62) + + c3 = c3 - c2 + self.assertEqual(c3.r, 215) + self.assertEqual(c3.g, 189) + self.assertEqual(c3.b, 91) + self.assertEqual(c3.a, 0) + + # Issue #286: Is type checking done for Python 3.x? + self.assertRaises(TypeError, operator.sub, c1, None) + self.assertRaises(TypeError, operator.sub, None, c1) + + def test_mul(self): + c1 = pygame.Color(0x01010101) + self.assertEqual(c1.r, 1) + self.assertEqual(c1.g, 1) + self.assertEqual(c1.b, 1) + self.assertEqual(c1.a, 1) + + c2 = pygame.Color(2, 5, 3, 22) + self.assertEqual(c2.r, 2) + self.assertEqual(c2.g, 5) + self.assertEqual(c2.b, 3) + self.assertEqual(c2.a, 22) + + c3 = c1 * c2 + self.assertEqual(c3.r, 2) + self.assertEqual(c3.g, 5) + self.assertEqual(c3.b, 3) + self.assertEqual(c3.a, 22) + + c3 = c3 * c2 + self.assertEqual(c3.r, 4) + self.assertEqual(c3.g, 25) + self.assertEqual(c3.b, 9) + self.assertEqual(c3.a, 255) + + # Issue #286: Is type checking done for Python 3.x? + self.assertRaises(TypeError, operator.mul, c1, None) + self.assertRaises(TypeError, operator.mul, None, c1) + + def test_div(self): + c1 = pygame.Color(0x80808080) + self.assertEqual(c1.r, 128) + self.assertEqual(c1.g, 128) + self.assertEqual(c1.b, 128) + self.assertEqual(c1.a, 128) + + c2 = pygame.Color(2, 4, 8, 16) + self.assertEqual(c2.r, 2) + self.assertEqual(c2.g, 4) + self.assertEqual(c2.b, 8) + self.assertEqual(c2.a, 16) + + c3 = c1 // c2 + self.assertEqual(c3.r, 64) + self.assertEqual(c3.g, 32) + self.assertEqual(c3.b, 16) + self.assertEqual(c3.a, 8) + + c3 = c3 // c2 + self.assertEqual(c3.r, 32) + self.assertEqual(c3.g, 8) + self.assertEqual(c3.b, 2) + self.assertEqual(c3.a, 0) + + # Issue #286: Is type checking done for Python 3.x? + self.assertRaises(TypeError, operator.floordiv, c1, None) + self.assertRaises(TypeError, operator.floordiv, None, c1) + + # Division by zero check + dividend = pygame.Color(255, 255, 255, 255) + for i in range(4): + divisor = pygame.Color(64, 64, 64, 64) + divisor[i] = 0 + quotient = pygame.Color(3, 3, 3, 3) + quotient[i] = 0 + self.assertEqual(dividend // divisor, quotient) + + def test_mod(self): + c1 = pygame.Color(0xFFFFFFFF) + self.assertEqual(c1.r, 255) + self.assertEqual(c1.g, 255) + self.assertEqual(c1.b, 255) + self.assertEqual(c1.a, 255) + + c2 = pygame.Color(2, 4, 8, 16) + self.assertEqual(c2.r, 2) + self.assertEqual(c2.g, 4) + self.assertEqual(c2.b, 8) + self.assertEqual(c2.a, 16) + + c3 = c1 % c2 + self.assertEqual(c3.r, 1) + self.assertEqual(c3.g, 3) + self.assertEqual(c3.b, 7) + self.assertEqual(c3.a, 15) + + # Issue #286: Is type checking done for Python 3.x? + self.assertRaises(TypeError, operator.mod, c1, None) + self.assertRaises(TypeError, operator.mod, None, c1) + + # Division by zero check + dividend = pygame.Color(255, 255, 255, 255) + for i in range(4): + divisor = pygame.Color(64, 64, 64, 64) + divisor[i] = 0 + quotient = pygame.Color(63, 63, 63, 63) + quotient[i] = 0 + self.assertEqual(dividend % divisor, quotient) + + def test_float(self): + c = pygame.Color(0xCC00CC00) + self.assertEqual(c.r, 204) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 204) + self.assertEqual(c.a, 0) + self.assertEqual(float(c), float(0xCC00CC00)) + + c = pygame.Color(0x33727592) + self.assertEqual(c.r, 51) + self.assertEqual(c.g, 114) + self.assertEqual(c.b, 117) + self.assertEqual(c.a, 146) + self.assertEqual(float(c), float(0x33727592)) + + def test_oct(self): + c = pygame.Color(0xCC00CC00) + self.assertEqual(c.r, 204) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 204) + self.assertEqual(c.a, 0) + self.assertEqual(oct(c), oct(0xCC00CC00)) + + c = pygame.Color(0x33727592) + self.assertEqual(c.r, 51) + self.assertEqual(c.g, 114) + self.assertEqual(c.b, 117) + self.assertEqual(c.a, 146) + self.assertEqual(oct(c), oct(0x33727592)) + + def test_hex(self): + c = pygame.Color(0xCC00CC00) + self.assertEqual(c.r, 204) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 204) + self.assertEqual(c.a, 0) + self.assertEqual(hex(c), hex(0xCC00CC00)) + + c = pygame.Color(0x33727592) + self.assertEqual(c.r, 51) + self.assertEqual(c.g, 114) + self.assertEqual(c.b, 117) + self.assertEqual(c.a, 146) + self.assertEqual(hex(c), hex(0x33727592)) + + def test_webstyle(self): + c = pygame.Color("#CC00CC11") + self.assertEqual(c.r, 204) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 204) + self.assertEqual(c.a, 17) + self.assertEqual(hex(c), hex(0xCC00CC11)) + + c = pygame.Color("#CC00CC") + self.assertEqual(c.r, 204) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 204) + self.assertEqual(c.a, 255) + self.assertEqual(hex(c), hex(0xCC00CCFF)) + + c = pygame.Color("0xCC00CC11") + self.assertEqual(c.r, 204) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 204) + self.assertEqual(c.a, 17) + self.assertEqual(hex(c), hex(0xCC00CC11)) + + c = pygame.Color("0xCC00CC") + self.assertEqual(c.r, 204) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 204) + self.assertEqual(c.a, 255) + self.assertEqual(hex(c), hex(0xCC00CCFF)) + + self.assertRaises(ValueError, pygame.Color, "#cc00qq") + self.assertRaises(ValueError, pygame.Color, "0xcc00qq") + self.assertRaises(ValueError, pygame.Color, "09abcdef") + self.assertRaises(ValueError, pygame.Color, "09abcde") + self.assertRaises(ValueError, pygame.Color, "quarky") + + def test_int(self): + # This will be a long + c = pygame.Color(0xCC00CC00) + self.assertEqual(c.r, 204) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 204) + self.assertEqual(c.a, 0) + self.assertEqual(int(c), int(0xCC00CC00)) + + # This will be an int + c = pygame.Color(0x33727592) + self.assertEqual(c.r, 51) + self.assertEqual(c.g, 114) + self.assertEqual(c.b, 117) + self.assertEqual(c.a, 146) + self.assertEqual(int(c), int(0x33727592)) + + def test_long(self): + # This will be a long + c = pygame.Color(0xCC00CC00) + self.assertEqual(c.r, 204) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 204) + self.assertEqual(c.a, 0) + self.assertEqual(int(c), int(0xCC00CC00)) + + # This will be an int + c = pygame.Color(0x33727592) + self.assertEqual(c.r, 51) + self.assertEqual(c.g, 114) + self.assertEqual(c.b, 117) + self.assertEqual(c.a, 146) + self.assertEqual(int(c), int(0x33727592)) + + def test_normalize(self): + c = pygame.Color(204, 38, 194, 55) + self.assertEqual(c.r, 204) + self.assertEqual(c.g, 38) + self.assertEqual(c.b, 194) + self.assertEqual(c.a, 55) + + t = c.normalize() + + self.assertAlmostEqual(t[0], 0.800000, 5) + self.assertAlmostEqual(t[1], 0.149016, 5) + self.assertAlmostEqual(t[2], 0.760784, 5) + self.assertAlmostEqual(t[3], 0.215686, 5) + + def test_len(self): + c = pygame.Color(204, 38, 194, 55) + self.assertEqual(len(c), 4) + + def test_get_item(self): + c = pygame.Color(204, 38, 194, 55) + self.assertEqual(c[0], 204) + self.assertEqual(c[1], 38) + self.assertEqual(c[2], 194) + self.assertEqual(c[3], 55) + + def test_set_item(self): + c = pygame.Color(204, 38, 194, 55) + self.assertEqual(c[0], 204) + self.assertEqual(c[1], 38) + self.assertEqual(c[2], 194) + self.assertEqual(c[3], 55) + + c[0] = 33 + self.assertEqual(c[0], 33) + c[1] = 48 + self.assertEqual(c[1], 48) + c[2] = 173 + self.assertEqual(c[2], 173) + c[3] = 213 + self.assertEqual(c[3], 213) + + # Now try some 'invalid' ones + self.assertRaises(TypeError, _assign_item, c, 0, 95.485) + self.assertEqual(c[0], 33) + self.assertRaises(ValueError, _assign_item, c, 1, -83) + self.assertEqual(c[1], 48) + self.assertRaises(TypeError, _assign_item, c, 2, "Hello") + self.assertEqual(c[2], 173) + + def test_Color_type_works_for_Surface_get_and_set_colorkey(self): + s = pygame.Surface((32, 32)) + + c = pygame.Color(33, 22, 11, 255) + s.set_colorkey(c) + + get_r, get_g, get_b, get_a = s.get_colorkey() + + self.assertTrue(get_r == c.r) + self.assertTrue(get_g == c.g) + self.assertTrue(get_b == c.b) + self.assertTrue(get_a == c.a) + + ########## HSLA, HSVA, CMY, I1I2I3 ALL ELEMENTS WITHIN SPECIFIED RANGE ######### + + def test_hsla__all_elements_within_limits(self): + for c in rgba_combos_Color_generator(): + h, s, l, a = c.hsla + self.assertTrue(0 <= h <= 360) + self.assertTrue(0 <= s <= 100) + self.assertTrue(0 <= l <= 100) + self.assertTrue(0 <= a <= 100) + + def test_hsva__all_elements_within_limits(self): + for c in rgba_combos_Color_generator(): + h, s, v, a = c.hsva + self.assertTrue(0 <= h <= 360) + self.assertTrue(0 <= s <= 100) + self.assertTrue(0 <= v <= 100) + self.assertTrue(0 <= a <= 100) + + def test_cmy__all_elements_within_limits(self): + for c in rgba_combos_Color_generator(): + c, m, y = c.cmy + self.assertTrue(0 <= c <= 1) + self.assertTrue(0 <= m <= 1) + self.assertTrue(0 <= y <= 1) + + def test_i1i2i3__all_elements_within_limits(self): + for c in rgba_combos_Color_generator(): + i1, i2, i3 = c.i1i2i3 + self.assertTrue(0 <= i1 <= 1) + self.assertTrue(-0.5 <= i2 <= 0.5) + self.assertTrue(-0.5 <= i3 <= 0.5) + + def test_issue_269(self): + """PyColor OverflowError on HSVA with hue value of 360 + + >>> c = pygame.Color(0) + >>> c.hsva = (360,0,0,0) + Traceback (most recent call last): + File "", line 1, in + OverflowError: this is not allowed to happen ever + >>> pygame.ver + '1.9.1release' + >>> + + """ + + c = pygame.Color(0) + c.hsva = 360, 0, 0, 0 + self.assertEqual(c.hsva, (0, 0, 0, 0)) + c.hsva = 360, 100, 100, 100 + self.assertEqual(c.hsva, (0, 100, 100, 100)) + self.assertEqual(c, (255, 0, 0, 255)) + + ####################### COLORSPACE PROPERTY SANITY TESTS ####################### + + def colorspaces_converted_should_not_raise(self, prop): + fails = 0 + + x = 0 + for c in rgba_combos_Color_generator(): + x += 1 + + other = pygame.Color(0) + + try: + setattr(other, prop, getattr(c, prop)) + # eg other.hsla = c.hsla + + except ValueError: + fails += 1 + + self.assertTrue(x > 0, "x is combination counter, 0 means no tests!") + self.assertTrue((fails, x) == (0, x)) + + def test_hsla__sanity_testing_converted_should_not_raise(self): + self.colorspaces_converted_should_not_raise("hsla") + + def test_hsva__sanity_testing_converted_should_not_raise(self): + self.colorspaces_converted_should_not_raise("hsva") + + def test_cmy__sanity_testing_converted_should_not_raise(self): + self.colorspaces_converted_should_not_raise("cmy") + + def test_i1i2i3__sanity_testing_converted_should_not_raise(self): + self.colorspaces_converted_should_not_raise("i1i2i3") + + ################################################################################ + + def colorspaces_converted_should_equate_bar_rounding(self, prop): + for c in rgba_combos_Color_generator(): + other = pygame.Color(0) + + try: + setattr(other, prop, getattr(c, prop)) + # eg other.hsla = c.hsla + + self.assertTrue(abs(other.r - c.r) <= 1) + self.assertTrue(abs(other.b - c.b) <= 1) + self.assertTrue(abs(other.g - c.g) <= 1) + # CMY and I1I2I3 do not care about the alpha + if not prop in ("cmy", "i1i2i3"): + self.assertTrue(abs(other.a - c.a) <= 1) + + except ValueError: + pass # other tests will notify, this tests equation + + def test_hsla__sanity_testing_converted_should_equate_bar_rounding(self): + self.colorspaces_converted_should_equate_bar_rounding("hsla") + + def test_hsva__sanity_testing_converted_should_equate_bar_rounding(self): + self.colorspaces_converted_should_equate_bar_rounding("hsva") + + def test_cmy__sanity_testing_converted_should_equate_bar_rounding(self): + self.colorspaces_converted_should_equate_bar_rounding("cmy") + + def test_i1i2i3__sanity_testing_converted_should_equate_bar_rounding(self): + self.colorspaces_converted_should_equate_bar_rounding("i1i2i3") + + ################################################################################ + + def test_correct_gamma__verified_against_python_implementation(self): + "|tags:slow|" + # gamma_correct defined at top of page + + gammas = [i / 10.0 for i in range(1, 31)] # [0.1 ... 3.0] + gammas_len = len(gammas) + + for i, c in enumerate(rgba_combos_Color_generator()): + gamma = gammas[i % gammas_len] + + corrected = pygame.Color(*[gamma_correct(x, gamma) for x in tuple(c)]) + lib_corrected = c.correct_gamma(gamma) + + self.assertTrue(corrected.r == lib_corrected.r) + self.assertTrue(corrected.g == lib_corrected.g) + self.assertTrue(corrected.b == lib_corrected.b) + self.assertTrue(corrected.a == lib_corrected.a) + + # TODO: test against statically defined verified _correct_ values + # assert corrected.r == 125 etc. + + def test_pickle(self): + import pickle + + c1 = pygame.Color(1, 2, 3, 4) + # c2 = pygame.Color(255,254,253,252) + pickle_string = pickle.dumps(c1) + c1_frompickle = pickle.loads(pickle_string) + self.assertEqual(c1, c1_frompickle) + + ################################################################################ + # only available if ctypes module is also available + + @unittest.skipIf(IS_PYPY, "PyPy has no ctypes") + def test_arraystruct(self): + import pygame.tests.test_utils.arrinter as ai + import ctypes as ct + + c_byte_p = ct.POINTER(ct.c_byte) + c = pygame.Color(5, 7, 13, 23) + flags = ai.PAI_CONTIGUOUS | ai.PAI_FORTRAN | ai.PAI_ALIGNED | ai.PAI_NOTSWAPPED + for i in range(1, 5): + c.set_length(i) + inter = ai.ArrayInterface(c) + self.assertEqual(inter.two, 2) + self.assertEqual(inter.nd, 1) + self.assertEqual(inter.typekind, "u") + self.assertEqual(inter.itemsize, 1) + self.assertEqual(inter.flags, flags) + self.assertEqual(inter.shape[0], i) + self.assertEqual(inter.strides[0], 1) + data = ct.cast(inter.data, c_byte_p) + for j in range(i): + self.assertEqual(data[j], c[j]) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + def test_newbuf(self): + from pygame.tests.test_utils import buftools + from ctypes import cast, POINTER, c_uint8 + + class ColorImporter(buftools.Importer): + def __init__(self, color, flags): + super().__init__(color, flags) + self.items = cast(self.buf, POINTER(c_uint8)) + + def __getitem__(self, index): + if 0 <= index < 4: + return self.items[index] + raise IndexError(f"valid index values are between 0 and 3: got {index}") + + def __setitem__(self, index, value): + if 0 <= index < 4: + self.items[index] = value + else: + raise IndexError( + f"valid index values are between 0 and 3: got {index}" + ) + + c = pygame.Color(50, 100, 150, 200) + imp = ColorImporter(c, buftools.PyBUF_SIMPLE) + self.assertTrue(imp.obj is c) + self.assertEqual(imp.ndim, 0) + self.assertEqual(imp.itemsize, 1) + self.assertEqual(imp.len, 4) + self.assertTrue(imp.readonly) + self.assertTrue(imp.format is None) + self.assertTrue(imp.shape is None) + self.assertTrue(imp.strides is None) + self.assertTrue(imp.suboffsets is None) + for i in range(4): + self.assertEqual(c[i], imp[i]) + imp[0] = 60 + self.assertEqual(c.r, 60) + imp[1] = 110 + self.assertEqual(c.g, 110) + imp[2] = 160 + self.assertEqual(c.b, 160) + imp[3] = 210 + self.assertEqual(c.a, 210) + imp = ColorImporter(c, buftools.PyBUF_FORMAT) + self.assertEqual(imp.ndim, 0) + self.assertEqual(imp.itemsize, 1) + self.assertEqual(imp.len, 4) + self.assertEqual(imp.format, "B") + self.assertEqual(imp.ndim, 0) + self.assertEqual(imp.itemsize, 1) + self.assertEqual(imp.len, 4) + imp = ColorImporter(c, buftools.PyBUF_ND) + self.assertEqual(imp.ndim, 1) + self.assertEqual(imp.itemsize, 1) + self.assertEqual(imp.len, 4) + self.assertTrue(imp.format is None) + self.assertEqual(imp.shape, (4,)) + self.assertEqual(imp.strides, None) + imp = ColorImporter(c, buftools.PyBUF_STRIDES) + self.assertEqual(imp.ndim, 1) + self.assertTrue(imp.format is None) + self.assertEqual(imp.shape, (4,)) + self.assertEqual(imp.strides, (1,)) + imp = ColorImporter(c, buftools.PyBUF_C_CONTIGUOUS) + self.assertEqual(imp.ndim, 1) + imp = ColorImporter(c, buftools.PyBUF_F_CONTIGUOUS) + self.assertEqual(imp.ndim, 1) + imp = ColorImporter(c, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertEqual(imp.ndim, 1) + for i in range(1, 5): + c.set_length(i) + imp = ColorImporter(c, buftools.PyBUF_ND) + self.assertEqual(imp.ndim, 1) + self.assertEqual(imp.len, i) + self.assertEqual(imp.shape, (i,)) + self.assertRaises(BufferError, ColorImporter, c, buftools.PyBUF_WRITABLE) + + def test_color_iter(self): + c = pygame.Color(50, 100, 150, 200) + + # call __iter__ explicitly to test that it is defined + color_iterator = c.__iter__() + for i, val in enumerate(color_iterator): + self.assertEqual(c[i], val) + + def test_color_contains(self): + c = pygame.Color(50, 60, 70) + + # call __contains__ explicitly to test that it is defined + self.assertTrue(c.__contains__(50)) + self.assertTrue(60 in c) + self.assertTrue(70 in c) + self.assertFalse(100 in c) + self.assertFalse(c.__contains__(10)) + + self.assertRaises(TypeError, lambda: "string" in c) + self.assertRaises(TypeError, lambda: 3.14159 in c) + + def test_grayscale(self): + Color = pygame.color.Color + + color = Color(255, 0, 0, 255) + self.assertEqual(color.grayscale(), Color(76, 76, 76, 255)) + color = Color(3, 5, 7, 255) + self.assertEqual(color.grayscale(), Color(4, 4, 4, 255)) + color = Color(3, 5, 70, 255) + self.assertEqual(color.grayscale(), Color(11, 11, 11, 255)) + color = Color(3, 50, 70, 255) + self.assertEqual(color.grayscale(), Color(38, 38, 38, 255)) + color = Color(30, 50, 70, 255) + self.assertEqual(color.grayscale(), Color(46, 46, 46, 255)) + + color = Color(255, 0, 0, 144) + self.assertEqual(color.grayscale(), Color(76, 76, 76, 144)) + color = Color(3, 5, 7, 144) + self.assertEqual(color.grayscale(), Color(4, 4, 4, 144)) + color = Color(3, 5, 70, 144) + self.assertEqual(color.grayscale(), Color(11, 11, 11, 144)) + color = Color(3, 50, 70, 144) + self.assertEqual(color.grayscale(), Color(38, 38, 38, 144)) + color = Color(30, 50, 70, 144) + self.assertEqual(color.grayscale(), Color(46, 46, 46, 144)) + + def test_lerp(self): + # setup + Color = pygame.color.Color + + color0 = Color(0, 0, 0, 0) + color128 = Color(128, 128, 128, 128) + color255 = Color(255, 255, 255, 255) + color100 = Color(100, 100, 100, 100) + + # type checking + self.assertTrue(isinstance(color0.lerp(color128, 0.5), Color)) + + # common value testing + self.assertEqual(color0.lerp(color128, 0.5), Color(64, 64, 64, 64)) + self.assertEqual(color0.lerp(color128, 0.5), Color(64, 64, 64, 64)) + self.assertEqual(color128.lerp(color255, 0.5), Color(192, 192, 192, 192)) + self.assertEqual(color0.lerp(color255, 0.5), Color(128, 128, 128, 128)) + + # testing extremes + self.assertEqual(color0.lerp(color100, 0), color0) + self.assertEqual(color0.lerp(color100, 0.01), Color(1, 1, 1, 1)) + self.assertEqual(color0.lerp(color100, 0.99), Color(99, 99, 99, 99)) + self.assertEqual(color0.lerp(color100, 1), color100) + + # kwarg testing + self.assertEqual(color0.lerp(color=color100, amount=0.5), Color(50, 50, 50, 50)) + self.assertEqual(color0.lerp(amount=0.5, color=color100), Color(50, 50, 50, 50)) + + # invalid input testing + self.assertRaises(ValueError, lambda: color0.lerp(color128, 2.5)) + self.assertRaises(ValueError, lambda: color0.lerp(color128, -0.5)) + self.assertRaises(ValueError, lambda: color0.lerp((256, 0, 0, 0), 0.5)) + self.assertRaises(ValueError, lambda: color0.lerp((0, 256, 0, 0), 0.5)) + self.assertRaises(ValueError, lambda: color0.lerp((0, 0, 256, 0), 0.5)) + self.assertRaises(ValueError, lambda: color0.lerp((0, 0, 0, 256), 0.5)) + self.assertRaises(TypeError, lambda: color0.lerp(0.2, 0.5)) + + def test_premul_alpha(self): + # setup + Color = pygame.color.Color + + color0 = Color(0, 0, 0, 0) + alpha0 = Color(255, 255, 255, 0) + alpha49 = Color(255, 0, 0, 49) + alpha67 = Color(0, 255, 0, 67) + alpha73 = Color(0, 0, 255, 73) + alpha128 = Color(255, 255, 255, 128) + alpha199 = Color(255, 255, 255, 199) + alpha255 = Color(128, 128, 128, 255) + + # type checking + self.assertTrue(isinstance(color0.premul_alpha(), Color)) + + # hand crafted value testing + self.assertEqual(alpha0.premul_alpha(), Color(0, 0, 0, 0)) + self.assertEqual(alpha49.premul_alpha(), Color(49, 0, 0, 49)) + self.assertEqual(alpha67.premul_alpha(), Color(0, 67, 0, 67)) + self.assertEqual(alpha73.premul_alpha(), Color(0, 0, 73, 73)) + self.assertEqual(alpha128.premul_alpha(), Color(128, 128, 128, 128)) + self.assertEqual(alpha199.premul_alpha(), Color(199, 199, 199, 199)) + self.assertEqual(alpha255.premul_alpha(), Color(128, 128, 128, 255)) + + # full range of alpha auto sub-testing + test_colors = [ + (200, 30, 74), + (76, 83, 24), + (184, 21, 6), + (74, 4, 74), + (76, 83, 24), + (184, 21, 234), + (160, 30, 74), + (96, 147, 204), + (198, 201, 60), + (132, 89, 74), + (245, 9, 224), + (184, 112, 6), + ] + + for r, g, b in test_colors: + for a in range(255): + with self.subTest(r=r, g=g, b=b, a=a): + alpha = a / 255.0 + self.assertEqual( + Color(r, g, b, a).premul_alpha(), + Color( + ((r + 1) * a) >> 8, + ((g + 1) * a) >> 8, + ((b + 1) * a) >> 8, + a, + ), + ) + + def test_update(self): + c = pygame.color.Color(0, 0, 0) + c.update(1, 2, 3, 4) + + self.assertEqual(c.r, 1) + self.assertEqual(c.g, 2) + self.assertEqual(c.b, 3) + self.assertEqual(c.a, 4) + + c = pygame.color.Color(0, 0, 0) + c.update([1, 2, 3, 4]) + + self.assertEqual(c.r, 1) + self.assertEqual(c.g, 2) + self.assertEqual(c.b, 3) + self.assertEqual(c.a, 4) + + c = pygame.color.Color(0, 0, 0) + c2 = pygame.color.Color(1, 2, 3, 4) + c.update(c2) + + self.assertEqual(c.r, 1) + self.assertEqual(c.g, 2) + self.assertEqual(c.b, 3) + self.assertEqual(c.a, 4) + + c = pygame.color.Color(1, 1, 1) + c.update("black") + + self.assertEqual(c.r, 0) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 0) + self.assertEqual(c.a, 255) + + c = pygame.color.Color(0, 0, 0, 120) + c.set_length(3) + c.update(1, 2, 3) + self.assertEqual(len(c), 3) + c.set_length(4) + self.assertEqual(c[3], 120) + + c.set_length(3) + c.update(1, 2, 3, 4) + self.assertEqual(len(c), 4) + + def test_collection_abc(self): + c = pygame.Color(64, 70, 75, 255) + self.assertTrue(isinstance(c, Collection)) + self.assertFalse(isinstance(c, Sequence)) + + +class SubclassTest(unittest.TestCase): + class MyColor(pygame.Color): + def __init__(self, *args, **kwds): + super(SubclassTest.MyColor, self).__init__(*args, **kwds) + self.an_attribute = True + + def test_add(self): + mc1 = self.MyColor(128, 128, 128, 255) + self.assertTrue(mc1.an_attribute) + c2 = pygame.Color(64, 64, 64, 255) + mc2 = mc1 + c2 + self.assertTrue(isinstance(mc2, self.MyColor)) + self.assertRaises(AttributeError, getattr, mc2, "an_attribute") + c3 = c2 + mc1 + self.assertTrue(type(c3) is pygame.Color) + + def test_sub(self): + mc1 = self.MyColor(128, 128, 128, 255) + self.assertTrue(mc1.an_attribute) + c2 = pygame.Color(64, 64, 64, 255) + mc2 = mc1 - c2 + self.assertTrue(isinstance(mc2, self.MyColor)) + self.assertRaises(AttributeError, getattr, mc2, "an_attribute") + c3 = c2 - mc1 + self.assertTrue(type(c3) is pygame.Color) + + def test_mul(self): + mc1 = self.MyColor(128, 128, 128, 255) + self.assertTrue(mc1.an_attribute) + c2 = pygame.Color(64, 64, 64, 255) + mc2 = mc1 * c2 + self.assertTrue(isinstance(mc2, self.MyColor)) + self.assertRaises(AttributeError, getattr, mc2, "an_attribute") + c3 = c2 * mc1 + self.assertTrue(type(c3) is pygame.Color) + + def test_div(self): + mc1 = self.MyColor(128, 128, 128, 255) + self.assertTrue(mc1.an_attribute) + c2 = pygame.Color(64, 64, 64, 255) + mc2 = mc1 // c2 + self.assertTrue(isinstance(mc2, self.MyColor)) + self.assertRaises(AttributeError, getattr, mc2, "an_attribute") + c3 = c2 // mc1 + self.assertTrue(type(c3) is pygame.Color) + + def test_mod(self): + mc1 = self.MyColor(128, 128, 128, 255) + self.assertTrue(mc1.an_attribute) + c2 = pygame.Color(64, 64, 64, 255) + mc2 = mc1 % c2 + self.assertTrue(isinstance(mc2, self.MyColor)) + self.assertRaises(AttributeError, getattr, mc2, "an_attribute") + c3 = c2 % mc1 + self.assertTrue(type(c3) is pygame.Color) + + def test_inv(self): + mc1 = self.MyColor(64, 64, 64, 64) + self.assertTrue(mc1.an_attribute) + mc2 = ~mc1 + self.assertTrue(isinstance(mc2, self.MyColor)) + self.assertRaises(AttributeError, getattr, mc2, "an_attribute") + + def test_correct_gamma(self): + mc1 = self.MyColor(64, 70, 75, 255) + self.assertTrue(mc1.an_attribute) + mc2 = mc1.correct_gamma(0.03) + self.assertTrue(isinstance(mc2, self.MyColor)) + self.assertRaises(AttributeError, getattr, mc2, "an_attribute") + + def test_collection_abc(self): + mc1 = self.MyColor(64, 70, 75, 255) + self.assertTrue(isinstance(mc1, Collection)) + self.assertFalse(isinstance(mc1, Sequence)) + + +################################################################################ + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/constants_test.py b/.venv/Lib/site-packages/pygame/tests/constants_test.py new file mode 100644 index 00000000..a028f982 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/constants_test.py @@ -0,0 +1,426 @@ +import unittest +import pygame.constants + + +# K_* and KSCAN_* common names. +K_AND_KSCAN_COMMON_NAMES = ( + "UNKNOWN", + "BACKSPACE", + "TAB", + "CLEAR", + "RETURN", + "PAUSE", + "ESCAPE", + "SPACE", + "COMMA", + "MINUS", + "PERIOD", + "SLASH", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "SEMICOLON", + "EQUALS", + "LEFTBRACKET", + "BACKSLASH", + "RIGHTBRACKET", + "DELETE", + "KP0", + "KP1", + "KP2", + "KP3", + "KP4", + "KP5", + "KP6", + "KP7", + "KP8", + "KP9", + "KP_PERIOD", + "KP_DIVIDE", + "KP_MULTIPLY", + "KP_MINUS", + "KP_PLUS", + "KP_ENTER", + "KP_EQUALS", + "UP", + "DOWN", + "RIGHT", + "LEFT", + "INSERT", + "HOME", + "END", + "PAGEUP", + "PAGEDOWN", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "F11", + "F12", + "F13", + "F14", + "F15", + "NUMLOCK", + "CAPSLOCK", + "SCROLLOCK", + "RSHIFT", + "LSHIFT", + "RCTRL", + "LCTRL", + "RALT", + "LALT", + "RMETA", + "LMETA", + "LSUPER", + "RSUPER", + "MODE", + "HELP", + "PRINT", + "SYSREQ", + "BREAK", + "MENU", + "POWER", + "EURO", + "KP_0", + "KP_1", + "KP_2", + "KP_3", + "KP_4", + "KP_5", + "KP_6", + "KP_7", + "KP_8", + "KP_9", + "NUMLOCKCLEAR", + "SCROLLLOCK", + "RGUI", + "LGUI", + "PRINTSCREEN", + "CURRENCYUNIT", + "CURRENCYSUBUNIT", +) + +# Constants that have the same value. +K_AND_KSCAN_COMMON_OVERLAPS = ( + ("KP0", "KP_0"), + ("KP1", "KP_1"), + ("KP2", "KP_2"), + ("KP3", "KP_3"), + ("KP4", "KP_4"), + ("KP5", "KP_5"), + ("KP6", "KP_6"), + ("KP7", "KP_7"), + ("KP8", "KP_8"), + ("KP9", "KP_9"), + ("NUMLOCK", "NUMLOCKCLEAR"), + ("SCROLLOCK", "SCROLLLOCK"), + ("LSUPER", "LMETA", "LGUI"), + ("RSUPER", "RMETA", "RGUI"), + ("PRINT", "PRINTSCREEN"), + ("BREAK", "PAUSE"), + ("EURO", "CURRENCYUNIT"), +) + + +def create_overlap_set(constant_names): + """Helper function to find overlapping constant values/names. + + Returns a set of fronzensets: + set(frozenset(names of overlapping constants), ...) + """ + # Create an overlap dict. + overlap_dict = {} + + for name in constant_names: + value = getattr(pygame.constants, name) + overlap_dict.setdefault(value, set()).add(name) + + # Get all entries with more than 1 value. + overlaps = set() + + for overlap_names in overlap_dict.values(): + if len(overlap_names) > 1: + overlaps.add(frozenset(overlap_names)) + + return overlaps + + +class KConstantsTests(unittest.TestCase): + """Test K_* (key) constants.""" + + # K_* specific names. + K_SPECIFIC_NAMES = ( + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "QUOTE", + "BACKQUOTE", + "EXCLAIM", + "QUOTEDBL", + "HASH", + "DOLLAR", + "AMPERSAND", + "LEFTPAREN", + "RIGHTPAREN", + "ASTERISK", + "PLUS", + "COLON", + "LESS", + "GREATER", + "QUESTION", + "AT", + "CARET", + "UNDERSCORE", + "PERCENT", + ) + + # Create a sequence of all the K_* constant names. + K_NAMES = tuple("K_" + n for n in K_AND_KSCAN_COMMON_NAMES + K_SPECIFIC_NAMES) + + def test_k__existence(self): + """Ensures K constants exist.""" + for name in self.K_NAMES: + self.assertTrue(hasattr(pygame.constants, name), f"missing constant {name}") + + def test_k__type(self): + """Ensures K constants are the correct type.""" + for name in self.K_NAMES: + value = getattr(pygame.constants, name) + + self.assertIs(type(value), int) + + def test_k__value_overlap(self): + """Ensures no unexpected K constant values overlap.""" + EXPECTED_OVERLAPS = { + frozenset("K_" + n for n in item) for item in K_AND_KSCAN_COMMON_OVERLAPS + } + + overlaps = create_overlap_set(self.K_NAMES) + + self.assertSetEqual(overlaps, EXPECTED_OVERLAPS) + + +class KscanConstantsTests(unittest.TestCase): + """Test KSCAN_* (scancode) constants.""" + + # KSCAN_* specific names. + KSCAN_SPECIFIC_NAMES = ( + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "APOSTROPHE", + "GRAVE", + "INTERNATIONAL1", + "INTERNATIONAL2", + "INTERNATIONAL3", + "INTERNATIONAL4", + "INTERNATIONAL5", + "INTERNATIONAL6", + "INTERNATIONAL7", + "INTERNATIONAL8", + "INTERNATIONAL9", + "LANG1", + "LANG2", + "LANG3", + "LANG4", + "LANG5", + "LANG6", + "LANG7", + "LANG8", + "LANG9", + "NONUSBACKSLASH", + "NONUSHASH", + ) + + # Create a sequence of all the KSCAN_* constant names. + KSCAN_NAMES = tuple( + "KSCAN_" + n for n in K_AND_KSCAN_COMMON_NAMES + KSCAN_SPECIFIC_NAMES + ) + + def test_kscan__existence(self): + """Ensures KSCAN constants exist.""" + for name in self.KSCAN_NAMES: + self.assertTrue(hasattr(pygame.constants, name), f"missing constant {name}") + + def test_kscan__type(self): + """Ensures KSCAN constants are the correct type.""" + for name in self.KSCAN_NAMES: + value = getattr(pygame.constants, name) + + self.assertIs(type(value), int) + + def test_kscan__value_overlap(self): + """Ensures no unexpected KSCAN constant values overlap.""" + EXPECTED_OVERLAPS = { + frozenset("KSCAN_" + n for n in item) + for item in K_AND_KSCAN_COMMON_OVERLAPS + } + + overlaps = create_overlap_set(self.KSCAN_NAMES) + + self.assertSetEqual(overlaps, EXPECTED_OVERLAPS) + + +class KmodConstantsTests(unittest.TestCase): + """Test KMOD_* (key modifier) constants.""" + + # KMOD_* constant names. + KMOD_CONSTANTS = ( + "KMOD_NONE", + "KMOD_LSHIFT", + "KMOD_RSHIFT", + "KMOD_SHIFT", + "KMOD_LCTRL", + "KMOD_RCTRL", + "KMOD_CTRL", + "KMOD_LALT", + "KMOD_RALT", + "KMOD_ALT", + "KMOD_LMETA", + "KMOD_RMETA", + "KMOD_META", + "KMOD_NUM", + "KMOD_CAPS", + "KMOD_MODE", + "KMOD_LGUI", + "KMOD_RGUI", + "KMOD_GUI", + ) + + def test_kmod__existence(self): + """Ensures KMOD constants exist.""" + for name in self.KMOD_CONSTANTS: + self.assertTrue(hasattr(pygame.constants, name), f"missing constant {name}") + + def test_kmod__type(self): + """Ensures KMOD constants are the correct type.""" + for name in self.KMOD_CONSTANTS: + value = getattr(pygame.constants, name) + + self.assertIs(type(value), int) + + def test_kmod__value_overlap(self): + """Ensures no unexpected KMOD constant values overlap.""" + # KMODs that have the same values. + EXPECTED_OVERLAPS = { + frozenset(["KMOD_LGUI", "KMOD_LMETA"]), + frozenset(["KMOD_RGUI", "KMOD_RMETA"]), + frozenset(["KMOD_GUI", "KMOD_META"]), + } + + overlaps = create_overlap_set(self.KMOD_CONSTANTS) + + self.assertSetEqual(overlaps, EXPECTED_OVERLAPS) + + def test_kmod__no_bitwise_overlap(self): + """Ensures certain KMOD constants have no overlapping bits.""" + NO_BITWISE_OVERLAP = ( + "KMOD_NONE", + "KMOD_LSHIFT", + "KMOD_RSHIFT", + "KMOD_LCTRL", + "KMOD_RCTRL", + "KMOD_LALT", + "KMOD_RALT", + "KMOD_LMETA", + "KMOD_RMETA", + "KMOD_NUM", + "KMOD_CAPS", + "KMOD_MODE", + ) + + kmods = 0 + + for name in NO_BITWISE_OVERLAP: + value = getattr(pygame.constants, name) + + self.assertFalse(kmods & value) + + kmods |= value + + def test_kmod__bitwise_overlap(self): + """Ensures certain KMOD constants have overlapping bits.""" + # KMODS that are comprised of other KMODs. + KMOD_COMPRISED_DICT = { + "KMOD_SHIFT": ("KMOD_LSHIFT", "KMOD_RSHIFT"), + "KMOD_CTRL": ("KMOD_LCTRL", "KMOD_RCTRL"), + "KMOD_ALT": ("KMOD_LALT", "KMOD_RALT"), + "KMOD_META": ("KMOD_LMETA", "KMOD_RMETA"), + "KMOD_GUI": ("KMOD_LGUI", "KMOD_RGUI"), + } + + for base_name, seq_names in KMOD_COMPRISED_DICT.items(): + expected_value = 0 # Reset. + + for name in seq_names: + expected_value |= getattr(pygame.constants, name) + + value = getattr(pygame.constants, base_name) + + self.assertEqual(value, expected_value) + + +################################################################################ + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/controller_test.py b/.venv/Lib/site-packages/pygame/tests/controller_test.py new file mode 100644 index 00000000..f05c00c5 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/controller_test.py @@ -0,0 +1,357 @@ +import unittest +import pygame +import pygame._sdl2.controller as controller +from pygame.tests.test_utils import prompt, question + + +class ControllerModuleTest(unittest.TestCase): + def setUp(self): + controller.init() + + def tearDown(self): + controller.quit() + + def test_init(self): + controller.quit() + controller.init() + self.assertTrue(controller.get_init()) + + def test_init__multiple(self): + controller.init() + controller.init() + self.assertTrue(controller.get_init()) + + def test_quit(self): + controller.quit() + self.assertFalse(controller.get_init()) + + def test_quit__multiple(self): + controller.quit() + controller.quit() + self.assertFalse(controller.get_init()) + + def test_get_init(self): + self.assertTrue(controller.get_init()) + + def test_get_eventstate(self): + controller.set_eventstate(True) + self.assertTrue(controller.get_eventstate()) + + controller.set_eventstate(False) + self.assertFalse(controller.get_eventstate()) + + controller.set_eventstate(True) + + def test_get_count(self): + self.assertGreaterEqual(controller.get_count(), 0) + + def test_is_controller(self): + for i in range(controller.get_count()): + if controller.is_controller(i): + c = controller.Controller(i) + self.assertIsInstance(c, controller.Controller) + c.quit() + else: + with self.assertRaises(pygame._sdl2.sdl2.error): + c = controller.Controller(i) + + with self.assertRaises(TypeError): + controller.is_controller("Test") + + def test_name_forindex(self): + self.assertIsNone(controller.name_forindex(-1)) + + +class ControllerTypeTest(unittest.TestCase): + def setUp(self): + controller.init() + + def tearDown(self): + controller.quit() + + def _get_first_controller(self): + for i in range(controller.get_count()): + if controller.is_controller(i): + return controller.Controller(i) + + def test_construction(self): + c = self._get_first_controller() + if c: + self.assertIsInstance(c, controller.Controller) + else: + self.skipTest("No controller connected") + + def test__auto_init(self): + c = self._get_first_controller() + if c: + self.assertTrue(c.get_init()) + else: + self.skipTest("No controller connected") + + def test_get_init(self): + c = self._get_first_controller() + if c: + self.assertTrue(c.get_init()) + c.quit() + self.assertFalse(c.get_init()) + else: + self.skipTest("No controller connected") + + def test_from_joystick(self): + for i in range(controller.get_count()): + if controller.is_controller(i): + joy = pygame.joystick.Joystick(i) + break + else: + self.skipTest("No controller connected") + + c = controller.Controller.from_joystick(joy) + self.assertIsInstance(c, controller.Controller) + + def test_as_joystick(self): + c = self._get_first_controller() + if c: + joy = c.as_joystick() + self.assertIsInstance(joy, type(pygame.joystick.Joystick(0))) + else: + self.skipTest("No controller connected") + + def test_get_mapping(self): + c = self._get_first_controller() + if c: + mapping = c.get_mapping() + self.assertIsInstance(mapping, dict) + self.assertIsNotNone(mapping["a"]) + else: + self.skipTest("No controller connected") + + def test_set_mapping(self): + c = self._get_first_controller() + if c: + mapping = c.get_mapping() + mapping["a"] = "b3" + mapping["y"] = "b0" + c.set_mapping(mapping) + new_mapping = c.get_mapping() + + self.assertEqual(len(mapping), len(new_mapping)) + for i in mapping: + if mapping[i] not in ("a", "y"): + self.assertEqual(mapping[i], new_mapping[i]) + else: + if i == "a": + self.assertEqual(new_mapping[i], mapping["y"]) + else: + self.assertEqual(new_mapping[i], mapping["a"]) + else: + self.skipTest("No controller connected") + + +class ControllerInteractiveTest(unittest.TestCase): + __tags__ = ["interactive"] + + def _get_first_controller(self): + for i in range(controller.get_count()): + if controller.is_controller(i): + return controller.Controller(i) + + def setUp(self): + controller.init() + + def tearDown(self): + controller.quit() + + def test__get_count_interactive(self): + prompt( + "Please connect at least one controller " + "before the test for controller.get_count() starts" + ) + + # Reset the number of joysticks counted + controller.quit() + controller.init() + + joystick_num = controller.get_count() + ans = question( + "get_count() thinks there are {} joysticks " + "connected. Is that correct?".format(joystick_num) + ) + + self.assertTrue(ans) + + def test_set_eventstate_on_interactive(self): + c = self._get_first_controller() + if not c: + self.skipTest("No controller connected") + + pygame.display.init() + pygame.font.init() + + screen = pygame.display.set_mode((400, 400)) + font = pygame.font.Font(None, 20) + running = True + + screen.fill((255, 255, 255)) + screen.blit( + font.render("Press button 'x' (on ps4) or 'a' (on xbox).", True, (0, 0, 0)), + (0, 0), + ) + pygame.display.update() + + controller.set_eventstate(True) + + while running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + + if event.type == pygame.CONTROLLERBUTTONDOWN: + running = False + + pygame.display.quit() + pygame.font.quit() + + def test_set_eventstate_off_interactive(self): + c = self._get_first_controller() + if not c: + self.skipTest("No controller connected") + + pygame.display.init() + pygame.font.init() + + screen = pygame.display.set_mode((400, 400)) + font = pygame.font.Font(None, 20) + running = True + + screen.fill((255, 255, 255)) + screen.blit( + font.render("Press button 'x' (on ps4) or 'a' (on xbox).", True, (0, 0, 0)), + (0, 0), + ) + pygame.display.update() + + controller.set_eventstate(False) + + while running: + for event in pygame.event.get(pygame.QUIT): + if event: + running = False + + if c.get_button(pygame.CONTROLLER_BUTTON_A): + if pygame.event.peek(pygame.CONTROLLERBUTTONDOWN): + pygame.display.quit() + pygame.font.quit() + self.fail() + else: + running = False + + pygame.display.quit() + pygame.font.quit() + + def test_get_button_interactive(self): + c = self._get_first_controller() + if not c: + self.skipTest("No controller connected") + + pygame.display.init() + pygame.font.init() + + screen = pygame.display.set_mode((400, 400)) + font = pygame.font.Font(None, 20) + running = True + + label1 = font.render( + "Press button 'x' (on ps4) or 'a' (on xbox).", True, (0, 0, 0) + ) + + label2 = font.render( + 'The two values should match up. Press "y" or "n" to confirm.', + True, + (0, 0, 0), + ) + + is_pressed = [False, False] # event, get_button() + while running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + if event.type == pygame.CONTROLLERBUTTONDOWN and event.button == 0: + is_pressed[0] = True + if event.type == pygame.CONTROLLERBUTTONUP and event.button == 0: + is_pressed[0] = False + + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_y: + running = False + if event.key == pygame.K_n: + running = False + pygame.display.quit() + pygame.font.quit() + self.fail() + + is_pressed[1] = c.get_button(pygame.CONTROLLER_BUTTON_A) + + screen.fill((255, 255, 255)) + screen.blit(label1, (0, 0)) + screen.blit(label2, (0, 20)) + screen.blit(font.render(str(is_pressed), True, (0, 0, 0)), (0, 40)) + pygame.display.update() + + pygame.display.quit() + pygame.font.quit() + + def test_get_axis_interactive(self): + c = self._get_first_controller() + if not c: + self.skipTest("No controller connected") + + pygame.display.init() + pygame.font.init() + + screen = pygame.display.set_mode((400, 400)) + font = pygame.font.Font(None, 20) + running = True + + label1 = font.render( + "Press down the right trigger. The value on-screen should", True, (0, 0, 0) + ) + + label2 = font.render( + "indicate how far the trigger is pressed down. This value should", + True, + (0, 0, 0), + ) + + label3 = font.render( + 'be in the range of 0-32767. Press "y" or "n" to confirm.', True, (0, 0, 0) + ) + + while running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_y: + running = False + if event.key == pygame.K_n: + running = False + pygame.display.quit() + pygame.font.quit() + self.fail() + + right_trigger = c.get_axis(pygame.CONTROLLER_AXIS_TRIGGERRIGHT) + + screen.fill((255, 255, 255)) + screen.blit(label1, (0, 0)) + screen.blit(label2, (0, 20)) + screen.blit(label3, (0, 40)) + screen.blit(font.render(str(right_trigger), True, (0, 0, 0)), (0, 60)) + pygame.display.update() + + pygame.display.quit() + pygame.font.quit() + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/cursors_test.py b/.venv/Lib/site-packages/pygame/tests/cursors_test.py new file mode 100644 index 00000000..8132c513 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/cursors_test.py @@ -0,0 +1,290 @@ +import unittest +from pygame.tests.test_utils import fixture_path +import pygame + + +class CursorsModuleTest(unittest.TestCase): + def test_compile(self): + # __doc__ (as of 2008-06-25) for pygame.cursors.compile: + + # pygame.cursors.compile(strings, black, white,xor) -> data, mask + # compile cursor strings into cursor data + # + # This takes a set of strings with equal length and computes + # the binary data for that cursor. The string widths must be + # divisible by 8. + # + # The black and white arguments are single letter strings that + # tells which characters will represent black pixels, and which + # characters represent white pixels. All other characters are + # considered clear. + # + # This returns a tuple containing the cursor data and cursor mask + # data. Both these arguments are used when setting a cursor with + # pygame.mouse.set_cursor(). + + # Various types of input strings + test_cursor1 = ("X.X.XXXX", "XXXXXX..", " XXXX ") + + test_cursor2 = ( + "X.X.XXXX", + "XXXXXX..", + "XXXXXX ", + "XXXXXX..", + "XXXXXX..", + "XXXXXX", + "XXXXXX..", + "XXXXXX..", + ) + test_cursor3 = (".XX.", " ", ".. ", "X.. X") + + # Test such that total number of strings is not divisible by 8 + with self.assertRaises(ValueError): + pygame.cursors.compile(test_cursor1) + + # Test such that size of individual string is not divisible by 8 + with self.assertRaises(ValueError): + pygame.cursors.compile(test_cursor2) + + # Test such that neither size of individual string nor total number of strings is divisible by 8 + with self.assertRaises(ValueError): + pygame.cursors.compile(test_cursor3) + + # Test that checks whether the byte data from compile function is equal to actual byte data + actual_byte_data = ( + 192, + 0, + 0, + 224, + 0, + 0, + 240, + 0, + 0, + 216, + 0, + 0, + 204, + 0, + 0, + 198, + 0, + 0, + 195, + 0, + 0, + 193, + 128, + 0, + 192, + 192, + 0, + 192, + 96, + 0, + 192, + 48, + 0, + 192, + 56, + 0, + 192, + 248, + 0, + 220, + 192, + 0, + 246, + 96, + 0, + 198, + 96, + 0, + 6, + 96, + 0, + 3, + 48, + 0, + 3, + 48, + 0, + 1, + 224, + 0, + 1, + 128, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ), ( + 192, + 0, + 0, + 224, + 0, + 0, + 240, + 0, + 0, + 248, + 0, + 0, + 252, + 0, + 0, + 254, + 0, + 0, + 255, + 0, + 0, + 255, + 128, + 0, + 255, + 192, + 0, + 255, + 224, + 0, + 255, + 240, + 0, + 255, + 248, + 0, + 255, + 248, + 0, + 255, + 192, + 0, + 247, + 224, + 0, + 199, + 224, + 0, + 7, + 224, + 0, + 3, + 240, + 0, + 3, + 240, + 0, + 1, + 224, + 0, + 1, + 128, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ) + + cursor = pygame.cursors.compile(pygame.cursors.thickarrow_strings) + self.assertEqual(cursor, actual_byte_data) + + # Test such that cursor byte data obtained from compile function is valid in pygame.mouse.set_cursor() + pygame.display.init() + try: + pygame.mouse.set_cursor((24, 24), (0, 0), *cursor) + except pygame.error as e: + if "not currently supported" in str(e): + unittest.skip("skipping test as set_cursor() is not supported") + finally: + pygame.display.quit() + + ################################################################################ + + def test_load_xbm(self): + # __doc__ (as of 2008-06-25) for pygame.cursors.load_xbm: + + # pygame.cursors.load_xbm(cursorfile, maskfile) -> cursor_args + # reads a pair of XBM files into set_cursor arguments + # + # Arguments can either be filenames or filelike objects + # with the readlines method. Not largely tested, but + # should work with typical XBM files. + + # Test that load_xbm will take filenames as arguments + cursorfile = fixture_path(r"xbm_cursors/white_sizing.xbm") + maskfile = fixture_path(r"xbm_cursors/white_sizing_mask.xbm") + cursor = pygame.cursors.load_xbm(cursorfile, maskfile) + + # Test that load_xbm will take file objects as arguments + with open(cursorfile) as cursor_f, open(maskfile) as mask_f: + cursor = pygame.cursors.load_xbm(cursor_f, mask_f) + + # Can it load using pathlib.Path? + import pathlib + + cursor = pygame.cursors.load_xbm( + pathlib.Path(cursorfile), pathlib.Path(maskfile) + ) + + # Is it in a format that mouse.set_cursor won't blow up on? + pygame.display.init() + try: + pygame.mouse.set_cursor(*cursor) + except pygame.error as e: + if "not currently supported" in str(e): + unittest.skip("skipping test as set_cursor() is not supported") + finally: + pygame.display.quit() + + def test_Cursor(self): + """Ensure that the cursor object parses information properly""" + + c1 = pygame.cursors.Cursor(pygame.SYSTEM_CURSOR_CROSSHAIR) + + self.assertEqual(c1.data, (pygame.SYSTEM_CURSOR_CROSSHAIR,)) + self.assertEqual(c1.type, "system") + + c2 = pygame.cursors.Cursor(c1) + + self.assertEqual(c1, c2) + + with self.assertRaises(TypeError): + pygame.cursors.Cursor(-34002) + with self.assertRaises(TypeError): + pygame.cursors.Cursor("a", "b", "c", "d") + with self.assertRaises(TypeError): + pygame.cursors.Cursor((2,)) + + c3 = pygame.cursors.Cursor((0, 0), pygame.Surface((20, 20))) + + self.assertEqual(c3.data[0], (0, 0)) + self.assertEqual(c3.data[1].get_size(), (20, 20)) + self.assertEqual(c3.type, "color") + + xormask, andmask = pygame.cursors.compile(pygame.cursors.thickarrow_strings) + c4 = pygame.cursors.Cursor((24, 24), (0, 0), xormask, andmask) + + self.assertEqual(c4.data, ((24, 24), (0, 0), xormask, andmask)) + self.assertEqual(c4.type, "bitmap") + + +################################################################################ + +if __name__ == "__main__": + unittest.main() + +################################################################################ diff --git a/.venv/Lib/site-packages/pygame/tests/display_test.py b/.venv/Lib/site-packages/pygame/tests/display_test.py new file mode 100644 index 00000000..35851fa7 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/display_test.py @@ -0,0 +1,1199 @@ +import unittest +import os +import sys +import time + +import pygame, pygame.transform + +from pygame.tests.test_utils import question + +from pygame import display + + +class DisplayModuleTest(unittest.TestCase): + default_caption = "pygame window" + + def setUp(self): + display.init() + + def tearDown(self): + display.quit() + + def test_Info(self): + inf = pygame.display.Info() + self.assertNotEqual(inf.current_h, -1) + self.assertNotEqual(inf.current_w, -1) + # probably have an older SDL than 1.2.10 if -1. + + screen = pygame.display.set_mode((128, 128)) + inf = pygame.display.Info() + self.assertEqual(inf.current_h, 128) + self.assertEqual(inf.current_w, 128) + + def test_flip(self): + screen = pygame.display.set_mode((100, 100)) + + # test without a change + self.assertIsNone(pygame.display.flip()) + + # test with a change + pygame.Surface.fill(screen, (66, 66, 53)) + self.assertIsNone(pygame.display.flip()) + + # test without display init + pygame.display.quit() + with self.assertRaises(pygame.error): + (pygame.display.flip()) + + # test without window + del screen + with self.assertRaises(pygame.error): + (pygame.display.flip()) + + def test_get_active(self): + """Test the get_active function""" + + # Initially, the display is not active + pygame.display.quit() + self.assertEqual(pygame.display.get_active(), False) + + # get_active defaults to true after a set_mode + pygame.display.init() + pygame.display.set_mode((640, 480)) + self.assertEqual(pygame.display.get_active(), True) + + # get_active after init/quit should be False + # since no display is visible + pygame.display.quit() + pygame.display.init() + self.assertEqual(pygame.display.get_active(), False) + + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") == "dummy", + "requires the SDL_VIDEODRIVER to be a non dummy value", + ) + def test_get_active_iconify(self): + """Test the get_active function after an iconify""" + + # According to the docs, get_active should return + # false if the display is iconified + pygame.display.set_mode((640, 480)) + + pygame.event.clear() + pygame.display.iconify() + + for _ in range(100): + time.sleep(0.01) + pygame.event.pump() + + self.assertEqual(pygame.display.get_active(), False) + + def test_get_caption(self): + screen = display.set_mode((100, 100)) + + self.assertEqual(display.get_caption()[0], self.default_caption) + + def test_set_caption(self): + TEST_CAPTION = "test" + screen = display.set_mode((100, 100)) + + self.assertIsNone(display.set_caption(TEST_CAPTION)) + self.assertEqual(display.get_caption()[0], TEST_CAPTION) + self.assertEqual(display.get_caption()[1], TEST_CAPTION) + + def test_set_caption_kwargs(self): + TEST_CAPTION = "test" + screen = display.set_mode((100, 100)) + + self.assertIsNone(display.set_caption(title=TEST_CAPTION)) + self.assertEqual(display.get_caption()[0], TEST_CAPTION) + self.assertEqual(display.get_caption()[1], TEST_CAPTION) + + def test_caption_unicode(self): + TEST_CAPTION = "台" + display.set_caption(TEST_CAPTION) + self.assertEqual(display.get_caption()[0], TEST_CAPTION) + + def test_get_driver(self): + drivers = [ + "aalib", + "android", + "arm", + "cocoa", + "dga", + "directx", + "directfb", + "dummy", + "emscripten", + "fbcon", + "ggi", + "haiku", + "khronos", + "kmsdrm", + "nacl", + "offscreen", + "pandora", + "psp", + "qnx", + "raspberry", + "svgalib", + "uikit", + "vgl", + "vivante", + "wayland", + "windows", + "windib", + "winrt", + "x11", + ] + driver = display.get_driver() + self.assertIn(driver, drivers) + + display.quit() + with self.assertRaises(pygame.error): + driver = display.get_driver() + + def test_get_init(self): + """Ensures the module's initialization state can be retrieved.""" + # display.init() already called in setUp() + self.assertTrue(display.get_init()) + + # This test can be uncommented when issues #991 and #993 are resolved. + @unittest.skipIf(True, "SDL2 issues") + def test_get_surface(self): + """Ensures get_surface gets the current display surface.""" + lengths = (1, 5, 100) + + for expected_size in ((w, h) for w in lengths for h in lengths): + for expected_depth in (8, 16, 24, 32): + expected_surface = display.set_mode(expected_size, 0, expected_depth) + + surface = pygame.display.get_surface() + + self.assertEqual(surface, expected_surface) + self.assertIsInstance(surface, pygame.Surface) + self.assertEqual(surface.get_size(), expected_size) + self.assertEqual(surface.get_bitsize(), expected_depth) + + def test_get_surface__mode_not_set(self): + """Ensures get_surface handles the display mode not being set.""" + surface = pygame.display.get_surface() + + self.assertIsNone(surface) + + def test_get_wm_info(self): + wm_info = display.get_wm_info() + # Assert function returns a dictionary type + self.assertIsInstance(wm_info, dict) + + wm_info_potential_keys = { + "colorbuffer", + "connection", + "data", + "dfb", + "display", + "framebuffer", + "fswindow", + "hdc", + "hglrc", + "hinstance", + "lock_func", + "resolveFramebuffer", + "shell_surface", + "surface", + "taskHandle", + "unlock_func", + "wimpVersion", + "window", + "wmwindow", + } + + # If any unexpected dict keys are present, they + # will be stored in set wm_info_remaining_keys + wm_info_remaining_keys = set(wm_info.keys()).difference(wm_info_potential_keys) + + # Assert set is empty (& therefore does not + # contain unexpected dict keys) + self.assertFalse(wm_info_remaining_keys) + + @unittest.skipIf( + ( + "skipping for all because some failures on rasppi and maybe other platforms" + or os.environ.get("SDL_VIDEODRIVER") == "dummy" + ), + 'OpenGL requires a non-"dummy" SDL_VIDEODRIVER', + ) + def test_gl_get_attribute(self): + screen = display.set_mode((0, 0), pygame.OPENGL) + + # We create a list where we store the original values of the + # flags before setting them with a different value. + original_values = [] + + original_values.append(pygame.display.gl_get_attribute(pygame.GL_ALPHA_SIZE)) + original_values.append(pygame.display.gl_get_attribute(pygame.GL_DEPTH_SIZE)) + original_values.append(pygame.display.gl_get_attribute(pygame.GL_STENCIL_SIZE)) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_ACCUM_RED_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_ACCUM_GREEN_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_ACCUM_BLUE_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_ACCUM_ALPHA_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_MULTISAMPLEBUFFERS) + ) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_MULTISAMPLESAMPLES) + ) + original_values.append(pygame.display.gl_get_attribute(pygame.GL_STEREO)) + + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_ACCELERATED_VISUAL) + ) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_CONTEXT_MAJOR_VERSION) + ) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_CONTEXT_MINOR_VERSION) + ) + original_values.append(pygame.display.gl_get_attribute(pygame.GL_CONTEXT_FLAGS)) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_CONTEXT_PROFILE_MASK) + ) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_SHARE_WITH_CURRENT_CONTEXT) + ) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_FRAMEBUFFER_SRGB_CAPABLE) + ) + + # Setting the flags with values supposedly different from the original values + + # assign SDL1-supported values with gl_set_attribute + pygame.display.gl_set_attribute(pygame.GL_ALPHA_SIZE, 8) + pygame.display.gl_set_attribute(pygame.GL_DEPTH_SIZE, 24) + pygame.display.gl_set_attribute(pygame.GL_STENCIL_SIZE, 8) + pygame.display.gl_set_attribute(pygame.GL_ACCUM_RED_SIZE, 16) + pygame.display.gl_set_attribute(pygame.GL_ACCUM_GREEN_SIZE, 16) + pygame.display.gl_set_attribute(pygame.GL_ACCUM_BLUE_SIZE, 16) + pygame.display.gl_set_attribute(pygame.GL_ACCUM_ALPHA_SIZE, 16) + pygame.display.gl_set_attribute(pygame.GL_MULTISAMPLEBUFFERS, 1) + pygame.display.gl_set_attribute(pygame.GL_MULTISAMPLESAMPLES, 1) + pygame.display.gl_set_attribute(pygame.GL_STEREO, 0) + pygame.display.gl_set_attribute(pygame.GL_ACCELERATED_VISUAL, 0) + pygame.display.gl_set_attribute(pygame.GL_CONTEXT_MAJOR_VERSION, 1) + pygame.display.gl_set_attribute(pygame.GL_CONTEXT_MINOR_VERSION, 1) + pygame.display.gl_set_attribute(pygame.GL_CONTEXT_FLAGS, 0) + pygame.display.gl_set_attribute(pygame.GL_CONTEXT_PROFILE_MASK, 0) + pygame.display.gl_set_attribute(pygame.GL_SHARE_WITH_CURRENT_CONTEXT, 0) + pygame.display.gl_set_attribute(pygame.GL_FRAMEBUFFER_SRGB_CAPABLE, 0) + + # We create a list where we store the values that we set each flag to + set_values = [8, 24, 8, 16, 16, 16, 16, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0] + + # We create a list where we store the values after getting them + get_values = [] + + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ALPHA_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_DEPTH_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_STENCIL_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_RED_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_GREEN_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_BLUE_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_ALPHA_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_MULTISAMPLEBUFFERS)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_MULTISAMPLESAMPLES)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_STEREO)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCELERATED_VISUAL)) + get_values.append( + pygame.display.gl_get_attribute(pygame.GL_CONTEXT_MAJOR_VERSION) + ) + get_values.append( + pygame.display.gl_get_attribute(pygame.GL_CONTEXT_MINOR_VERSION) + ) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_CONTEXT_FLAGS)) + get_values.append( + pygame.display.gl_get_attribute(pygame.GL_CONTEXT_PROFILE_MASK) + ) + get_values.append( + pygame.display.gl_get_attribute(pygame.GL_SHARE_WITH_CURRENT_CONTEXT) + ) + get_values.append( + pygame.display.gl_get_attribute(pygame.GL_FRAMEBUFFER_SRGB_CAPABLE) + ) + + # We check to see if the values that we get correspond to the values that we set + # them to or to the original values. + for i in range(len(original_values)): + self.assertTrue( + (get_values[i] == original_values[i]) + or (get_values[i] == set_values[i]) + ) + + # test using non-flag argument + with self.assertRaises(TypeError): + pygame.display.gl_get_attribute("DUMMY") + + @unittest.skipIf( + ( + "skipping for all because some failures on rasppi and maybe other platforms" + or os.environ.get("SDL_VIDEODRIVER") == "dummy" + ), + 'OpenGL requires a non-"dummy" SDL_VIDEODRIVER', + ) + def test_gl_get_attribute_kwargs(self): + screen = display.set_mode((0, 0), pygame.OPENGL) + + # We create a list where we store the original values of the + # flags before setting them with a different value. + original_values = [] + + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ALPHA_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_DEPTH_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_STENCIL_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_RED_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_GREEN_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_BLUE_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_ALPHA_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_MULTISAMPLEBUFFERS) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_MULTISAMPLESAMPLES) + ) + original_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_STEREO)) + + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCELERATED_VISUAL) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_MAJOR_VERSION) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_MINOR_VERSION) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_FLAGS) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_PROFILE_MASK) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_SHARE_WITH_CURRENT_CONTEXT) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_FRAMEBUFFER_SRGB_CAPABLE) + ) + + # Setting the flags with values supposedly different from the original values + + # assign SDL1-supported values with gl_set_attribute + pygame.display.gl_set_attribute(flag=pygame.GL_ALPHA_SIZE, value=8) + pygame.display.gl_set_attribute(flag=pygame.GL_DEPTH_SIZE, value=24) + pygame.display.gl_set_attribute(flag=pygame.GL_STENCIL_SIZE, value=8) + pygame.display.gl_set_attribute(flag=pygame.GL_ACCUM_RED_SIZE, value=16) + pygame.display.gl_set_attribute(flag=pygame.GL_ACCUM_GREEN_SIZE, value=16) + pygame.display.gl_set_attribute(flag=pygame.GL_ACCUM_BLUE_SIZE, value=16) + pygame.display.gl_set_attribute(flag=pygame.GL_ACCUM_ALPHA_SIZE, value=16) + pygame.display.gl_set_attribute(flag=pygame.GL_MULTISAMPLEBUFFERS, value=1) + pygame.display.gl_set_attribute(flag=pygame.GL_MULTISAMPLESAMPLES, value=1) + pygame.display.gl_set_attribute(flag=pygame.GL_STEREO, value=0) + pygame.display.gl_set_attribute(flag=pygame.GL_ACCELERATED_VISUAL, value=0) + pygame.display.gl_set_attribute(flag=pygame.GL_CONTEXT_MAJOR_VERSION, value=1) + pygame.display.gl_set_attribute(flag=pygame.GL_CONTEXT_MINOR_VERSION, value=1) + pygame.display.gl_set_attribute(flag=pygame.GL_CONTEXT_FLAGS, value=0) + pygame.display.gl_set_attribute(flag=pygame.GL_CONTEXT_PROFILE_MASK, value=0) + pygame.display.gl_set_attribute( + flag=pygame.GL_SHARE_WITH_CURRENT_CONTEXT, value=0 + ) + pygame.display.gl_set_attribute( + flag=pygame.GL_FRAMEBUFFER_SRGB_CAPABLE, value=0 + ) + + # We create a list where we store the values that we set each flag to + set_values = [8, 24, 8, 16, 16, 16, 16, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0] + + # We create a list where we store the values after getting them + get_values = [] + + get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_ALPHA_SIZE)) + get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_DEPTH_SIZE)) + get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_STENCIL_SIZE)) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_RED_SIZE) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_GREEN_SIZE) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_BLUE_SIZE) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_ALPHA_SIZE) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_MULTISAMPLEBUFFERS) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_MULTISAMPLESAMPLES) + ) + get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_STEREO)) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCELERATED_VISUAL) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_MAJOR_VERSION) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_MINOR_VERSION) + ) + get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_FLAGS)) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_PROFILE_MASK) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_SHARE_WITH_CURRENT_CONTEXT) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_FRAMEBUFFER_SRGB_CAPABLE) + ) + + # We check to see if the values that we get correspond to the values that we set + # them to or to the original values. + for i in range(len(original_values)): + self.assertTrue( + (get_values[i] == original_values[i]) + or (get_values[i] == set_values[i]) + ) + + # test using non-flag argument + with self.assertRaises(TypeError): + pygame.display.gl_get_attribute("DUMMY") + + @unittest.skipIf( + ( + "skipping for all because some failures on rasppi and maybe other platforms" + or os.environ.get("SDL_VIDEODRIVER") == "dummy" + ), + 'OpenGL requires a non-"dummy" SDL_VIDEODRIVER', + ) + def test_gl_set_attribute(self): + # __doc__ (as of 2008-08-02) for pygame.display.gl_set_attribute: + + # pygame.display.gl_set_attribute(flag, value): return None + # request an opengl display attribute for the display mode + # + # When calling pygame.display.set_mode() with the pygame.OPENGL flag, + # Pygame automatically handles setting the OpenGL attributes like + # color and doublebuffering. OpenGL offers several other attributes + # you may want control over. Pass one of these attributes as the flag, + # and its appropriate value. This must be called before + # pygame.display.set_mode() + # + # The OPENGL flags are; + # GL_ALPHA_SIZE, GL_DEPTH_SIZE, GL_STENCIL_SIZE, GL_ACCUM_RED_SIZE, + # GL_ACCUM_GREEN_SIZE, GL_ACCUM_BLUE_SIZE, GL_ACCUM_ALPHA_SIZE, + # GL_MULTISAMPLEBUFFERS, GL_MULTISAMPLESAMPLES, GL_STEREO + + screen = display.set_mode((0, 0), pygame.OPENGL) + + # We create a list where we store the values that we set each flag to + set_values = [8, 24, 8, 16, 16, 16, 16, 1, 1, 0] + + # Setting the flags with values supposedly different from the original values + + # assign SDL1-supported values with gl_set_attribute + pygame.display.gl_set_attribute(pygame.GL_ALPHA_SIZE, set_values[0]) + pygame.display.gl_set_attribute(pygame.GL_DEPTH_SIZE, set_values[1]) + pygame.display.gl_set_attribute(pygame.GL_STENCIL_SIZE, set_values[2]) + pygame.display.gl_set_attribute(pygame.GL_ACCUM_RED_SIZE, set_values[3]) + pygame.display.gl_set_attribute(pygame.GL_ACCUM_GREEN_SIZE, set_values[4]) + pygame.display.gl_set_attribute(pygame.GL_ACCUM_BLUE_SIZE, set_values[5]) + pygame.display.gl_set_attribute(pygame.GL_ACCUM_ALPHA_SIZE, set_values[6]) + pygame.display.gl_set_attribute(pygame.GL_MULTISAMPLEBUFFERS, set_values[7]) + pygame.display.gl_set_attribute(pygame.GL_MULTISAMPLESAMPLES, set_values[8]) + pygame.display.gl_set_attribute(pygame.GL_STEREO, set_values[9]) + + # We create a list where we store the values after getting them + get_values = [] + + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ALPHA_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_DEPTH_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_STENCIL_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_RED_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_GREEN_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_BLUE_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_ALPHA_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_MULTISAMPLEBUFFERS)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_MULTISAMPLESAMPLES)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_STEREO)) + + # We check to see if the values that we get correspond to the values that we set + # them to or to the original values. + for i in range(len(set_values)): + self.assertTrue(get_values[i] == set_values[i]) + + # test using non-flag argument + with self.assertRaises(TypeError): + pygame.display.gl_get_attribute("DUMMY") + + @unittest.skipIf( + ( + "skipping for all because some failures on rasppi and maybe other platforms" + or os.environ.get("SDL_VIDEODRIVER") == "dummy" + ), + 'OpenGL requires a non-"dummy" SDL_VIDEODRIVER', + ) + def test_gl_set_attribute_kwargs(self): + # __doc__ (as of 2008-08-02) for pygame.display.gl_set_attribute: + + # pygame.display.gl_set_attribute(flag, value): return None + # request an opengl display attribute for the display mode + # + # When calling pygame.display.set_mode() with the pygame.OPENGL flag, + # Pygame automatically handles setting the OpenGL attributes like + # color and doublebuffering. OpenGL offers several other attributes + # you may want control over. Pass one of these attributes as the flag, + # and its appropriate value. This must be called before + # pygame.display.set_mode() + # + # The OPENGL flags are; + # GL_ALPHA_SIZE, GL_DEPTH_SIZE, GL_STENCIL_SIZE, GL_ACCUM_RED_SIZE, + # GL_ACCUM_GREEN_SIZE, GL_ACCUM_BLUE_SIZE, GL_ACCUM_ALPHA_SIZE, + # GL_MULTISAMPLEBUFFERS, GL_MULTISAMPLESAMPLES, GL_STEREO + + screen = display.set_mode((0, 0), pygame.OPENGL) + + # We create a list where we store the values that we set each flag to + set_values = [8, 24, 8, 16, 16, 16, 16, 1, 1, 0] + + # Setting the flags with values supposedly different from the original values + + # assign SDL1-supported values with gl_set_attribute + pygame.display.gl_set_attribute(flag=pygame.GL_ALPHA_SIZE, value=set_values[0]) + pygame.display.gl_set_attribute(flag=pygame.GL_DEPTH_SIZE, value=set_values[1]) + pygame.display.gl_set_attribute( + flag=pygame.GL_STENCIL_SIZE, value=set_values[2] + ) + pygame.display.gl_set_attribute( + flag=pygame.GL_ACCUM_RED_SIZE, value=set_values[3] + ) + pygame.display.gl_set_attribute( + flag=pygame.GL_ACCUM_GREEN_SIZE, value=set_values[4] + ) + pygame.display.gl_set_attribute( + flag=pygame.GL_ACCUM_BLUE_SIZE, value=set_values[5] + ) + pygame.display.gl_set_attribute( + flag=pygame.GL_ACCUM_ALPHA_SIZE, value=set_values[6] + ) + pygame.display.gl_set_attribute( + flag=pygame.GL_MULTISAMPLEBUFFERS, value=set_values[7] + ) + pygame.display.gl_set_attribute( + flag=pygame.GL_MULTISAMPLESAMPLES, value=set_values[8] + ) + pygame.display.gl_set_attribute(flag=pygame.GL_STEREO, value=set_values[9]) + + # We create a list where we store the values after getting them + get_values = [] + + get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_ALPHA_SIZE)) + get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_DEPTH_SIZE)) + get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_STENCIL_SIZE)) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_RED_SIZE) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_GREEN_SIZE) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_BLUE_SIZE) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_ALPHA_SIZE) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_MULTISAMPLEBUFFERS) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_MULTISAMPLESAMPLES) + ) + get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_STEREO)) + + # We check to see if the values that we get correspond to the values that we set + # them to or to the original values. + for i in range(len(set_values)): + self.assertTrue(get_values[i] == set_values[i]) + + # test using non-flag argument + with self.assertRaises(TypeError): + pygame.display.gl_get_attribute("DUMMY") + + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") in ["dummy", "android"], + "iconify is only supported on some video drivers/platforms", + ) + def test_iconify(self): + pygame.display.set_mode((640, 480)) + + self.assertEqual(pygame.display.get_active(), True) + + success = pygame.display.iconify() + + if success: + active_event = window_minimized_event = False + # make sure we cycle the event loop enough to get the display + # hidden. Test that both ACTIVEEVENT and WINDOWMINIMISED event appears + for _ in range(50): + time.sleep(0.01) + for event in pygame.event.get(): + if event.type == pygame.ACTIVEEVENT: + if not event.gain and event.state == pygame.APPACTIVE: + active_event = True + if event.type == pygame.WINDOWMINIMIZED: + window_minimized_event = True + + self.assertTrue(window_minimized_event) + self.assertTrue(active_event) + self.assertFalse(pygame.display.get_active()) + + else: + self.fail("Iconify not supported on this platform, please skip") + + def test_init(self): + """Ensures the module is initialized after init called.""" + # display.init() already called in setUp(), so quit and re-init + display.quit() + display.init() + + self.assertTrue(display.get_init()) + + def test_init__multiple(self): + """Ensures the module is initialized after multiple init calls.""" + display.init() + display.init() + + self.assertTrue(display.get_init()) + + def test_list_modes(self): + modes = pygame.display.list_modes(depth=0, flags=pygame.FULLSCREEN, display=0) + # modes == -1 means any mode is supported. + if modes != -1: + self.assertEqual(len(modes[0]), 2) + self.assertEqual(type(modes[0][0]), int) + + modes = pygame.display.list_modes() + if modes != -1: + self.assertEqual(len(modes[0]), 2) + self.assertEqual(type(modes[0][0]), int) + self.assertEqual(len(modes), len(set(modes))) + + modes = pygame.display.list_modes(depth=0, flags=0, display=0) + if modes != -1: + self.assertEqual(len(modes[0]), 2) + self.assertEqual(type(modes[0][0]), int) + + def test_mode_ok(self): + pygame.display.mode_ok((128, 128)) + modes = pygame.display.list_modes() + if modes != -1: + size = modes[0] + self.assertNotEqual(pygame.display.mode_ok(size), 0) + + pygame.display.mode_ok((128, 128), 0, 32) + pygame.display.mode_ok((128, 128), flags=0, depth=32, display=0) + + def test_mode_ok_fullscreen(self): + modes = pygame.display.list_modes() + if modes != -1: + size = modes[0] + self.assertNotEqual( + pygame.display.mode_ok(size, flags=pygame.FULLSCREEN), 0 + ) + + def test_mode_ok_scaled(self): + modes = pygame.display.list_modes() + if modes != -1: + size = modes[0] + self.assertNotEqual(pygame.display.mode_ok(size, flags=pygame.SCALED), 0) + + def test_get_num_displays(self): + self.assertGreater(pygame.display.get_num_displays(), 0) + + def test_quit(self): + """Ensures the module is not initialized after quit called.""" + display.quit() + + self.assertFalse(display.get_init()) + + def test_quit__multiple(self): + """Ensures the module is not initialized after multiple quit calls.""" + display.quit() + display.quit() + + self.assertFalse(display.get_init()) + + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") == "dummy", "Needs a not dummy videodriver" + ) + def test_set_gamma(self): + pygame.display.set_mode((1, 1)) + + gammas = [0.25, 0.5, 0.88, 1.0] + for gamma in gammas: + with self.subTest(gamma=gamma): + with self.assertWarns(DeprecationWarning): + self.assertEqual(pygame.display.set_gamma(gamma), True) + self.assertEqual(pygame.display.set_gamma(gamma), True) + + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") == "dummy", "Needs a not dummy videodriver" + ) + def test_set_gamma__tuple(self): + pygame.display.set_mode((1, 1)) + + gammas = [(0.5, 0.5, 0.5), (1.0, 1.0, 1.0), (0.25, 0.33, 0.44)] + for r, g, b in gammas: + with self.subTest(r=r, g=g, b=b): + self.assertEqual(pygame.display.set_gamma(r, g, b), True) + + @unittest.skipIf( + not hasattr(pygame.display, "set_gamma_ramp"), + "Not all systems and hardware support gamma ramps", + ) + def test_set_gamma_ramp(self): + # __doc__ (as of 2008-08-02) for pygame.display.set_gamma_ramp: + + # change the hardware gamma ramps with a custom lookup + # pygame.display.set_gamma_ramp(red, green, blue): return bool + # set_gamma_ramp(red, green, blue): return bool + # + # Set the red, green, and blue gamma ramps with an explicit lookup + # table. Each argument should be sequence of 256 integers. The + # integers should range between 0 and 0xffff. Not all systems and + # hardware support gamma ramps, if the function succeeds it will + # return True. + # + pygame.display.set_mode((5, 5)) + r = list(range(256)) + g = [number + 256 for number in r] + b = [number + 256 for number in g] + with self.assertWarns(DeprecationWarning): + isSupported = pygame.display.set_gamma_ramp(r, g, b) + if isSupported: + self.assertTrue(pygame.display.set_gamma_ramp(r, g, b)) + else: + self.assertFalse(pygame.display.set_gamma_ramp(r, g, b)) + + def test_set_mode_kwargs(self): + pygame.display.set_mode(size=(1, 1), flags=0, depth=0, display=0) + + def test_set_mode_scaled(self): + surf = pygame.display.set_mode( + size=(1, 1), flags=pygame.SCALED, depth=0, display=0 + ) + winsize = pygame.display.get_window_size() + self.assertEqual( + winsize[0] % surf.get_size()[0], + 0, + "window width should be a multiple of the surface width", + ) + self.assertEqual( + winsize[1] % surf.get_size()[1], + 0, + "window height should be a multiple of the surface height", + ) + self.assertEqual( + winsize[0] / surf.get_size()[0], winsize[1] / surf.get_size()[1] + ) + + def test_set_mode_vector2(self): + pygame.display.set_mode(pygame.Vector2(1, 1)) + + def test_set_mode_unscaled(self): + """Ensures a window created with SCALED can become smaller.""" + # see https://github.com/pygame/pygame/issues/2327 + + screen = pygame.display.set_mode((300, 300), pygame.SCALED) + self.assertEqual(screen.get_size(), (300, 300)) + + screen = pygame.display.set_mode((200, 200)) + self.assertEqual(screen.get_size(), (200, 200)) + + def test_screensaver_support(self): + pygame.display.set_allow_screensaver(True) + self.assertTrue(pygame.display.get_allow_screensaver()) + pygame.display.set_allow_screensaver(False) + self.assertFalse(pygame.display.get_allow_screensaver()) + pygame.display.set_allow_screensaver() + self.assertTrue(pygame.display.get_allow_screensaver()) + + # the following test fails always with SDL2 + @unittest.skipIf(True, "set_palette() not supported in SDL2") + def test_set_palette(self): + with self.assertRaises(pygame.error): + palette = [1, 2, 3] + pygame.display.set_palette(palette) + pygame.display.set_mode((1024, 768), 0, 8) + palette = [] + self.assertIsNone(pygame.display.set_palette(palette)) + + with self.assertRaises(ValueError): + palette = 12 + pygame.display.set_palette(palette) + with self.assertRaises(TypeError): + palette = [[1, 2], [1, 2]] + pygame.display.set_palette(palette) + with self.assertRaises(TypeError): + palette = [[0, 0, 0, 0, 0]] + [[x, x, x, x, x] for x in range(1, 255)] + pygame.display.set_palette(palette) + with self.assertRaises(TypeError): + palette = "qwerty" + pygame.display.set_palette(palette) + with self.assertRaises(TypeError): + palette = [[123, 123, 123] * 10000] + pygame.display.set_palette(palette) + with self.assertRaises(TypeError): + palette = [1, 2, 3] + pygame.display.set_palette(palette) + + skip_list = ["dummy", "android"] + + @unittest.skipIf(True, "set_palette() not supported in SDL2") + def test_set_palette_kwargs(self): + with self.assertRaises(pygame.error): + palette = [1, 2, 3] + pygame.display.set_palette(palette=palette) + pygame.display.set_mode((1024, 768), 0, 8) + palette = [] + self.assertIsNone(pygame.display.set_palette(palette=palette)) + + with self.assertRaises(ValueError): + palette = 12 + pygame.display.set_palette(palette=palette) + with self.assertRaises(TypeError): + palette = [[1, 2], [1, 2]] + pygame.display.set_palette(palette=palette) + with self.assertRaises(TypeError): + palette = [[0, 0, 0, 0, 0]] + [[x, x, x, x, x] for x in range(1, 255)] + pygame.display.set_palette(palette=palette) + with self.assertRaises(TypeError): + palette = "qwerty" + pygame.display.set_palette(palette=palette) + with self.assertRaises(TypeError): + palette = [[123, 123, 123] * 10000] + pygame.display.set_palette(palette=palette) + with self.assertRaises(TypeError): + palette = [1, 2, 3] + pygame.display.set_palette(palette=palette) + + skip_list = ["dummy", "android"] + + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") in skip_list, + "requires the SDL_VIDEODRIVER to be non dummy", + ) + def test_toggle_fullscreen(self): + """Test for toggle fullscreen""" + + # try to toggle fullscreen with no active display + # this should result in an error + pygame.display.quit() + with self.assertRaises(pygame.error): + pygame.display.toggle_fullscreen() + + pygame.display.init() + width_height = (640, 480) + test_surf = pygame.display.set_mode(width_height) + + # try to toggle fullscreen + try: + pygame.display.toggle_fullscreen() + + except pygame.error: + self.fail() + + else: + # if toggle success, the width/height should be a + # value found in list_modes + if pygame.display.toggle_fullscreen() == 1: + boolean = ( + test_surf.get_width(), + test_surf.get_height(), + ) in pygame.display.list_modes( + depth=0, flags=pygame.FULLSCREEN, display=0 + ) + + self.assertEqual(boolean, True) + + # if not original width/height should be preserved + else: + self.assertEqual( + (test_surf.get_width(), test_surf.get_height()), width_height + ) + + +class DisplayUpdateTest(unittest.TestCase): + def question(self, qstr): + """this is used in the interactive subclass.""" + + def setUp(self): + display.init() + self.screen = pygame.display.set_mode((500, 500)) + self.screen.fill("black") + pygame.display.flip() + pygame.event.pump() # so mac updates + + def tearDown(self): + display.quit() + + def test_update_negative(self): + """takes rects with negative values.""" + self.screen.fill("green") + + r1 = pygame.Rect(0, 0, 100, 100) + pygame.display.update(r1) + + r2 = pygame.Rect(-10, 0, 100, 100) + pygame.display.update(r2) + + r3 = pygame.Rect(-10, 0, -100, -100) + pygame.display.update(r3) + + self.question("Is the screen green in (0, 0, 100, 100)?") + + def test_update_sequence(self): + """only updates the part of the display given by the rects.""" + self.screen.fill("green") + rects = [ + pygame.Rect(0, 0, 100, 100), + pygame.Rect(100, 0, 100, 100), + pygame.Rect(200, 0, 100, 100), + pygame.Rect(300, 300, 100, 100), + ] + pygame.display.update(rects) + pygame.event.pump() # so mac updates + + self.question(f"Is the screen green in {rects}?") + + def test_update_none_skipped(self): + """None is skipped inside sequences.""" + self.screen.fill("green") + rects = ( + None, + pygame.Rect(100, 0, 100, 100), + None, + pygame.Rect(200, 0, 100, 100), + pygame.Rect(300, 300, 100, 100), + ) + pygame.display.update(rects) + pygame.event.pump() # so mac updates + + self.question(f"Is the screen green in {rects}?") + + def test_update_none(self): + """does NOT update the display.""" + self.screen.fill("green") + pygame.display.update(None) + pygame.event.pump() # so mac updates + self.question(f"Is the screen black and NOT green?") + + def test_update_no_args(self): + """does NOT update the display.""" + self.screen.fill("green") + pygame.display.update() + pygame.event.pump() # so mac updates + self.question(f"Is the WHOLE screen green?") + + def test_update_args(self): + """updates the display using the args as a rect.""" + self.screen.fill("green") + pygame.display.update(100, 100, 100, 100) + pygame.event.pump() # so mac updates + self.question("Is the screen green in (100, 100, 100, 100)?") + + def test_update_incorrect_args(self): + """raises a ValueError when inputs are wrong.""" + + with self.assertRaises(ValueError): + pygame.display.update(100, "asdf", 100, 100) + + with self.assertRaises(ValueError): + pygame.display.update([100, "asdf", 100, 100]) + + def test_update_no_init(self): + """raises a pygame.error.""" + + pygame.display.quit() + with self.assertRaises(pygame.error): + pygame.display.update() + + +class DisplayUpdateInteractiveTest(DisplayUpdateTest): + """Because we want these tests to run as interactive and not interactive.""" + + __tags__ = ["interactive"] + + def question(self, qstr): + """since this is the interactive sublcass we ask a question.""" + question(qstr) + + +class DisplayInteractiveTest(unittest.TestCase): + __tags__ = ["interactive"] + + def test_set_icon_interactive(self): + os.environ["SDL_VIDEO_WINDOW_POS"] = "100,250" + pygame.display.quit() + pygame.display.init() + + test_icon = pygame.Surface((32, 32)) + test_icon.fill((255, 0, 0)) + + pygame.display.set_icon(test_icon) + screen = pygame.display.set_mode((400, 100)) + pygame.display.set_caption("Is the window icon a red square?") + + response = question("Is the display icon red square?") + + self.assertTrue(response) + pygame.display.quit() + + def test_set_gamma_ramp(self): + os.environ["SDL_VIDEO_WINDOW_POS"] = "100,250" + pygame.display.quit() + pygame.display.init() + + screen = pygame.display.set_mode((400, 100)) + screen.fill((100, 100, 100)) + + blue_ramp = [x * 256 for x in range(0, 256)] + blue_ramp[100] = 150 * 256 # Can't tint too far or gamma ramps fail + normal_ramp = [x * 256 for x in range(0, 256)] + # test to see if this platform supports gamma ramps + gamma_success = False + if pygame.display.set_gamma_ramp(normal_ramp, normal_ramp, blue_ramp): + pygame.display.update() + gamma_success = True + + if gamma_success: + response = question("Is the window background tinted blue?") + self.assertTrue(response) + # restore normal ramp + pygame.display.set_gamma_ramp(normal_ramp, normal_ramp, normal_ramp) + + pygame.display.quit() + + +class FullscreenToggleTests(unittest.TestCase): + __tags__ = ["interactive"] + + screen = None + font = None + isfullscreen = False + + WIDTH = 800 + HEIGHT = 600 + + def setUp(self): + pygame.init() + if sys.platform == "win32": + # known issue with windows, must have mode from pygame.display.list_modes() + # or window created with flag pygame.SCALED + self.screen = pygame.display.set_mode( + (self.WIDTH, self.HEIGHT), flags=pygame.SCALED + ) + else: + self.screen = pygame.display.set_mode((self.WIDTH, self.HEIGHT)) + pygame.display.set_caption("Fullscreen Tests") + self.screen.fill((255, 255, 255)) + pygame.display.flip() + self.font = pygame.font.Font(None, 32) + + def tearDown(self): + if self.isfullscreen: + pygame.display.toggle_fullscreen() + pygame.quit() + + def visual_test(self, fullscreen=False): + text = "" + if fullscreen: + if not self.isfullscreen: + pygame.display.toggle_fullscreen() + self.isfullscreen = True + text = "Is this in fullscreen? [y/n]" + else: + if self.isfullscreen: + pygame.display.toggle_fullscreen() + self.isfullscreen = False + text = "Is this not in fullscreen [y/n]" + s = self.font.render(text, False, (0, 0, 0)) + self.screen.blit(s, (self.WIDTH / 2 - self.font.size(text)[0] / 2, 100)) + pygame.display.flip() + + while True: + for event in pygame.event.get(): + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_ESCAPE: + return False + if event.key == pygame.K_y: + return True + if event.key == pygame.K_n: + return False + if event.type == pygame.QUIT: + return False + + def test_fullscreen_true(self): + self.assertTrue(self.visual_test(fullscreen=True)) + + def test_fullscreen_false(self): + self.assertTrue(self.visual_test(fullscreen=False)) + + +@unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") == "dummy", + 'OpenGL requires a non-"dummy" SDL_VIDEODRIVER', +) +class DisplayOpenGLTest(unittest.TestCase): + def test_screen_size_opengl(self): + """returns a surface with the same size requested. + |tags:display,slow,opengl| + """ + pygame.display.init() + screen = pygame.display.set_mode((640, 480), pygame.OPENGL) + self.assertEqual((640, 480), screen.get_size()) + + +class X11CrashTest(unittest.TestCase): + def test_x11_set_mode_crash_gh1654(self): + # Test for https://github.com/pygame/pygame/issues/1654 + # If unfixed, this will trip a segmentation fault + pygame.display.init() + pygame.display.quit() + screen = pygame.display.set_mode((640, 480), 0) + self.assertEqual((640, 480), screen.get_size()) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/docs_test.py b/.venv/Lib/site-packages/pygame/tests/docs_test.py new file mode 100644 index 00000000..de021a8e --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/docs_test.py @@ -0,0 +1,35 @@ +import os +import subprocess +import sys +import unittest + + +class DocsIncludedTest(unittest.TestCase): + def test_doc_import_works(self): + from pygame.docs.__main__ import has_local_docs, open_docs + + @unittest.skipIf("CI" not in os.environ, "Docs not required for local builds") + def test_docs_included(self): + from pygame.docs.__main__ import has_local_docs + + self.assertTrue(has_local_docs()) + + @unittest.skipIf("CI" not in os.environ, "Docs not required for local builds") + def test_docs_command(self): + try: + subprocess.run( + [sys.executable, "-m", "pygame.docs"], + timeout=5, + # check ensures an exception is raised when the process fails + check=True, + # pipe stdout/stderr so that they don't clutter main stdout + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + except subprocess.TimeoutExpired: + # timeout errors are not an issue + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/draw_test.py b/.venv/Lib/site-packages/pygame/tests/draw_test.py new file mode 100644 index 00000000..1ee59ddf --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/draw_test.py @@ -0,0 +1,6508 @@ +import math +import unittest +import sys +import warnings + +import pygame +from pygame import draw +from pygame import draw_py +from pygame.locals import SRCALPHA +from pygame.tests import test_utils +from pygame.math import Vector2 + + +RED = BG_RED = pygame.Color("red") +GREEN = FG_GREEN = pygame.Color("green") + +# Clockwise from the top left corner and ending with the center point. +RECT_POSITION_ATTRIBUTES = ( + "topleft", + "midtop", + "topright", + "midright", + "bottomright", + "midbottom", + "bottomleft", + "midleft", + "center", +) + + +def get_border_values(surface, width, height): + """Returns a list containing lists with the values of the surface's + borders. + """ + border_top = [surface.get_at((x, 0)) for x in range(width)] + border_left = [surface.get_at((0, y)) for y in range(height)] + border_right = [surface.get_at((width - 1, y)) for y in range(height)] + border_bottom = [surface.get_at((x, height - 1)) for x in range(width)] + + return [border_top, border_left, border_right, border_bottom] + + +def corners(surface): + """Returns a tuple with the corner positions of the given surface. + + Clockwise from the top left corner. + """ + width, height = surface.get_size() + return ((0, 0), (width - 1, 0), (width - 1, height - 1), (0, height - 1)) + + +def rect_corners_mids_and_center(rect): + """Returns a tuple with each corner, mid, and the center for a given rect. + + Clockwise from the top left corner and ending with the center point. + """ + return ( + rect.topleft, + rect.midtop, + rect.topright, + rect.midright, + rect.bottomright, + rect.midbottom, + rect.bottomleft, + rect.midleft, + rect.center, + ) + + +def border_pos_and_color(surface): + """Yields each border position and its color for a given surface. + + Clockwise from the top left corner. + """ + width, height = surface.get_size() + right, bottom = width - 1, height - 1 + + # Top edge. + for x in range(width): + pos = (x, 0) + yield pos, surface.get_at(pos) + + # Right edge. + # Top right done in top edge loop. + for y in range(1, height): + pos = (right, y) + yield pos, surface.get_at(pos) + + # Bottom edge. + # Bottom right done in right edge loop. + for x in range(right - 1, -1, -1): + pos = (x, bottom) + yield pos, surface.get_at(pos) + + # Left edge. + # Bottom left done in bottom edge loop. Top left done in top edge loop. + for y in range(bottom - 1, 0, -1): + pos = (0, y) + yield pos, surface.get_at(pos) + + +def get_color_points(surface, color, bounds_rect=None, match_color=True): + """Get all the points of a given color on the surface within the given + bounds. + + If bounds_rect is None the full surface is checked. + If match_color is True, all points matching the color are returned, + otherwise all points not matching the color are returned. + """ + get_at = surface.get_at # For possible speed up. + + if bounds_rect is None: + x_range = range(surface.get_width()) + y_range = range(surface.get_height()) + else: + x_range = range(bounds_rect.left, bounds_rect.right) + y_range = range(bounds_rect.top, bounds_rect.bottom) + + surface.lock() # For possible speed up. + + if match_color: + pts = [(x, y) for x in x_range for y in y_range if get_at((x, y)) == color] + else: + pts = [(x, y) for x in x_range for y in y_range if get_at((x, y)) != color] + + surface.unlock() + return pts + + +def create_bounding_rect(surface, surf_color, default_pos): + """Create a rect to bound all the pixels that don't match surf_color. + + The default_pos parameter is used to position the bounding rect for the + case where all pixels match the surf_color. + """ + width, height = surface.get_clip().size + xmin, ymin = width, height + xmax, ymax = -1, -1 + get_at = surface.get_at # For possible speed up. + + surface.lock() # For possible speed up. + + for y in range(height): + for x in range(width): + if get_at((x, y)) != surf_color: + xmin = min(x, xmin) + xmax = max(x, xmax) + ymin = min(y, ymin) + ymax = max(y, ymax) + + surface.unlock() + + if -1 == xmax: + # No points means a 0 sized rect positioned at default_pos. + return pygame.Rect(default_pos, (0, 0)) + return pygame.Rect((xmin, ymin), (xmax - xmin + 1, ymax - ymin + 1)) + + +class InvalidBool: + """To help test invalid bool values.""" + + __bool__ = None + + +class DrawTestCase(unittest.TestCase): + """Base class to test draw module functions.""" + + draw_rect = staticmethod(draw.rect) + draw_polygon = staticmethod(draw.polygon) + draw_circle = staticmethod(draw.circle) + draw_ellipse = staticmethod(draw.ellipse) + draw_arc = staticmethod(draw.arc) + draw_line = staticmethod(draw.line) + draw_lines = staticmethod(draw.lines) + draw_aaline = staticmethod(draw.aaline) + draw_aalines = staticmethod(draw.aalines) + + +class PythonDrawTestCase(unittest.TestCase): + """Base class to test draw_py module functions.""" + + # draw_py is currently missing some functions. + # draw_rect = staticmethod(draw_py.draw_rect) + draw_polygon = staticmethod(draw_py.draw_polygon) + # draw_circle = staticmethod(draw_py.draw_circle) + # draw_ellipse = staticmethod(draw_py.draw_ellipse) + # draw_arc = staticmethod(draw_py.draw_arc) + draw_line = staticmethod(draw_py.draw_line) + draw_lines = staticmethod(draw_py.draw_lines) + draw_aaline = staticmethod(draw_py.draw_aaline) + draw_aalines = staticmethod(draw_py.draw_aalines) + + +### Ellipse Testing ########################################################### + + +class DrawEllipseMixin: + """Mixin tests for drawing ellipses. + + This class contains all the general ellipse drawing tests. + """ + + def test_ellipse__args(self): + """Ensures draw ellipse accepts the correct args.""" + bounds_rect = self.draw_ellipse( + pygame.Surface((3, 3)), (0, 10, 0, 50), pygame.Rect((0, 0), (3, 2)), 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__args_without_width(self): + """Ensures draw ellipse accepts the args without a width.""" + bounds_rect = self.draw_ellipse( + pygame.Surface((2, 2)), (1, 1, 1, 99), pygame.Rect((1, 1), (1, 1)) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__args_with_negative_width(self): + """Ensures draw ellipse accepts the args with negative width.""" + bounds_rect = self.draw_ellipse( + pygame.Surface((3, 3)), (0, 10, 0, 50), pygame.Rect((2, 3), (3, 2)), -1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + self.assertEqual(bounds_rect, pygame.Rect(2, 3, 0, 0)) + + def test_ellipse__args_with_width_gt_radius(self): + """Ensures draw ellipse accepts the args with + width > rect.w // 2 and width > rect.h // 2. + """ + rect = pygame.Rect((0, 0), (4, 4)) + bounds_rect = self.draw_ellipse( + pygame.Surface((3, 3)), (0, 10, 0, 50), rect, rect.w // 2 + 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + bounds_rect = self.draw_ellipse( + pygame.Surface((3, 3)), (0, 10, 0, 50), rect, rect.h // 2 + 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__kwargs(self): + """Ensures draw ellipse accepts the correct kwargs + with and without a width arg. + """ + kwargs_list = [ + { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("yellow"), + "rect": pygame.Rect((0, 0), (3, 2)), + "width": 1, + }, + { + "surface": pygame.Surface((2, 1)), + "color": (0, 10, 20), + "rect": (0, 0, 1, 1), + }, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_ellipse(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__kwargs_order_independent(self): + """Ensures draw ellipse's kwargs are not order dependent.""" + bounds_rect = self.draw_ellipse( + color=(1, 2, 3), + surface=pygame.Surface((3, 2)), + width=0, + rect=pygame.Rect((1, 0), (1, 1)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__args_missing(self): + """Ensures draw ellipse detects any missing required args.""" + surface = pygame.Surface((1, 1)) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_ellipse(surface, pygame.Color("red")) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_ellipse(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_ellipse() + + def test_ellipse__kwargs_missing(self): + """Ensures draw ellipse detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((1, 2)), + "color": pygame.Color("red"), + "rect": pygame.Rect((1, 0), (2, 2)), + "width": 2, + } + + for name in ("rect", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_ellipse(**invalid_kwargs) + + def test_ellipse__arg_invalid_types(self): + """Ensures draw ellipse detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + rect = pygame.Rect((1, 1), (1, 1)) + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_ellipse(surface, color, rect, "1") + + with self.assertRaises(TypeError): + # Invalid rect. + bounds_rect = self.draw_ellipse(surface, color, (1, 2, 3, 4, 5), 1) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_ellipse(surface, 2.3, rect, 0) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_ellipse(rect, color, rect, 2) + + def test_ellipse__kwarg_invalid_types(self): + """Ensures draw ellipse detects invalid kwarg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("green") + rect = pygame.Rect((0, 1), (1, 1)) + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "rect": rect, + "width": 1, + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "rect": rect, + "width": 1, + }, + { + "surface": surface, + "color": color, + "rect": (0, 0, 0), # Invalid rect. + "width": 1, + }, + {"surface": surface, "color": color, "rect": rect, "width": 1.1}, + ] # Invalid width. + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_ellipse(**kwargs) + + def test_ellipse__kwarg_invalid_name(self): + """Ensures draw ellipse detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + rect = pygame.Rect((0, 1), (2, 2)) + kwargs_list = [ + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "invalid": 1, + }, + {"surface": surface, "color": color, "rect": rect, "invalid": 1}, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_ellipse(**kwargs) + + def test_ellipse__args_and_kwargs(self): + """Ensures draw ellipse accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 1)) + color = (255, 255, 0, 0) + rect = pygame.Rect((1, 0), (2, 1)) + width = 0 + kwargs = {"surface": surface, "color": color, "rect": rect, "width": width} + + for name in ("surface", "color", "rect", "width"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_ellipse(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_ellipse(surface, color, **kwargs) + elif "rect" == name: + bounds_rect = self.draw_ellipse(surface, color, rect, **kwargs) + else: + bounds_rect = self.draw_ellipse(surface, color, rect, width, **kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__valid_width_values(self): + """Ensures draw ellipse accepts different width values.""" + pos = (1, 1) + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + color = (10, 20, 30, 255) + kwargs = { + "surface": surface, + "color": color, + "rect": pygame.Rect(pos, (3, 2)), + "width": None, + } + + for width in (-1000, -10, -1, 0, 1, 10, 1000): + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = color if width >= 0 else surface_color + + bounds_rect = self.draw_ellipse(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__valid_rect_formats(self): + """Ensures draw ellipse accepts different rect formats.""" + pos = (1, 1) + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((4, 4)) + kwargs = {"surface": surface, "color": expected_color, "rect": None, "width": 0} + rects = (pygame.Rect(pos, (1, 3)), (pos, (2, 1)), (pos[0], pos[1], 1, 1)) + + for rect in rects: + surface.fill(surface_color) # Clear for each test. + kwargs["rect"] = rect + + bounds_rect = self.draw_ellipse(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__valid_color_formats(self): + """Ensures draw ellipse accepts different color formats.""" + pos = (1, 1) + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": None, + "rect": pygame.Rect(pos, (1, 2)), + "width": 0, + } + reds = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in reds: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_ellipse(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__invalid_color_formats(self): + """Ensures draw ellipse handles invalid color formats correctly.""" + pos = (1, 1) + surface = pygame.Surface((4, 3)) + kwargs = { + "surface": surface, + "color": None, + "rect": pygame.Rect(pos, (2, 2)), + "width": 1, + } + + for expected_color in (2.3, surface): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_ellipse(**kwargs) + + def test_ellipse(self): + """Tests ellipses of differing sizes on surfaces of differing sizes. + + Checks if the number of sides touching the border of the surface is + correct. + """ + left_top = [(0, 0), (1, 0), (0, 1), (1, 1)] + sizes = [(4, 4), (5, 4), (4, 5), (5, 5)] + color = (1, 13, 24, 255) + + def same_size(width, height, border_width): + """Test for ellipses with the same size as the surface.""" + surface = pygame.Surface((width, height)) + + self.draw_ellipse(surface, color, (0, 0, width, height), border_width) + + # For each of the four borders check if it contains the color + borders = get_border_values(surface, width, height) + for border in borders: + self.assertTrue(color in border) + + def not_same_size(width, height, border_width, left, top): + """Test for ellipses that aren't the same size as the surface.""" + surface = pygame.Surface((width, height)) + + self.draw_ellipse( + surface, color, (left, top, width - 1, height - 1), border_width + ) + + borders = get_border_values(surface, width, height) + + # Check if two sides of the ellipse are touching the border + sides_touching = [color in border for border in borders].count(True) + self.assertEqual(sides_touching, 2) + + for width, height in sizes: + for border_width in (0, 1): + same_size(width, height, border_width) + for left, top in left_top: + not_same_size(width, height, border_width, left, top) + + def test_ellipse__big_ellipse(self): + """Test for big ellipse that could overflow in algorithm""" + width = 1025 + height = 1025 + border = 1 + x_value_test = int(0.4 * height) + y_value_test = int(0.4 * height) + surface = pygame.Surface((width, height)) + + self.draw_ellipse(surface, (255, 0, 0), (0, 0, width, height), border) + colored_pixels = 0 + for y in range(height): + if surface.get_at((x_value_test, y)) == (255, 0, 0): + colored_pixels += 1 + for x in range(width): + if surface.get_at((x, y_value_test)) == (255, 0, 0): + colored_pixels += 1 + self.assertEqual(colored_pixels, border * 4) + + def test_ellipse__thick_line(self): + """Ensures a thick lined ellipse is drawn correctly.""" + ellipse_color = pygame.Color("yellow") + surface_color = pygame.Color("black") + surface = pygame.Surface((40, 40)) + rect = pygame.Rect((0, 0), (31, 23)) + rect.center = surface.get_rect().center + + # As the lines get thicker the internals of the ellipse are not + # cleanly defined. So only test up to a few thicknesses before the + # maximum thickness. + for thickness in range(1, min(*rect.size) // 2 - 2): + surface.fill(surface_color) # Clear for each test. + + self.draw_ellipse(surface, ellipse_color, rect, thickness) + + surface.lock() # For possible speed up. + + # Check vertical thickness on the ellipse's top. + x = rect.centerx + y_start = rect.top + y_end = rect.top + thickness - 1 + + for y in range(y_start, y_end + 1): + self.assertEqual(surface.get_at((x, y)), ellipse_color, thickness) + + # Check pixels above and below this line. + self.assertEqual(surface.get_at((x, y_start - 1)), surface_color, thickness) + self.assertEqual(surface.get_at((x, y_end + 1)), surface_color, thickness) + + # Check vertical thickness on the ellipse's bottom. + x = rect.centerx + y_start = rect.bottom - thickness + y_end = rect.bottom - 1 + + for y in range(y_start, y_end + 1): + self.assertEqual(surface.get_at((x, y)), ellipse_color, thickness) + + # Check pixels above and below this line. + self.assertEqual(surface.get_at((x, y_start - 1)), surface_color, thickness) + self.assertEqual(surface.get_at((x, y_end + 1)), surface_color, thickness) + + # Check horizontal thickness on the ellipse's left. + x_start = rect.left + x_end = rect.left + thickness - 1 + y = rect.centery + + for x in range(x_start, x_end + 1): + self.assertEqual(surface.get_at((x, y)), ellipse_color, thickness) + + # Check pixels to the left and right of this line. + self.assertEqual(surface.get_at((x_start - 1, y)), surface_color, thickness) + self.assertEqual(surface.get_at((x_end + 1, y)), surface_color, thickness) + + # Check horizontal thickness on the ellipse's right. + x_start = rect.right - thickness + x_end = rect.right - 1 + y = rect.centery + + for x in range(x_start, x_end + 1): + self.assertEqual(surface.get_at((x, y)), ellipse_color, thickness) + + # Check pixels to the left and right of this line. + self.assertEqual(surface.get_at((x_start - 1, y)), surface_color, thickness) + self.assertEqual(surface.get_at((x_end + 1, y)), surface_color, thickness) + + surface.unlock() + + def test_ellipse__no_holes(self): + width = 80 + height = 70 + surface = pygame.Surface((width + 1, height)) + rect = pygame.Rect(0, 0, width, height) + for thickness in range(1, 37, 5): + surface.fill("BLACK") + self.draw_ellipse(surface, "RED", rect, thickness) + for y in range(height): + number_of_changes = 0 + drawn_pixel = False + for x in range(width + 1): + if ( + not drawn_pixel + and surface.get_at((x, y)) == pygame.Color("RED") + or drawn_pixel + and surface.get_at((x, y)) == pygame.Color("BLACK") + ): + drawn_pixel = not drawn_pixel + number_of_changes += 1 + if y < thickness or y > height - thickness - 1: + self.assertEqual(number_of_changes, 2) + else: + self.assertEqual(number_of_changes, 4) + + def test_ellipse__max_width(self): + """Ensures an ellipse with max width (and greater) is drawn correctly.""" + ellipse_color = pygame.Color("yellow") + surface_color = pygame.Color("black") + surface = pygame.Surface((40, 40)) + rect = pygame.Rect((0, 0), (31, 21)) + rect.center = surface.get_rect().center + max_thickness = (min(*rect.size) + 1) // 2 + + for thickness in range(max_thickness, max_thickness + 3): + surface.fill(surface_color) # Clear for each test. + + self.draw_ellipse(surface, ellipse_color, rect, thickness) + + surface.lock() # For possible speed up. + + # Check vertical thickness. + for y in range(rect.top, rect.bottom): + self.assertEqual(surface.get_at((rect.centerx, y)), ellipse_color) + + # Check horizontal thickness. + for x in range(rect.left, rect.right): + self.assertEqual(surface.get_at((x, rect.centery)), ellipse_color) + + # Check pixels above and below ellipse. + self.assertEqual( + surface.get_at((rect.centerx, rect.top - 1)), surface_color + ) + self.assertEqual( + surface.get_at((rect.centerx, rect.bottom + 1)), surface_color + ) + + # Check pixels to the left and right of the ellipse. + self.assertEqual( + surface.get_at((rect.left - 1, rect.centery)), surface_color + ) + self.assertEqual( + surface.get_at((rect.right + 1, rect.centery)), surface_color + ) + + surface.unlock() + + def _check_1_pixel_sized_ellipse( + self, surface, collide_rect, surface_color, ellipse_color + ): + # Helper method to check the surface for 1 pixel wide and/or high + # ellipses. + surf_w, surf_h = surface.get_size() + + surface.lock() # For possible speed up. + + for pos in ((x, y) for y in range(surf_h) for x in range(surf_w)): + # Since the ellipse is just a line we can use a rect to help find + # where it is expected to be drawn. + if collide_rect.collidepoint(pos): + expected_color = ellipse_color + else: + expected_color = surface_color + + self.assertEqual( + surface.get_at(pos), + expected_color, + f"collide_rect={collide_rect}, pos={pos}", + ) + + surface.unlock() + + def test_ellipse__1_pixel_width(self): + """Ensures an ellipse with a width of 1 is drawn correctly. + + An ellipse with a width of 1 pixel is a vertical line. + """ + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("black") + surf_w, surf_h = 10, 20 + + surface = pygame.Surface((surf_w, surf_h)) + rect = pygame.Rect((0, 0), (1, 0)) + collide_rect = rect.copy() + + # Calculate some positions. + off_left = -1 + off_right = surf_w + off_bottom = surf_h + center_x = surf_w // 2 + center_y = surf_h // 2 + + # Test some even and odd heights. + for ellipse_h in range(6, 10): + collide_rect.h = ellipse_h + rect.h = ellipse_h + + # Calculate some variable positions. + off_top = -(ellipse_h + 1) + half_off_top = -(ellipse_h // 2) + half_off_bottom = surf_h - (ellipse_h // 2) + + # Draw the ellipse in different positions: fully on-surface, + # partially off-surface, and fully off-surface. + positions = ( + (off_left, off_top), + (off_left, half_off_top), + (off_left, center_y), + (off_left, half_off_bottom), + (off_left, off_bottom), + (center_x, off_top), + (center_x, half_off_top), + (center_x, center_y), + (center_x, half_off_bottom), + (center_x, off_bottom), + (off_right, off_top), + (off_right, half_off_top), + (off_right, center_y), + (off_right, half_off_bottom), + (off_right, off_bottom), + ) + + for rect_pos in positions: + surface.fill(surface_color) # Clear before each draw. + rect.topleft = rect_pos + collide_rect.topleft = rect_pos + + self.draw_ellipse(surface, ellipse_color, rect) + + self._check_1_pixel_sized_ellipse( + surface, collide_rect, surface_color, ellipse_color + ) + + def test_ellipse__1_pixel_width_spanning_surface(self): + """Ensures an ellipse with a width of 1 is drawn correctly + when spanning the height of the surface. + + An ellipse with a width of 1 pixel is a vertical line. + """ + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("black") + surf_w, surf_h = 10, 20 + + surface = pygame.Surface((surf_w, surf_h)) + rect = pygame.Rect((0, 0), (1, surf_h + 2)) # Longer than the surface. + + # Draw the ellipse in different positions: on-surface and off-surface. + positions = ( + (-1, -1), # (off_left, off_top) + (0, -1), # (left_edge, off_top) + (surf_w // 2, -1), # (center_x, off_top) + (surf_w - 1, -1), # (right_edge, off_top) + (surf_w, -1), + ) # (off_right, off_top) + + for rect_pos in positions: + surface.fill(surface_color) # Clear before each draw. + rect.topleft = rect_pos + + self.draw_ellipse(surface, ellipse_color, rect) + + self._check_1_pixel_sized_ellipse( + surface, rect, surface_color, ellipse_color + ) + + def test_ellipse__1_pixel_height(self): + """Ensures an ellipse with a height of 1 is drawn correctly. + + An ellipse with a height of 1 pixel is a horizontal line. + """ + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("black") + surf_w, surf_h = 20, 10 + + surface = pygame.Surface((surf_w, surf_h)) + rect = pygame.Rect((0, 0), (0, 1)) + collide_rect = rect.copy() + + # Calculate some positions. + off_right = surf_w + off_top = -1 + off_bottom = surf_h + center_x = surf_w // 2 + center_y = surf_h // 2 + + # Test some even and odd widths. + for ellipse_w in range(6, 10): + collide_rect.w = ellipse_w + rect.w = ellipse_w + + # Calculate some variable positions. + off_left = -(ellipse_w + 1) + half_off_left = -(ellipse_w // 2) + half_off_right = surf_w - (ellipse_w // 2) + + # Draw the ellipse in different positions: fully on-surface, + # partially off-surface, and fully off-surface. + positions = ( + (off_left, off_top), + (half_off_left, off_top), + (center_x, off_top), + (half_off_right, off_top), + (off_right, off_top), + (off_left, center_y), + (half_off_left, center_y), + (center_x, center_y), + (half_off_right, center_y), + (off_right, center_y), + (off_left, off_bottom), + (half_off_left, off_bottom), + (center_x, off_bottom), + (half_off_right, off_bottom), + (off_right, off_bottom), + ) + + for rect_pos in positions: + surface.fill(surface_color) # Clear before each draw. + rect.topleft = rect_pos + collide_rect.topleft = rect_pos + + self.draw_ellipse(surface, ellipse_color, rect) + + self._check_1_pixel_sized_ellipse( + surface, collide_rect, surface_color, ellipse_color + ) + + def test_ellipse__1_pixel_height_spanning_surface(self): + """Ensures an ellipse with a height of 1 is drawn correctly + when spanning the width of the surface. + + An ellipse with a height of 1 pixel is a horizontal line. + """ + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("black") + surf_w, surf_h = 20, 10 + + surface = pygame.Surface((surf_w, surf_h)) + rect = pygame.Rect((0, 0), (surf_w + 2, 1)) # Wider than the surface. + + # Draw the ellipse in different positions: on-surface and off-surface. + positions = ( + (-1, -1), # (off_left, off_top) + (-1, 0), # (off_left, top_edge) + (-1, surf_h // 2), # (off_left, center_y) + (-1, surf_h - 1), # (off_left, bottom_edge) + (-1, surf_h), + ) # (off_left, off_bottom) + + for rect_pos in positions: + surface.fill(surface_color) # Clear before each draw. + rect.topleft = rect_pos + + self.draw_ellipse(surface, ellipse_color, rect) + + self._check_1_pixel_sized_ellipse( + surface, rect, surface_color, ellipse_color + ) + + def test_ellipse__1_pixel_width_and_height(self): + """Ensures an ellipse with a width and height of 1 is drawn correctly. + + An ellipse with a width and height of 1 pixel is a single pixel. + """ + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("black") + surf_w, surf_h = 10, 10 + + surface = pygame.Surface((surf_w, surf_h)) + rect = pygame.Rect((0, 0), (1, 1)) + + # Calculate some positions. + off_left = -1 + off_right = surf_w + off_top = -1 + off_bottom = surf_h + left_edge = 0 + right_edge = surf_w - 1 + top_edge = 0 + bottom_edge = surf_h - 1 + center_x = surf_w // 2 + center_y = surf_h // 2 + + # Draw the ellipse in different positions: center surface, + # top/bottom/left/right edges, and off-surface. + positions = ( + (off_left, off_top), + (off_left, top_edge), + (off_left, center_y), + (off_left, bottom_edge), + (off_left, off_bottom), + (left_edge, off_top), + (left_edge, top_edge), + (left_edge, center_y), + (left_edge, bottom_edge), + (left_edge, off_bottom), + (center_x, off_top), + (center_x, top_edge), + (center_x, center_y), + (center_x, bottom_edge), + (center_x, off_bottom), + (right_edge, off_top), + (right_edge, top_edge), + (right_edge, center_y), + (right_edge, bottom_edge), + (right_edge, off_bottom), + (off_right, off_top), + (off_right, top_edge), + (off_right, center_y), + (off_right, bottom_edge), + (off_right, off_bottom), + ) + + for rect_pos in positions: + surface.fill(surface_color) # Clear before each draw. + rect.topleft = rect_pos + + self.draw_ellipse(surface, ellipse_color, rect) + + self._check_1_pixel_sized_ellipse( + surface, rect, surface_color, ellipse_color + ) + + def test_ellipse__bounding_rect(self): + """Ensures draw ellipse returns the correct bounding rect. + + Tests ellipses on and off the surface and a range of width/thickness + values. + """ + ellipse_color = pygame.Color("red") + surf_color = pygame.Color("black") + min_width = min_height = 5 + max_width = max_height = 7 + sizes = ((min_width, min_height), (max_width, max_height)) + surface = pygame.Surface((20, 20), 0, 32) + surf_rect = surface.get_rect() + # Make a rect that is bigger than the surface to help test drawing + # ellipses off and partially off the surface. + big_rect = surf_rect.inflate(min_width * 2 + 1, min_height * 2 + 1) + + for pos in rect_corners_mids_and_center( + surf_rect + ) + rect_corners_mids_and_center(big_rect): + # Each of the ellipse's rect position attributes will be set to + # the pos value. + for attr in RECT_POSITION_ATTRIBUTES: + # Test using different rect sizes and thickness values. + for width, height in sizes: + ellipse_rect = pygame.Rect((0, 0), (width, height)) + setattr(ellipse_rect, attr, pos) + + for thickness in (0, 1, 2, 3, min(width, height)): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_ellipse( + surface, ellipse_color, ellipse_rect, thickness + ) + + # Calculating the expected_rect after the ellipse + # is drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect( + surface, surf_color, ellipse_rect.topleft + ) + + self.assertEqual(bounding_rect, expected_rect) + + def test_ellipse__surface_clip(self): + """Ensures draw ellipse respects a surface's clip area. + + Tests drawing the ellipse filled and unfilled. + """ + surfw = surfh = 30 + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (11, 11)) + clip_rect.center = surface.get_rect().center + pos_rect = clip_rect.copy() # Manages the ellipse's pos. + + for width in (0, 1): # Filled and unfilled. + # Test centering the ellipse along the clip rect's edge. + for center in rect_corners_mids_and_center(clip_rect): + # Get the expected points by drawing the ellipse without the + # clip area set. + pos_rect.center = center + surface.set_clip(None) + surface.fill(surface_color) + self.draw_ellipse(surface, ellipse_color, pos_rect, width) + expected_pts = get_color_points(surface, ellipse_color, clip_rect) + + # Clear the surface and set the clip area. Redraw the ellipse + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_ellipse(surface, ellipse_color, pos_rect, width) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure only the expected_pts + # are the ellipse_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + expected_color = ellipse_color + else: + expected_color = surface_color + + self.assertEqual(surface.get_at(pt), expected_color, pt) + + surface.unlock() + + +class DrawEllipseTest(DrawEllipseMixin, DrawTestCase): + """Test draw module function ellipse. + + This class inherits the general tests from DrawEllipseMixin. It is also + the class to add any draw.ellipse specific tests to. + """ + + +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever properly supports drawing ellipses. +# @unittest.skip('draw_py.draw_ellipse not supported yet') +# class PythonDrawEllipseTest(DrawEllipseMixin, PythonDrawTestCase): +# """Test draw_py module function draw_ellipse. +# +# This class inherits the general tests from DrawEllipseMixin. It is also +# the class to add any draw_py.draw_ellipse specific tests to. +# """ + + +### Line/Lines/AALine/AALines Testing ######################################### + + +class BaseLineMixin: + """Mixin base for drawing various lines. + + This class contains general helper methods and setup for testing the + different types of lines. + """ + + COLORS = ( + (0, 0, 0), + (255, 0, 0), + (0, 255, 0), + (0, 0, 255), + (255, 255, 0), + (255, 0, 255), + (0, 255, 255), + (255, 255, 255), + ) + + @staticmethod + def _create_surfaces(): + # Create some surfaces with different sizes, depths, and flags. + surfaces = [] + for size in ((49, 49), (50, 50)): + for depth in (8, 16, 24, 32): + for flags in (0, SRCALPHA): + surface = pygame.display.set_mode(size, flags, depth) + surfaces.append(surface) + surfaces.append(surface.convert_alpha()) + return surfaces + + @staticmethod + def _rect_lines(rect): + # Yields pairs of end points and their reverse (to test symmetry). + # Uses a rect with the points radiating from its midleft. + for pt in rect_corners_mids_and_center(rect): + if pt in [rect.midleft, rect.center]: + # Don't bother with these points. + continue + yield (rect.midleft, pt) + yield (pt, rect.midleft) + + +### Line Testing ############################################################## + + +class LineMixin(BaseLineMixin): + """Mixin test for drawing a single line. + + This class contains all the general single line drawing tests. + """ + + def test_line__args(self): + """Ensures draw line accepts the correct args.""" + bounds_rect = self.draw_line( + pygame.Surface((3, 3)), (0, 10, 0, 50), (0, 0), (1, 1), 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__args_without_width(self): + """Ensures draw line accepts the args without a width.""" + bounds_rect = self.draw_line( + pygame.Surface((2, 2)), (0, 0, 0, 50), (0, 0), (2, 2) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__kwargs(self): + """Ensures draw line accepts the correct kwargs + with and without a width arg. + """ + surface = pygame.Surface((4, 4)) + color = pygame.Color("yellow") + start_pos = (1, 1) + end_pos = (2, 2) + kwargs_list = [ + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "width": 1, + }, + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + }, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_line(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__kwargs_order_independent(self): + """Ensures draw line's kwargs are not order dependent.""" + bounds_rect = self.draw_line( + start_pos=(1, 2), + end_pos=(2, 1), + width=2, + color=(10, 20, 30), + surface=pygame.Surface((3, 2)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__args_missing(self): + """Ensures draw line detects any missing required args.""" + surface = pygame.Surface((1, 1)) + color = pygame.Color("blue") + + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(surface, color, (0, 0)) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(surface, color) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_line() + + def test_line__kwargs_missing(self): + """Ensures draw line detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((3, 2)), + "color": pygame.Color("red"), + "start_pos": (2, 1), + "end_pos": (2, 2), + "width": 1, + } + + for name in ("end_pos", "start_pos", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(**invalid_kwargs) + + def test_line__arg_invalid_types(self): + """Ensures draw line detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + start_pos = (0, 1) + end_pos = (1, 2) + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_line(surface, color, start_pos, end_pos, "1") + + with self.assertRaises(TypeError): + # Invalid end_pos. + bounds_rect = self.draw_line(surface, color, start_pos, (1, 2, 3)) + + with self.assertRaises(TypeError): + # Invalid start_pos. + bounds_rect = self.draw_line(surface, color, (1,), end_pos) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_line(surface, 2.3, start_pos, end_pos) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_line((1, 2, 3, 4), color, start_pos, end_pos) + + def test_line__kwarg_invalid_types(self): + """Ensures draw line detects invalid kwarg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("green") + start_pos = (1, 0) + end_pos = (2, 0) + width = 1 + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "width": width, + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "start_pos": start_pos, + "end_pos": end_pos, + "width": width, + }, + { + "surface": surface, + "color": color, + "start_pos": (0, 0, 0), # Invalid start_pos. + "end_pos": end_pos, + "width": width, + }, + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": (0,), # Invalid end_pos. + "width": width, + }, + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "width": 1.2, + }, + ] # Invalid width. + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(**kwargs) + + def test_line__kwarg_invalid_name(self): + """Ensures draw line detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + start_pos = (1, 1) + end_pos = (2, 0) + kwargs_list = [ + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "width": 1, + "invalid": 1, + }, + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "invalid": 1, + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(**kwargs) + + def test_line__args_and_kwargs(self): + """Ensures draw line accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 2)) + color = (255, 255, 0, 0) + start_pos = (0, 1) + end_pos = (1, 2) + width = 0 + kwargs = { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "width": width, + } + + for name in ("surface", "color", "start_pos", "end_pos", "width"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_line(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_line(surface, color, **kwargs) + elif "start_pos" == name: + bounds_rect = self.draw_line(surface, color, start_pos, **kwargs) + elif "end_pos" == name: + bounds_rect = self.draw_line( + surface, color, start_pos, end_pos, **kwargs + ) + else: + bounds_rect = self.draw_line( + surface, color, start_pos, end_pos, width, **kwargs + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__valid_width_values(self): + """Ensures draw line accepts different width values.""" + line_color = pygame.Color("yellow") + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + pos = (2, 1) + kwargs = { + "surface": surface, + "color": line_color, + "start_pos": pos, + "end_pos": (2, 2), + "width": None, + } + + for width in (-100, -10, -1, 0, 1, 10, 100): + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = line_color if width > 0 else surface_color + + bounds_rect = self.draw_line(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__valid_start_pos_formats(self): + """Ensures draw line accepts different start_pos formats.""" + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((4, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "start_pos": None, + "end_pos": (2, 2), + "width": 2, + } + x, y = 2, 1 # start position + + # The point values can be ints or floats. + for start_pos in ((x, y), (x + 0.1, y), (x, y + 0.1), (x + 0.1, y + 0.1)): + # The point type can be a tuple/list/Vector2. + for seq_type in (tuple, list, Vector2): + surface.fill(surface_color) # Clear for each test. + kwargs["start_pos"] = seq_type(start_pos) + + bounds_rect = self.draw_line(**kwargs) + + self.assertEqual(surface.get_at((x, y)), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__valid_end_pos_formats(self): + """Ensures draw line accepts different end_pos formats.""" + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((4, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "start_pos": (2, 1), + "end_pos": None, + "width": 2, + } + x, y = 2, 2 # end position + + # The point values can be ints or floats. + for end_pos in ((x, y), (x + 0.2, y), (x, y + 0.2), (x + 0.2, y + 0.2)): + # The point type can be a tuple/list/Vector2. + for seq_type in (tuple, list, Vector2): + surface.fill(surface_color) # Clear for each test. + kwargs["end_pos"] = seq_type(end_pos) + + bounds_rect = self.draw_line(**kwargs) + + self.assertEqual(surface.get_at((x, y)), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__invalid_start_pos_formats(self): + """Ensures draw line handles invalid start_pos formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "start_pos": None, + "end_pos": (2, 2), + "width": 1, + } + + start_pos_fmts = ( + (2,), # Too few coords. + (2, 1, 0), # Too many coords. + (2, "1"), # Wrong type. + {2, 1}, # Wrong type. + dict(((2, 1),)), + ) # Wrong type. + + for start_pos in start_pos_fmts: + kwargs["start_pos"] = start_pos + + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(**kwargs) + + def test_line__invalid_end_pos_formats(self): + """Ensures draw line handles invalid end_pos formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "start_pos": (2, 2), + "end_pos": None, + "width": 1, + } + + end_pos_fmts = ( + (2,), # Too few coords. + (2, 1, 0), # Too many coords. + (2, "1"), # Wrong type. + {2, 1}, # Wrong type. + dict(((2, 1),)), + ) # Wrong type. + + for end_pos in end_pos_fmts: + kwargs["end_pos"] = end_pos + + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(**kwargs) + + def test_line__valid_color_formats(self): + """Ensures draw line accepts different color formats.""" + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + pos = (1, 1) + kwargs = { + "surface": surface, + "color": None, + "start_pos": pos, + "end_pos": (2, 1), + "width": 3, + } + greens = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in greens: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_line(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__invalid_color_formats(self): + """Ensures draw line handles invalid color formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 3)), + "color": None, + "start_pos": (1, 1), + "end_pos": (2, 1), + "width": 1, + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(**kwargs) + + def test_line__color(self): + """Tests if the line drawn is the correct color.""" + pos = (0, 0) + for surface in self._create_surfaces(): + for expected_color in self.COLORS: + self.draw_line(surface, expected_color, pos, (1, 0)) + + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + + def test_line__color_with_thickness(self): + """Ensures a thick line is drawn using the correct color.""" + from_x = 5 + to_x = 10 + y = 5 + for surface in self._create_surfaces(): + for expected_color in self.COLORS: + self.draw_line(surface, expected_color, (from_x, y), (to_x, y), 5) + for pos in ((x, y + i) for i in (-2, 0, 2) for x in (from_x, to_x)): + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + + def test_line__gaps(self): + """Tests if the line drawn contains any gaps.""" + expected_color = (255, 255, 255) + for surface in self._create_surfaces(): + width = surface.get_width() + self.draw_line(surface, expected_color, (0, 0), (width - 1, 0)) + + for x in range(width): + pos = (x, 0) + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + + def test_line__gaps_with_thickness(self): + """Ensures a thick line is drawn without any gaps.""" + expected_color = (255, 255, 255) + thickness = 5 + for surface in self._create_surfaces(): + width = surface.get_width() - 1 + h = width // 5 + w = h * 5 + self.draw_line(surface, expected_color, (0, 5), (w, 5 + h), thickness) + + for x in range(w + 1): + for y in range(3, 8): + pos = (x, y + ((x + 2) // 5)) + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + + def test_line__bounding_rect(self): + """Ensures draw line returns the correct bounding rect. + + Tests lines with endpoints on and off the surface and a range of + width/thickness values. + """ + if isinstance(self, PythonDrawTestCase): + self.skipTest("bounding rects not supported in draw_py.draw_line") + + line_color = pygame.Color("red") + surf_color = pygame.Color("black") + width = height = 30 + # Using a rect to help manage where the lines are drawn. + helper_rect = pygame.Rect((0, 0), (width, height)) + + # Testing surfaces of different sizes. One larger than the helper_rect + # and one smaller (to test lines that span the surface). + for size in ((width + 5, height + 5), (width - 5, height - 5)): + surface = pygame.Surface(size, 0, 32) + surf_rect = surface.get_rect() + + # Move the helper rect to different positions to test line + # endpoints on and off the surface. + for pos in rect_corners_mids_and_center(surf_rect): + helper_rect.center = pos + + # Draw using different thicknesses. + for thickness in range(-1, 5): + for start, end in self._rect_lines(helper_rect): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_line( + surface, line_color, start, end, thickness + ) + + if 0 < thickness: + # Calculating the expected_rect after the line is + # drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect( + surface, surf_color, start + ) + else: + # Nothing drawn. + expected_rect = pygame.Rect(start, (0, 0)) + + self.assertEqual( + bounding_rect, + expected_rect, + "start={}, end={}, size={}, thickness={}".format( + start, end, size, thickness + ), + ) + + def test_line__surface_clip(self): + """Ensures draw line respects a surface's clip area.""" + surfw = surfh = 30 + line_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (11, 11)) + clip_rect.center = surface.get_rect().center + pos_rect = clip_rect.copy() # Manages the line's pos. + + for thickness in (1, 3): # Test different line widths. + # Test centering the line along the clip rect's edge. + for center in rect_corners_mids_and_center(clip_rect): + # Get the expected points by drawing the line without the + # clip area set. + pos_rect.center = center + surface.set_clip(None) + surface.fill(surface_color) + self.draw_line( + surface, line_color, pos_rect.midtop, pos_rect.midbottom, thickness + ) + expected_pts = get_color_points(surface, line_color, clip_rect) + + # Clear the surface and set the clip area. Redraw the line + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_line( + surface, line_color, pos_rect.midtop, pos_rect.midbottom, thickness + ) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure only the expected_pts + # are the line_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + expected_color = line_color + else: + expected_color = surface_color + + self.assertEqual(surface.get_at(pt), expected_color, pt) + + surface.unlock() + + +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever fully supports drawing single lines. +# @unittest.skip('draw_py.draw_line not fully supported yet') +# class PythonDrawLineTest(LineMixin, PythonDrawTestCase): +# """Test draw_py module function line. +# +# This class inherits the general tests from LineMixin. It is also the class +# to add any draw_py.draw_line specific tests to. +# """ + + +class DrawLineTest(LineMixin, DrawTestCase): + """Test draw module function line. + + This class inherits the general tests from LineMixin. It is also the class + to add any draw.line specific tests to. + """ + + def test_line_endianness(self): + """test color component order""" + for depth in (24, 32): + surface = pygame.Surface((5, 3), 0, depth) + surface.fill(pygame.Color(0, 0, 0)) + self.draw_line(surface, pygame.Color(255, 0, 0), (0, 1), (2, 1), 1) + + self.assertGreater(surface.get_at((1, 1)).r, 0, "there should be red here") + + surface.fill(pygame.Color(0, 0, 0)) + self.draw_line(surface, pygame.Color(0, 0, 255), (0, 1), (2, 1), 1) + + self.assertGreater(surface.get_at((1, 1)).b, 0, "there should be blue here") + + def test_line(self): + # (l, t), (l, t) + self.surf_size = (320, 200) + self.surf = pygame.Surface(self.surf_size, pygame.SRCALPHA) + self.color = (1, 13, 24, 205) + + drawn = draw.line(self.surf, self.color, (1, 0), (200, 0)) + self.assertEqual( + drawn.right, 201, "end point arg should be (or at least was) inclusive" + ) + + # Should be colored where it's supposed to be + for pt in test_utils.rect_area_pts(drawn): + self.assertEqual(self.surf.get_at(pt), self.color) + + # And not where it shouldn't + for pt in test_utils.rect_outer_bounds(drawn): + self.assertNotEqual(self.surf.get_at(pt), self.color) + + # Line width greater that 1 + line_width = 2 + offset = 5 + a = (offset, offset) + b = (self.surf_size[0] - offset, a[1]) + c = (a[0], self.surf_size[1] - offset) + d = (b[0], c[1]) + e = (a[0] + offset, c[1]) + f = (b[0], c[0] + 5) + lines = [ + (a, d), + (b, c), + (c, b), + (d, a), + (a, b), + (b, a), + (a, c), + (c, a), + (a, e), + (e, a), + (a, f), + (f, a), + (a, a), + ] + + for p1, p2 in lines: + msg = f"{p1} - {p2}" + if p1[0] <= p2[0]: + plow = p1 + phigh = p2 + else: + plow = p2 + phigh = p1 + + self.surf.fill((0, 0, 0)) + rec = draw.line(self.surf, (255, 255, 255), p1, p2, line_width) + xinc = yinc = 0 + + if abs(p1[0] - p2[0]) > abs(p1[1] - p2[1]): + yinc = 1 + else: + xinc = 1 + + for i in range(line_width): + p = (p1[0] + xinc * i, p1[1] + yinc * i) + self.assertEqual(self.surf.get_at(p), (255, 255, 255), msg) + + p = (p2[0] + xinc * i, p2[1] + yinc * i) + self.assertEqual(self.surf.get_at(p), (255, 255, 255), msg) + + p = (plow[0] - 1, plow[1]) + self.assertEqual(self.surf.get_at(p), (0, 0, 0), msg) + + p = (plow[0] + xinc * line_width, plow[1] + yinc * line_width) + self.assertEqual(self.surf.get_at(p), (0, 0, 0), msg) + + p = (phigh[0] + xinc * line_width, phigh[1] + yinc * line_width) + self.assertEqual(self.surf.get_at(p), (0, 0, 0), msg) + + if p1[0] < p2[0]: + rx = p1[0] + else: + rx = p2[0] + + if p1[1] < p2[1]: + ry = p1[1] + else: + ry = p2[1] + + w = abs(p2[0] - p1[0]) + 1 + xinc * (line_width - 1) + h = abs(p2[1] - p1[1]) + 1 + yinc * (line_width - 1) + msg += f", {rec}" + + self.assertEqual(rec, (rx, ry, w, h), msg) + + def test_line_for_gaps(self): + # This checks bug Thick Line Bug #448 + + width = 200 + height = 200 + surf = pygame.Surface((width, height), pygame.SRCALPHA) + + def white_surrounded_pixels(x, y): + offsets = [(1, 0), (0, 1), (-1, 0), (0, -1)] + WHITE = (255, 255, 255, 255) + return len( + [1 for dx, dy in offsets if surf.get_at((x + dx, y + dy)) == WHITE] + ) + + def check_white_line(start, end): + surf.fill((0, 0, 0)) + pygame.draw.line(surf, (255, 255, 255), start, end, 30) + + BLACK = (0, 0, 0, 255) + for x in range(1, width - 1): + for y in range(1, height - 1): + if surf.get_at((x, y)) == BLACK: + self.assertTrue(white_surrounded_pixels(x, y) < 3) + + check_white_line((50, 50), (140, 0)) + check_white_line((50, 50), (0, 120)) + check_white_line((50, 50), (199, 198)) + + +### Lines Testing ############################################################# + + +class LinesMixin(BaseLineMixin): + """Mixin test for drawing lines. + + This class contains all the general lines drawing tests. + """ + + def test_lines__args(self): + """Ensures draw lines accepts the correct args.""" + bounds_rect = self.draw_lines( + pygame.Surface((3, 3)), (0, 10, 0, 50), False, ((0, 0), (1, 1)), 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__args_without_width(self): + """Ensures draw lines accepts the args without a width.""" + bounds_rect = self.draw_lines( + pygame.Surface((2, 2)), (0, 0, 0, 50), False, ((0, 0), (1, 1)) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__kwargs(self): + """Ensures draw lines accepts the correct kwargs + with and without a width arg. + """ + surface = pygame.Surface((4, 4)) + color = pygame.Color("yellow") + points = ((0, 0), (1, 1), (2, 2)) + kwargs_list = [ + { + "surface": surface, + "color": color, + "closed": False, + "points": points, + "width": 1, + }, + {"surface": surface, "color": color, "closed": False, "points": points}, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_lines(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__kwargs_order_independent(self): + """Ensures draw lines's kwargs are not order dependent.""" + bounds_rect = self.draw_lines( + closed=1, + points=((0, 0), (1, 1), (2, 2)), + width=2, + color=(10, 20, 30), + surface=pygame.Surface((3, 2)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__args_missing(self): + """Ensures draw lines detects any missing required args.""" + surface = pygame.Surface((1, 1)) + color = pygame.Color("blue") + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(surface, color, 0) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(surface, color) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines() + + def test_lines__kwargs_missing(self): + """Ensures draw lines detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((3, 2)), + "color": pygame.Color("red"), + "closed": 1, + "points": ((2, 2), (1, 1)), + "width": 1, + } + + for name in ("points", "closed", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(**invalid_kwargs) + + def test_lines__arg_invalid_types(self): + """Ensures draw lines detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + closed = 0 + points = ((1, 2), (2, 1)) + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_lines(surface, color, closed, points, "1") + + with self.assertRaises(TypeError): + # Invalid points. + bounds_rect = self.draw_lines(surface, color, closed, (1, 2, 3)) + + with self.assertRaises(TypeError): + # Invalid closed. + bounds_rect = self.draw_lines(surface, color, InvalidBool(), points) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_lines(surface, 2.3, closed, points) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_lines((1, 2, 3, 4), color, closed, points) + + def test_lines__kwarg_invalid_types(self): + """Ensures draw lines detects invalid kwarg types.""" + valid_kwargs = { + "surface": pygame.Surface((3, 3)), + "color": pygame.Color("green"), + "closed": False, + "points": ((1, 2), (2, 1)), + "width": 1, + } + + invalid_kwargs = { + "surface": pygame.Surface, + "color": 2.3, + "closed": InvalidBool(), + "points": (0, 0, 0), + "width": 1.2, + } + + for kwarg in ("surface", "color", "closed", "points", "width"): + kwargs = dict(valid_kwargs) + kwargs[kwarg] = invalid_kwargs[kwarg] + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(**kwargs) + + def test_lines__kwarg_invalid_name(self): + """Ensures draw lines detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + closed = 1 + points = ((1, 2), (2, 1)) + kwargs_list = [ + { + "surface": surface, + "color": color, + "closed": closed, + "points": points, + "width": 1, + "invalid": 1, + }, + { + "surface": surface, + "color": color, + "closed": closed, + "points": points, + "invalid": 1, + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(**kwargs) + + def test_lines__args_and_kwargs(self): + """Ensures draw lines accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 2)) + color = (255, 255, 0, 0) + closed = 0 + points = ((1, 2), (2, 1)) + width = 1 + kwargs = { + "surface": surface, + "color": color, + "closed": closed, + "points": points, + "width": width, + } + + for name in ("surface", "color", "closed", "points", "width"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_lines(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_lines(surface, color, **kwargs) + elif "closed" == name: + bounds_rect = self.draw_lines(surface, color, closed, **kwargs) + elif "points" == name: + bounds_rect = self.draw_lines(surface, color, closed, points, **kwargs) + else: + bounds_rect = self.draw_lines( + surface, color, closed, points, width, **kwargs + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__valid_width_values(self): + """Ensures draw lines accepts different width values.""" + line_color = pygame.Color("yellow") + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + pos = (1, 1) + kwargs = { + "surface": surface, + "color": line_color, + "closed": False, + "points": (pos, (2, 1)), + "width": None, + } + + for width in (-100, -10, -1, 0, 1, 10, 100): + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = line_color if width > 0 else surface_color + + bounds_rect = self.draw_lines(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__valid_points_format(self): + """Ensures draw lines accepts different points formats.""" + expected_color = (10, 20, 30, 255) + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "closed": False, + "points": None, + "width": 1, + } + + # The point type can be a tuple/list/Vector2. + point_types = ( + (tuple, tuple, tuple, tuple), # all tuples + (list, list, list, list), # all lists + (Vector2, Vector2, Vector2, Vector2), # all Vector2s + (list, Vector2, tuple, Vector2), + ) # mix + + # The point values can be ints or floats. + point_values = ( + ((1, 1), (2, 1), (2, 2), (1, 2)), + ((1, 1), (2.2, 1), (2.1, 2.2), (1, 2.1)), + ) + + # Each sequence of points can be a tuple or a list. + seq_types = (tuple, list) + + for point_type in point_types: + for values in point_values: + check_pos = values[0] + points = [point_type[i](pt) for i, pt in enumerate(values)] + + for seq_type in seq_types: + surface.fill(surface_color) # Clear for each test. + kwargs["points"] = seq_type(points) + + bounds_rect = self.draw_lines(**kwargs) + + self.assertEqual(surface.get_at(check_pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__invalid_points_formats(self): + """Ensures draw lines handles invalid points formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "closed": False, + "points": None, + "width": 1, + } + + points_fmts = ( + ((1, 1), (2,)), # Too few coords. + ((1, 1), (2, 2, 2)), # Too many coords. + ((1, 1), (2, "2")), # Wrong type. + ((1, 1), {2, 3}), # Wrong type. + ((1, 1), dict(((2, 2), (3, 3)))), # Wrong type. + {(1, 1), (1, 2)}, # Wrong type. + dict(((1, 1), (4, 4))), + ) # Wrong type. + + for points in points_fmts: + kwargs["points"] = points + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(**kwargs) + + def test_lines__invalid_points_values(self): + """Ensures draw lines handles invalid points values correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "closed": False, + "points": None, + "width": 1, + } + + for points in ([], ((1, 1),)): # Too few points. + for seq_type in (tuple, list): # Test as tuples and lists. + kwargs["points"] = seq_type(points) + + with self.assertRaises(ValueError): + bounds_rect = self.draw_lines(**kwargs) + + def test_lines__valid_closed_values(self): + """Ensures draw lines accepts different closed values.""" + line_color = pygame.Color("blue") + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + pos = (1, 2) + kwargs = { + "surface": surface, + "color": line_color, + "closed": None, + "points": ((1, 1), (3, 1), (3, 3), (1, 3)), + "width": 1, + } + + true_values = (-7, 1, 10, "2", 3.1, (4,), [5], True) + false_values = (None, "", 0, (), [], False) + + for closed in true_values + false_values: + surface.fill(surface_color) # Clear for each test. + kwargs["closed"] = closed + expected_color = line_color if closed else surface_color + + bounds_rect = self.draw_lines(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__valid_color_formats(self): + """Ensures draw lines accepts different color formats.""" + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + pos = (1, 1) + kwargs = { + "surface": surface, + "color": None, + "closed": False, + "points": (pos, (2, 1)), + "width": 3, + } + greens = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in greens: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_lines(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__invalid_color_formats(self): + """Ensures draw lines handles invalid color formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 3)), + "color": None, + "closed": False, + "points": ((1, 1), (1, 2)), + "width": 1, + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(**kwargs) + + def test_lines__color(self): + """Tests if the lines drawn are the correct color. + + Draws lines around the border of the given surface and checks if all + borders of the surface only contain the given color. + """ + for surface in self._create_surfaces(): + for expected_color in self.COLORS: + self.draw_lines(surface, expected_color, True, corners(surface)) + + for pos, color in border_pos_and_color(surface): + self.assertEqual(color, expected_color, f"pos={pos}") + + def test_lines__color_with_thickness(self): + """Ensures thick lines are drawn using the correct color.""" + x_left = y_top = 5 + for surface in self._create_surfaces(): + x_right = surface.get_width() - 5 + y_bottom = surface.get_height() - 5 + endpoints = ( + (x_left, y_top), + (x_right, y_top), + (x_right, y_bottom), + (x_left, y_bottom), + ) + for expected_color in self.COLORS: + self.draw_lines(surface, expected_color, True, endpoints, 3) + + for t in (-1, 0, 1): + for x in range(x_left, x_right + 1): + for y in (y_top, y_bottom): + pos = (x, y + t) + self.assertEqual( + surface.get_at(pos), + expected_color, + f"pos={pos}", + ) + for y in range(y_top, y_bottom + 1): + for x in (x_left, x_right): + pos = (x + t, y) + self.assertEqual( + surface.get_at(pos), + expected_color, + f"pos={pos}", + ) + + def test_lines__gaps(self): + """Tests if the lines drawn contain any gaps. + + Draws lines around the border of the given surface and checks if + all borders of the surface contain any gaps. + """ + expected_color = (255, 255, 255) + for surface in self._create_surfaces(): + self.draw_lines(surface, expected_color, True, corners(surface)) + + for pos, color in border_pos_and_color(surface): + self.assertEqual(color, expected_color, f"pos={pos}") + + def test_lines__gaps_with_thickness(self): + """Ensures thick lines are drawn without any gaps.""" + expected_color = (255, 255, 255) + x_left = y_top = 5 + for surface in self._create_surfaces(): + h = (surface.get_width() - 11) // 5 + w = h * 5 + x_right = x_left + w + y_bottom = y_top + h + endpoints = ((x_left, y_top), (x_right, y_top), (x_right, y_bottom)) + self.draw_lines(surface, expected_color, True, endpoints, 3) + + for x in range(x_left, x_right + 1): + for t in (-1, 0, 1): + pos = (x, y_top + t) + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + pos = (x, y_top + t + ((x - 3) // 5)) + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + for y in range(y_top, y_bottom + 1): + for t in (-1, 0, 1): + pos = (x_right + t, y) + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + + def test_lines__bounding_rect(self): + """Ensures draw lines returns the correct bounding rect. + + Tests lines with endpoints on and off the surface and a range of + width/thickness values. + """ + line_color = pygame.Color("red") + surf_color = pygame.Color("black") + width = height = 30 + # Using a rect to help manage where the lines are drawn. + pos_rect = pygame.Rect((0, 0), (width, height)) + + # Testing surfaces of different sizes. One larger than the pos_rect + # and one smaller (to test lines that span the surface). + for size in ((width + 5, height + 5), (width - 5, height - 5)): + surface = pygame.Surface(size, 0, 32) + surf_rect = surface.get_rect() + + # Move pos_rect to different positions to test line endpoints on + # and off the surface. + for pos in rect_corners_mids_and_center(surf_rect): + pos_rect.center = pos + # Shape: Triangle (if closed), ^ caret (if not closed). + pts = (pos_rect.midleft, pos_rect.midtop, pos_rect.midright) + pos = pts[0] # Rect position if nothing drawn. + + # Draw using different thickness and closed values. + for thickness in range(-1, 5): + for closed in (True, False): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_lines( + surface, line_color, closed, pts, thickness + ) + + if 0 < thickness: + # Calculating the expected_rect after the lines are + # drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect( + surface, surf_color, pos + ) + else: + # Nothing drawn. + expected_rect = pygame.Rect(pos, (0, 0)) + + self.assertEqual(bounding_rect, expected_rect) + + def test_lines__surface_clip(self): + """Ensures draw lines respects a surface's clip area.""" + surfw = surfh = 30 + line_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (11, 11)) + clip_rect.center = surface.get_rect().center + pos_rect = clip_rect.copy() # Manages the lines's pos. + + # Test centering the pos_rect along the clip rect's edge to allow for + # drawing the lines over the clip_rect's bounds. + for center in rect_corners_mids_and_center(clip_rect): + pos_rect.center = center + pts = (pos_rect.midtop, pos_rect.center, pos_rect.midbottom) + + for closed in (True, False): # Test closed and not closed. + for thickness in (1, 3): # Test different line widths. + # Get the expected points by drawing the lines without the + # clip area set. + surface.set_clip(None) + surface.fill(surface_color) + self.draw_lines(surface, line_color, closed, pts, thickness) + expected_pts = get_color_points(surface, line_color, clip_rect) + + # Clear the surface and set the clip area. Redraw the lines + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_lines(surface, line_color, closed, pts, thickness) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure only the + # expected_pts are the line_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + expected_color = line_color + else: + expected_color = surface_color + + self.assertEqual(surface.get_at(pt), expected_color, pt) + + surface.unlock() + + +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever fully supports drawing lines. +# class PythonDrawLinesTest(LinesMixin, PythonDrawTestCase): +# """Test draw_py module function lines. +# +# This class inherits the general tests from LinesMixin. It is also the +# class to add any draw_py.draw_lines specific tests to. +# """ + + +class DrawLinesTest(LinesMixin, DrawTestCase): + """Test draw module function lines. + + This class inherits the general tests from LinesMixin. It is also the class + to add any draw.lines specific tests to. + """ + + +### AALine Testing ############################################################ + + +class AALineMixin(BaseLineMixin): + """Mixin test for drawing a single aaline. + + This class contains all the general single aaline drawing tests. + """ + + def test_aaline__args(self): + """Ensures draw aaline accepts the correct args.""" + bounds_rect = self.draw_aaline( + pygame.Surface((3, 3)), (0, 10, 0, 50), (0, 0), (1, 1), 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaline__args_without_blend(self): + """Ensures draw aaline accepts the args without a blend.""" + bounds_rect = self.draw_aaline( + pygame.Surface((2, 2)), (0, 0, 0, 50), (0, 0), (2, 2) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaline__blend_warning(self): + """From pygame 2, blend=False should raise DeprecationWarning.""" + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + warnings.simplefilter("always") + # Trigger DeprecationWarning. + self.draw_aaline( + pygame.Surface((2, 2)), (0, 0, 0, 50), (0, 0), (2, 2), False + ) + # Check if there is only one warning and is a DeprecationWarning. + self.assertEqual(len(w), 1) + self.assertTrue(issubclass(w[-1].category, DeprecationWarning)) + + def test_aaline__kwargs(self): + """Ensures draw aaline accepts the correct kwargs""" + surface = pygame.Surface((4, 4)) + color = pygame.Color("yellow") + start_pos = (1, 1) + end_pos = (2, 2) + kwargs_list = [ + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + }, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_aaline(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaline__kwargs_order_independent(self): + """Ensures draw aaline's kwargs are not order dependent.""" + bounds_rect = self.draw_aaline( + start_pos=(1, 2), + end_pos=(2, 1), + color=(10, 20, 30), + surface=pygame.Surface((3, 2)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaline__args_missing(self): + """Ensures draw aaline detects any missing required args.""" + surface = pygame.Surface((1, 1)) + color = pygame.Color("blue") + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(surface, color, (0, 0)) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(surface, color) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline() + + def test_aaline__kwargs_missing(self): + """Ensures draw aaline detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((3, 2)), + "color": pygame.Color("red"), + "start_pos": (2, 1), + "end_pos": (2, 2), + } + + for name in ("end_pos", "start_pos", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(**invalid_kwargs) + + def test_aaline__arg_invalid_types(self): + """Ensures draw aaline detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + start_pos = (0, 1) + end_pos = (1, 2) + + with self.assertRaises(TypeError): + # Invalid end_pos. + bounds_rect = self.draw_aaline(surface, color, start_pos, (1, 2, 3)) + + with self.assertRaises(TypeError): + # Invalid start_pos. + bounds_rect = self.draw_aaline(surface, color, (1,), end_pos) + + with self.assertRaises(ValueError): + # Invalid color. + bounds_rect = self.draw_aaline(surface, "invalid-color", start_pos, end_pos) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_aaline((1, 2, 3, 4), color, start_pos, end_pos) + + def test_aaline__kwarg_invalid_types(self): + """Ensures draw aaline detects invalid kwarg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("green") + start_pos = (1, 0) + end_pos = (2, 0) + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "start_pos": start_pos, + "end_pos": end_pos, + }, + { + "surface": surface, + "color": color, + "start_pos": (0, 0, 0), # Invalid start_pos. + "end_pos": end_pos, + }, + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": (0,), # Invalid end_pos. + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(**kwargs) + + def test_aaline__kwarg_invalid_name(self): + """Ensures draw aaline detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + start_pos = (1, 1) + end_pos = (2, 0) + kwargs_list = [ + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "invalid": 1, + }, + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "invalid": 1, + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(**kwargs) + + def test_aaline__args_and_kwargs(self): + """Ensures draw aaline accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 2)) + color = (255, 255, 0, 0) + start_pos = (0, 1) + end_pos = (1, 2) + kwargs = { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + } + + for name in ("surface", "color", "start_pos", "end_pos"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_aaline(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_aaline(surface, color, **kwargs) + elif "start_pos" == name: + bounds_rect = self.draw_aaline(surface, color, start_pos, **kwargs) + elif "end_pos" == name: + bounds_rect = self.draw_aaline( + surface, color, start_pos, end_pos, **kwargs + ) + else: + bounds_rect = self.draw_aaline( + surface, color, start_pos, end_pos, **kwargs + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaline__valid_start_pos_formats(self): + """Ensures draw aaline accepts different start_pos formats.""" + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((4, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "start_pos": None, + "end_pos": (2, 2), + } + x, y = 2, 1 # start position + positions = ((x, y), (x + 0.01, y), (x, y + 0.01), (x + 0.01, y + 0.01)) + + for start_pos in positions: + for seq_type in (tuple, list, Vector2): + surface.fill(surface_color) # Clear for each test. + kwargs["start_pos"] = seq_type(start_pos) + + bounds_rect = self.draw_aaline(**kwargs) + + color = surface.get_at((x, y)) + for i, sub_color in enumerate(expected_color): + # The color could be slightly off the expected color due to + # any fractional position arguments. + self.assertGreaterEqual(color[i] + 6, sub_color, start_pos) + self.assertIsInstance(bounds_rect, pygame.Rect, start_pos) + + def test_aaline__valid_end_pos_formats(self): + """Ensures draw aaline accepts different end_pos formats.""" + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((4, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "start_pos": (2, 1), + "end_pos": None, + } + x, y = 2, 2 # end position + positions = ((x, y), (x + 0.02, y), (x, y + 0.02), (x + 0.02, y + 0.02)) + + for end_pos in positions: + for seq_type in (tuple, list, Vector2): + surface.fill(surface_color) # Clear for each test. + kwargs["end_pos"] = seq_type(end_pos) + + bounds_rect = self.draw_aaline(**kwargs) + + color = surface.get_at((x, y)) + for i, sub_color in enumerate(expected_color): + # The color could be slightly off the expected color due to + # any fractional position arguments. + self.assertGreaterEqual(color[i] + 15, sub_color, end_pos) + self.assertIsInstance(bounds_rect, pygame.Rect, end_pos) + + def test_aaline__invalid_start_pos_formats(self): + """Ensures draw aaline handles invalid start_pos formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "start_pos": None, + "end_pos": (2, 2), + } + + start_pos_fmts = ( + (2,), # Too few coords. + (2, 1, 0), # Too many coords. + (2, "1"), # Wrong type. + {2, 1}, # Wrong type. + dict(((2, 1),)), + ) # Wrong type. + + for start_pos in start_pos_fmts: + kwargs["start_pos"] = start_pos + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(**kwargs) + + def test_aaline__invalid_end_pos_formats(self): + """Ensures draw aaline handles invalid end_pos formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "start_pos": (2, 2), + "end_pos": None, + } + + end_pos_fmts = ( + (2,), # Too few coords. + (2, 1, 0), # Too many coords. + (2, "1"), # Wrong type. + {2, 1}, # Wrong type. + dict(((2, 1),)), + ) # Wrong type. + + for end_pos in end_pos_fmts: + kwargs["end_pos"] = end_pos + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(**kwargs) + + def test_aaline__valid_color_formats(self): + """Ensures draw aaline accepts different color formats.""" + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + pos = (1, 1) + kwargs = { + "surface": surface, + "color": None, + "start_pos": pos, + "end_pos": (2, 1), + } + greens = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in greens: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_aaline(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaline__invalid_color_formats(self): + """Ensures draw aaline handles invalid color formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 3)), + "color": None, + "start_pos": (1, 1), + "end_pos": (2, 1), + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(**kwargs) + + def test_aaline__color(self): + """Tests if the aaline drawn is the correct color.""" + pos = (0, 0) + for surface in self._create_surfaces(): + for expected_color in self.COLORS: + self.draw_aaline(surface, expected_color, pos, (1, 0)) + + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + + def test_aaline__gaps(self): + """Tests if the aaline drawn contains any gaps. + + See: #512 + """ + expected_color = (255, 255, 255) + for surface in self._create_surfaces(): + width = surface.get_width() + self.draw_aaline(surface, expected_color, (0, 0), (width - 1, 0)) + + for x in range(width): + pos = (x, 0) + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + + def test_aaline__bounding_rect(self): + """Ensures draw aaline returns the correct bounding rect. + + Tests lines with endpoints on and off the surface. + """ + line_color = pygame.Color("red") + surf_color = pygame.Color("blue") + width = height = 30 + # Using a rect to help manage where the lines are drawn. + helper_rect = pygame.Rect((0, 0), (width, height)) + + # Testing surfaces of different sizes. One larger than the helper_rect + # and one smaller (to test lines that span the surface). + for size in ((width + 5, height + 5), (width - 5, height - 5)): + surface = pygame.Surface(size, 0, 32) + surf_rect = surface.get_rect() + + # Move the helper rect to different positions to test line + # endpoints on and off the surface. + for pos in rect_corners_mids_and_center(surf_rect): + helper_rect.center = pos + + for start, end in self._rect_lines(helper_rect): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_aaline(surface, line_color, start, end) + + # Calculating the expected_rect after the line is + # drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect(surface, surf_color, start) + + self.assertEqual(bounding_rect, expected_rect) + + def test_aaline__surface_clip(self): + """Ensures draw aaline respects a surface's clip area.""" + surfw = surfh = 30 + aaline_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (11, 11)) + clip_rect.center = surface.get_rect().center + pos_rect = clip_rect.copy() # Manages the aaline's pos. + + # Test centering the pos_rect along the clip rect's edge to allow for + # drawing the aaline over the clip_rect's bounds. + for center in rect_corners_mids_and_center(clip_rect): + pos_rect.center = center + + # Get the expected points by drawing the aaline without the + # clip area set. + surface.set_clip(None) + surface.fill(surface_color) + self.draw_aaline(surface, aaline_color, pos_rect.midtop, pos_rect.midbottom) + + expected_pts = get_color_points(surface, surface_color, clip_rect, False) + + # Clear the surface and set the clip area. Redraw the aaline + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_aaline(surface, aaline_color, pos_rect.midtop, pos_rect.midbottom) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure the expected_pts + # are not surface_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + self.assertNotEqual(surface.get_at(pt), surface_color, pt) + else: + self.assertEqual(surface.get_at(pt), surface_color, pt) + + surface.unlock() + + +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever fully supports drawing single aalines. +# class PythonDrawAALineTest(AALineMixin, PythonDrawTestCase): +# """Test draw_py module function aaline. +# +# This class inherits the general tests from AALineMixin. It is also the +# class to add any draw_py.draw_aaline specific tests to. +# """ + + +class DrawAALineTest(AALineMixin, DrawTestCase): + """Test draw module function aaline. + + This class inherits the general tests from AALineMixin. It is also the + class to add any draw.aaline specific tests to. + """ + + def test_aaline_endianness(self): + """test color component order""" + for depth in (24, 32): + surface = pygame.Surface((5, 3), 0, depth) + surface.fill(pygame.Color(0, 0, 0)) + self.draw_aaline(surface, pygame.Color(255, 0, 0), (0, 1), (2, 1), 1) + + self.assertGreater(surface.get_at((1, 1)).r, 0, "there should be red here") + + surface.fill(pygame.Color(0, 0, 0)) + self.draw_aaline(surface, pygame.Color(0, 0, 255), (0, 1), (2, 1), 1) + + self.assertGreater(surface.get_at((1, 1)).b, 0, "there should be blue here") + + def _check_antialiasing( + self, from_point, to_point, should, check_points, set_endpoints=True + ): + """Draw a line between two points and check colors of check_points.""" + if set_endpoints: + should[from_point] = should[to_point] = FG_GREEN + + def check_one_direction(from_point, to_point, should): + self.draw_aaline(self.surface, FG_GREEN, from_point, to_point, True) + + for pt in check_points: + color = should.get(pt, BG_RED) + with self.subTest(from_pt=from_point, pt=pt, to=to_point): + self.assertEqual(self.surface.get_at(pt), color) + + # reset + draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0) + + # it is important to test also opposite direction, the algorithm + # is (#512) or was not symmetric + check_one_direction(from_point, to_point, should) + if from_point != to_point: + check_one_direction(to_point, from_point, should) + + def test_short_non_antialiased_lines(self): + """test very short not anti aliased lines in all directions.""" + + # Horizontal, vertical and diagonal lines should not be anti-aliased, + # even with draw.aaline ... + self.surface = pygame.Surface((10, 10)) + draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0) + + check_points = [(i, j) for i in range(3, 8) for j in range(3, 8)] + + def check_both_directions(from_pt, to_pt, other_points): + should = {pt: FG_GREEN for pt in other_points} + self._check_antialiasing(from_pt, to_pt, should, check_points) + + # 0. one point + check_both_directions((5, 5), (5, 5), []) + # 1. horizontal + check_both_directions((4, 7), (5, 7), []) + check_both_directions((5, 4), (7, 4), [(6, 4)]) + + # 2. vertical + check_both_directions((5, 5), (5, 6), []) + check_both_directions((6, 4), (6, 6), [(6, 5)]) + # 3. diagonals + check_both_directions((5, 5), (6, 6), []) + check_both_directions((5, 5), (7, 7), [(6, 6)]) + check_both_directions((5, 6), (6, 5), []) + check_both_directions((6, 4), (4, 6), [(5, 5)]) + + def test_short_line_anti_aliasing(self): + self.surface = pygame.Surface((10, 10)) + draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0) + + check_points = [(i, j) for i in range(3, 8) for j in range(3, 8)] + + def check_both_directions(from_pt, to_pt, should): + self._check_antialiasing(from_pt, to_pt, should, check_points) + + brown = (127, 127, 0) + reddish = (191, 63, 0) + greenish = (63, 191, 0) + + # lets say dx = abs(x0 - x1) ; dy = abs(y0 - y1) + + # dy / dx = 0.5 + check_both_directions((4, 4), (6, 5), {(5, 4): brown, (5, 5): brown}) + check_both_directions((4, 5), (6, 4), {(5, 4): brown, (5, 5): brown}) + + # dy / dx = 2 + check_both_directions((4, 4), (5, 6), {(4, 5): brown, (5, 5): brown}) + check_both_directions((5, 4), (4, 6), {(4, 5): brown, (5, 5): brown}) + + # some little longer lines; so we need to check more points: + check_points = [(i, j) for i in range(2, 9) for j in range(2, 9)] + # dy / dx = 0.25 + should = { + (4, 3): greenish, + (5, 3): brown, + (6, 3): reddish, + (4, 4): reddish, + (5, 4): brown, + (6, 4): greenish, + } + check_both_directions((3, 3), (7, 4), should) + + should = { + (4, 3): reddish, + (5, 3): brown, + (6, 3): greenish, + (4, 4): greenish, + (5, 4): brown, + (6, 4): reddish, + } + check_both_directions((3, 4), (7, 3), should) + + # dy / dx = 4 + should = { + (4, 4): greenish, + (4, 5): brown, + (4, 6): reddish, + (5, 4): reddish, + (5, 5): brown, + (5, 6): greenish, + } + check_both_directions((4, 3), (5, 7), should) + + should = { + (4, 4): reddish, + (4, 5): brown, + (4, 6): greenish, + (5, 4): greenish, + (5, 5): brown, + (5, 6): reddish, + } + check_both_directions((5, 3), (4, 7), should) + + def test_anti_aliasing_float_coordinates(self): + """Float coordinates should be blended smoothly.""" + + self.surface = pygame.Surface((10, 10)) + draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0) + + check_points = [(i, j) for i in range(5) for j in range(5)] + brown = (127, 127, 0) + reddish = (191, 63, 0) + greenish = (63, 191, 0) + + # 0. identical point : current implementation does no smoothing... + expected = {(2, 2): FG_GREEN} + self._check_antialiasing( + (1.5, 2), (1.5, 2), expected, check_points, set_endpoints=False + ) + expected = {(2, 3): FG_GREEN} + self._check_antialiasing( + (2.49, 2.7), (2.49, 2.7), expected, check_points, set_endpoints=False + ) + + # 1. horizontal lines + # a) blend endpoints + expected = {(1, 2): brown, (2, 2): FG_GREEN} + self._check_antialiasing( + (1.5, 2), (2, 2), expected, check_points, set_endpoints=False + ) + expected = {(1, 2): brown, (2, 2): FG_GREEN, (3, 2): brown} + self._check_antialiasing( + (1.5, 2), (2.5, 2), expected, check_points, set_endpoints=False + ) + expected = {(2, 2): brown, (1, 2): FG_GREEN} + self._check_antialiasing( + (1, 2), (1.5, 2), expected, check_points, set_endpoints=False + ) + expected = {(1, 2): brown, (2, 2): greenish} + self._check_antialiasing( + (1.5, 2), (1.75, 2), expected, check_points, set_endpoints=False + ) + + # b) blend y-coordinate + expected = {(x, y): brown for x in range(2, 5) for y in (1, 2)} + self._check_antialiasing( + (2, 1.5), (4, 1.5), expected, check_points, set_endpoints=False + ) + + # 2. vertical lines + # a) blend endpoints + expected = {(2, 1): brown, (2, 2): FG_GREEN, (2, 3): brown} + self._check_antialiasing( + (2, 1.5), (2, 2.5), expected, check_points, set_endpoints=False + ) + expected = {(2, 1): brown, (2, 2): greenish} + self._check_antialiasing( + (2, 1.5), (2, 1.75), expected, check_points, set_endpoints=False + ) + # b) blend x-coordinate + expected = {(x, y): brown for x in (1, 2) for y in range(2, 5)} + self._check_antialiasing( + (1.5, 2), (1.5, 4), expected, check_points, set_endpoints=False + ) + # 3. diagonal lines + # a) blend endpoints + expected = {(1, 1): brown, (2, 2): FG_GREEN, (3, 3): brown} + self._check_antialiasing( + (1.5, 1.5), (2.5, 2.5), expected, check_points, set_endpoints=False + ) + expected = {(3, 1): brown, (2, 2): FG_GREEN, (1, 3): brown} + self._check_antialiasing( + (2.5, 1.5), (1.5, 2.5), expected, check_points, set_endpoints=False + ) + # b) blend sidewards + expected = {(2, 1): brown, (2, 2): brown, (3, 2): brown, (3, 3): brown} + self._check_antialiasing( + (2, 1.5), (3, 2.5), expected, check_points, set_endpoints=False + ) + + expected = { + (2, 1): greenish, + (2, 2): reddish, + (3, 2): greenish, + (3, 3): reddish, + (4, 3): greenish, + (4, 4): reddish, + } + + self._check_antialiasing( + (2, 1.25), (4, 3.25), expected, check_points, set_endpoints=False + ) + + def test_anti_aliasing_at_and_outside_the_border(self): + """Ensures antialiasing works correct at a surface's borders.""" + + self.surface = pygame.Surface((10, 10)) + draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0) + + check_points = [(i, j) for i in range(10) for j in range(10)] + + reddish = (191, 63, 0) + brown = (127, 127, 0) + greenish = (63, 191, 0) + from_point, to_point = (3, 3), (7, 4) + should = { + (4, 3): greenish, + (5, 3): brown, + (6, 3): reddish, + (4, 4): reddish, + (5, 4): brown, + (6, 4): greenish, + } + + for dx, dy in ( + (-4, 0), + (4, 0), # moved to left and right borders + (0, -5), + (0, -4), + (0, -3), # upper border + (0, 5), + (0, 6), + (0, 7), # lower border + (-4, -4), + (-4, -3), + (-3, -4), + ): # upper left corner + first = from_point[0] + dx, from_point[1] + dy + second = to_point[0] + dx, to_point[1] + dy + expected = {(x + dx, y + dy): color for (x, y), color in should.items()} + + self._check_antialiasing(first, second, expected, check_points) + + +### AALines Testing ########################################################### + + +class AALinesMixin(BaseLineMixin): + """Mixin test for drawing aalines. + + This class contains all the general aalines drawing tests. + """ + + def test_aalines__args(self): + """Ensures draw aalines accepts the correct args.""" + bounds_rect = self.draw_aalines( + pygame.Surface((3, 3)), (0, 10, 0, 50), False, ((0, 0), (1, 1)), 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__args_without_blend(self): + """Ensures draw aalines accepts the args without a blend.""" + bounds_rect = self.draw_aalines( + pygame.Surface((2, 2)), (0, 0, 0, 50), False, ((0, 0), (1, 1)) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__blend_warning(self): + """From pygame 2, blend=False should raise DeprecationWarning.""" + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + warnings.simplefilter("always") + # Trigger DeprecationWarning. + self.draw_aalines( + pygame.Surface((2, 2)), (0, 0, 0, 50), False, ((0, 0), (1, 1)), False + ) + # Check if there is only one warning and is a DeprecationWarning. + self.assertEqual(len(w), 1) + self.assertTrue(issubclass(w[-1].category, DeprecationWarning)) + + def test_aalines__kwargs(self): + """Ensures draw aalines accepts the correct kwargs.""" + surface = pygame.Surface((4, 4)) + color = pygame.Color("yellow") + points = ((0, 0), (1, 1), (2, 2)) + kwargs_list = [ + {"surface": surface, "color": color, "closed": False, "points": points}, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_aalines(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__kwargs_order_independent(self): + """Ensures draw aalines's kwargs are not order dependent.""" + bounds_rect = self.draw_aalines( + closed=1, + points=((0, 0), (1, 1), (2, 2)), + color=(10, 20, 30), + surface=pygame.Surface((3, 2)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__args_missing(self): + """Ensures draw aalines detects any missing required args.""" + surface = pygame.Surface((1, 1)) + color = pygame.Color("blue") + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(surface, color, 0) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(surface, color) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines() + + def test_aalines__kwargs_missing(self): + """Ensures draw aalines detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((3, 2)), + "color": pygame.Color("red"), + "closed": 1, + "points": ((2, 2), (1, 1)), + } + + for name in ("points", "closed", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(**invalid_kwargs) + + def test_aalines__arg_invalid_types(self): + """Ensures draw aalines detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + closed = 0 + points = ((1, 2), (2, 1)) + + with self.assertRaises(TypeError): + # Invalid blend. + bounds_rect = self.draw_aalines(surface, color, closed, points, "1") + + with self.assertRaises(TypeError): + # Invalid points. + bounds_rect = self.draw_aalines(surface, color, closed, (1, 2, 3)) + + with self.assertRaises(TypeError): + # Invalid closed. + bounds_rect = self.draw_aalines(surface, color, InvalidBool(), points) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_aalines(surface, 2.3, closed, points) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_aalines((1, 2, 3, 4), color, closed, points) + + def test_aalines__kwarg_invalid_types(self): + """Ensures draw aalines detects invalid kwarg types.""" + valid_kwargs = { + "surface": pygame.Surface((3, 3)), + "color": pygame.Color("green"), + "closed": False, + "points": ((1, 2), (2, 1)), + } + + invalid_kwargs = { + "surface": pygame.Surface, + "color": 2.3, + "closed": InvalidBool(), + "points": (0, 0, 0), + } + + for kwarg in ("surface", "color", "closed", "points"): + kwargs = dict(valid_kwargs) + kwargs[kwarg] = invalid_kwargs[kwarg] + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(**kwargs) + + def test_aalines__kwarg_invalid_name(self): + """Ensures draw aalines detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + closed = 1 + points = ((1, 2), (2, 1)) + kwargs_list = [ + { + "surface": surface, + "color": color, + "closed": closed, + "points": points, + "invalid": 1, + }, + { + "surface": surface, + "color": color, + "closed": closed, + "points": points, + "invalid": 1, + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(**kwargs) + + def test_aalines__args_and_kwargs(self): + """Ensures draw aalines accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 2)) + color = (255, 255, 0, 0) + closed = 0 + points = ((1, 2), (2, 1)) + kwargs = { + "surface": surface, + "color": color, + "closed": closed, + "points": points, + } + + for name in ("surface", "color", "closed", "points"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_aalines(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_aalines(surface, color, **kwargs) + elif "closed" == name: + bounds_rect = self.draw_aalines(surface, color, closed, **kwargs) + elif "points" == name: + bounds_rect = self.draw_aalines( + surface, color, closed, points, **kwargs + ) + else: + bounds_rect = self.draw_aalines( + surface, color, closed, points, **kwargs + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__valid_points_format(self): + """Ensures draw aalines accepts different points formats.""" + expected_color = (10, 20, 30, 255) + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "closed": False, + "points": None, + } + + # The point type can be a tuple/list/Vector2. + point_types = ( + (tuple, tuple, tuple, tuple), # all tuples + (list, list, list, list), # all lists + (Vector2, Vector2, Vector2, Vector2), # all Vector2s + (list, Vector2, tuple, Vector2), + ) # mix + + # The point values can be ints or floats. + point_values = ( + ((1, 1), (2, 1), (2, 2), (1, 2)), + ((1, 1), (2.2, 1), (2.1, 2.2), (1, 2.1)), + ) + + # Each sequence of points can be a tuple or a list. + seq_types = (tuple, list) + + for point_type in point_types: + for values in point_values: + check_pos = values[0] + points = [point_type[i](pt) for i, pt in enumerate(values)] + + for seq_type in seq_types: + surface.fill(surface_color) # Clear for each test. + kwargs["points"] = seq_type(points) + + bounds_rect = self.draw_aalines(**kwargs) + + self.assertEqual(surface.get_at(check_pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__invalid_points_formats(self): + """Ensures draw aalines handles invalid points formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "closed": False, + "points": None, + } + + points_fmts = ( + ((1, 1), (2,)), # Too few coords. + ((1, 1), (2, 2, 2)), # Too many coords. + ((1, 1), (2, "2")), # Wrong type. + ((1, 1), {2, 3}), # Wrong type. + ((1, 1), dict(((2, 2), (3, 3)))), # Wrong type. + {(1, 1), (1, 2)}, # Wrong type. + dict(((1, 1), (4, 4))), + ) # Wrong type. + + for points in points_fmts: + kwargs["points"] = points + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(**kwargs) + + def test_aalines__invalid_points_values(self): + """Ensures draw aalines handles invalid points values correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "closed": False, + "points": None, + } + + for points in ([], ((1, 1),)): # Too few points. + for seq_type in (tuple, list): # Test as tuples and lists. + kwargs["points"] = seq_type(points) + + with self.assertRaises(ValueError): + bounds_rect = self.draw_aalines(**kwargs) + + def test_aalines__valid_closed_values(self): + """Ensures draw aalines accepts different closed values.""" + line_color = pygame.Color("blue") + surface_color = pygame.Color("white") + surface = pygame.Surface((5, 5)) + pos = (1, 3) + kwargs = { + "surface": surface, + "color": line_color, + "closed": None, + "points": ((1, 1), (4, 1), (4, 4), (1, 4)), + } + + true_values = (-7, 1, 10, "2", 3.1, (4,), [5], True) + false_values = (None, "", 0, (), [], False) + + for closed in true_values + false_values: + surface.fill(surface_color) # Clear for each test. + kwargs["closed"] = closed + expected_color = line_color if closed else surface_color + + bounds_rect = self.draw_aalines(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__valid_color_formats(self): + """Ensures draw aalines accepts different color formats.""" + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + pos = (1, 1) + kwargs = { + "surface": surface, + "color": None, + "closed": False, + "points": (pos, (2, 1)), + } + greens = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in greens: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_aalines(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__invalid_color_formats(self): + """Ensures draw aalines handles invalid color formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 3)), + "color": None, + "closed": False, + "points": ((1, 1), (1, 2)), + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(**kwargs) + + def test_aalines__color(self): + """Tests if the aalines drawn are the correct color. + + Draws aalines around the border of the given surface and checks if all + borders of the surface only contain the given color. + """ + for surface in self._create_surfaces(): + for expected_color in self.COLORS: + self.draw_aalines(surface, expected_color, True, corners(surface)) + + for pos, color in border_pos_and_color(surface): + self.assertEqual(color, expected_color, f"pos={pos}") + + def test_aalines__gaps(self): + """Tests if the aalines drawn contain any gaps. + + Draws aalines around the border of the given surface and checks if + all borders of the surface contain any gaps. + + See: #512 + """ + expected_color = (255, 255, 255) + for surface in self._create_surfaces(): + self.draw_aalines(surface, expected_color, True, corners(surface)) + + for pos, color in border_pos_and_color(surface): + self.assertEqual(color, expected_color, f"pos={pos}") + + def test_aalines__bounding_rect(self): + """Ensures draw aalines returns the correct bounding rect. + + Tests lines with endpoints on and off the surface and blending + enabled and disabled. + """ + line_color = pygame.Color("red") + surf_color = pygame.Color("blue") + width = height = 30 + # Using a rect to help manage where the lines are drawn. + pos_rect = pygame.Rect((0, 0), (width, height)) + + # Testing surfaces of different sizes. One larger than the pos_rect + # and one smaller (to test lines that span the surface). + for size in ((width + 5, height + 5), (width - 5, height - 5)): + surface = pygame.Surface(size, 0, 32) + surf_rect = surface.get_rect() + + # Move pos_rect to different positions to test line endpoints on + # and off the surface. + for pos in rect_corners_mids_and_center(surf_rect): + pos_rect.center = pos + # Shape: Triangle (if closed), ^ caret (if not closed). + pts = (pos_rect.midleft, pos_rect.midtop, pos_rect.midright) + pos = pts[0] # Rect position if nothing drawn. + + for closed in (True, False): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_aalines(surface, line_color, closed, pts) + + # Calculating the expected_rect after the lines are + # drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect(surface, surf_color, pos) + + self.assertEqual(bounding_rect, expected_rect) + + def test_aalines__surface_clip(self): + """Ensures draw aalines respects a surface's clip area.""" + surfw = surfh = 30 + aaline_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (11, 11)) + clip_rect.center = surface.get_rect().center + pos_rect = clip_rect.copy() # Manages the aalines's pos. + + # Test centering the pos_rect along the clip rect's edge to allow for + # drawing the aalines over the clip_rect's bounds. + for center in rect_corners_mids_and_center(clip_rect): + pos_rect.center = center + pts = (pos_rect.midtop, pos_rect.center, pos_rect.midbottom) + for closed in (True, False): # Test closed and not closed. + # Get the expected points by drawing the aalines without + # the clip area set. + surface.set_clip(None) + surface.fill(surface_color) + self.draw_aalines(surface, aaline_color, closed, pts) + + expected_pts = get_color_points( + surface, surface_color, clip_rect, False + ) + + # Clear the surface and set the clip area. Redraw the + # aalines and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_aalines(surface, aaline_color, closed, pts) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure the expected_pts + # are not surface_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + self.assertNotEqual(surface.get_at(pt), surface_color, pt) + else: + self.assertEqual(surface.get_at(pt), surface_color, pt) + + surface.unlock() + + +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever fully supports drawing aalines. +# class PythonDrawAALinesTest(AALinesMixin, PythonDrawTestCase): +# """Test draw_py module function aalines. +# +# This class inherits the general tests from AALinesMixin. It is also the +# class to add any draw_py.draw_aalines specific tests to. +# """ + + +class DrawAALinesTest(AALinesMixin, DrawTestCase): + """Test draw module function aalines. + + This class inherits the general tests from AALinesMixin. It is also the + class to add any draw.aalines specific tests to. + """ + + +### Polygon Testing ########################################################### + +SQUARE = ([0, 0], [3, 0], [3, 3], [0, 3]) +DIAMOND = [(1, 3), (3, 5), (5, 3), (3, 1)] +CROSS = ( + [2, 0], + [4, 0], + [4, 2], + [6, 2], + [6, 4], + [4, 4], + [4, 6], + [2, 6], + [2, 4], + [0, 4], + [0, 2], + [2, 2], +) + + +class DrawPolygonMixin: + """Mixin tests for drawing polygons. + + This class contains all the general polygon drawing tests. + """ + + def setUp(self): + self.surface = pygame.Surface((20, 20)) + + def test_polygon__args(self): + """Ensures draw polygon accepts the correct args.""" + bounds_rect = self.draw_polygon( + pygame.Surface((3, 3)), (0, 10, 0, 50), ((0, 0), (1, 1), (2, 2)), 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__args_without_width(self): + """Ensures draw polygon accepts the args without a width.""" + bounds_rect = self.draw_polygon( + pygame.Surface((2, 2)), (0, 0, 0, 50), ((0, 0), (1, 1), (2, 2)) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__kwargs(self): + """Ensures draw polygon accepts the correct kwargs + with and without a width arg. + """ + surface = pygame.Surface((4, 4)) + color = pygame.Color("yellow") + points = ((0, 0), (1, 1), (2, 2)) + kwargs_list = [ + {"surface": surface, "color": color, "points": points, "width": 1}, + {"surface": surface, "color": color, "points": points}, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_polygon(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__kwargs_order_independent(self): + """Ensures draw polygon's kwargs are not order dependent.""" + bounds_rect = self.draw_polygon( + color=(10, 20, 30), + surface=pygame.Surface((3, 2)), + width=0, + points=((0, 1), (1, 2), (2, 3)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__args_missing(self): + """Ensures draw polygon detects any missing required args.""" + surface = pygame.Surface((1, 1)) + color = pygame.Color("blue") + + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon(surface, color) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon() + + def test_polygon__kwargs_missing(self): + """Ensures draw polygon detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((1, 2)), + "color": pygame.Color("red"), + "points": ((2, 1), (2, 2), (2, 3)), + "width": 1, + } + + for name in ("points", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon(**invalid_kwargs) + + def test_polygon__arg_invalid_types(self): + """Ensures draw polygon detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + points = ((0, 1), (1, 2), (1, 3)) + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_polygon(surface, color, points, "1") + + with self.assertRaises(TypeError): + # Invalid points. + bounds_rect = self.draw_polygon(surface, color, (1, 2, 3)) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_polygon(surface, 2.3, points) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_polygon((1, 2, 3, 4), color, points) + + def test_polygon__kwarg_invalid_types(self): + """Ensures draw polygon detects invalid kwarg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("green") + points = ((0, 0), (1, 0), (2, 0)) + width = 1 + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "points": points, + "width": width, + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "points": points, + "width": width, + }, + { + "surface": surface, + "color": color, + "points": ((1,), (1,), (1,)), # Invalid points. + "width": width, + }, + {"surface": surface, "color": color, "points": points, "width": 1.2}, + ] # Invalid width. + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon(**kwargs) + + def test_polygon__kwarg_invalid_name(self): + """Ensures draw polygon detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + points = ((1, 1), (1, 2), (1, 3)) + kwargs_list = [ + { + "surface": surface, + "color": color, + "points": points, + "width": 1, + "invalid": 1, + }, + {"surface": surface, "color": color, "points": points, "invalid": 1}, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon(**kwargs) + + def test_polygon__args_and_kwargs(self): + """Ensures draw polygon accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 1)) + color = (255, 255, 0, 0) + points = ((0, 1), (1, 2), (2, 3)) + width = 0 + kwargs = {"surface": surface, "color": color, "points": points, "width": width} + + for name in ("surface", "color", "points", "width"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_polygon(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_polygon(surface, color, **kwargs) + elif "points" == name: + bounds_rect = self.draw_polygon(surface, color, points, **kwargs) + else: + bounds_rect = self.draw_polygon(surface, color, points, width, **kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__valid_width_values(self): + """Ensures draw polygon accepts different width values.""" + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + color = (10, 20, 30, 255) + kwargs = { + "surface": surface, + "color": color, + "points": ((1, 1), (2, 1), (2, 2), (1, 2)), + "width": None, + } + pos = kwargs["points"][0] + + for width in (-100, -10, -1, 0, 1, 10, 100): + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = color if width >= 0 else surface_color + + bounds_rect = self.draw_polygon(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__valid_points_format(self): + """Ensures draw polygon accepts different points formats.""" + expected_color = (10, 20, 30, 255) + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "points": None, + "width": 0, + } + + # The point type can be a tuple/list/Vector2. + point_types = ( + (tuple, tuple, tuple, tuple), # all tuples + (list, list, list, list), # all lists + (Vector2, Vector2, Vector2, Vector2), # all Vector2s + (list, Vector2, tuple, Vector2), + ) # mix + + # The point values can be ints or floats. + point_values = ( + ((1, 1), (2, 1), (2, 2), (1, 2)), + ((1, 1), (2.2, 1), (2.1, 2.2), (1, 2.1)), + ) + + # Each sequence of points can be a tuple or a list. + seq_types = (tuple, list) + + for point_type in point_types: + for values in point_values: + check_pos = values[0] + points = [point_type[i](pt) for i, pt in enumerate(values)] + + for seq_type in seq_types: + surface.fill(surface_color) # Clear for each test. + kwargs["points"] = seq_type(points) + + bounds_rect = self.draw_polygon(**kwargs) + + self.assertEqual(surface.get_at(check_pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__invalid_points_formats(self): + """Ensures draw polygon handles invalid points formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "points": None, + "width": 0, + } + + points_fmts = ( + ((1, 1), (2, 1), (2,)), # Too few coords. + ((1, 1), (2, 1), (2, 2, 2)), # Too many coords. + ((1, 1), (2, 1), (2, "2")), # Wrong type. + ((1, 1), (2, 1), {2, 3}), # Wrong type. + ((1, 1), (2, 1), dict(((2, 2), (3, 3)))), # Wrong type. + {(1, 1), (2, 1), (2, 2), (1, 2)}, # Wrong type. + dict(((1, 1), (2, 2), (3, 3), (4, 4))), + ) # Wrong type. + + for points in points_fmts: + kwargs["points"] = points + + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon(**kwargs) + + def test_polygon__invalid_points_values(self): + """Ensures draw polygon handles invalid points values correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "points": None, + "width": 0, + } + + points_fmts = ( + tuple(), # Too few points. + ((1, 1),), # Too few points. + ((1, 1), (2, 1)), + ) # Too few points. + + for points in points_fmts: + for seq_type in (tuple, list): # Test as tuples and lists. + kwargs["points"] = seq_type(points) + + with self.assertRaises(ValueError): + bounds_rect = self.draw_polygon(**kwargs) + + def test_polygon__valid_color_formats(self): + """Ensures draw polygon accepts different color formats.""" + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": None, + "points": ((1, 1), (2, 1), (2, 2), (1, 2)), + "width": 0, + } + pos = kwargs["points"][0] + greens = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in greens: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_polygon(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__invalid_color_formats(self): + """Ensures draw polygon handles invalid color formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 3)), + "color": None, + "points": ((1, 1), (2, 1), (2, 2), (1, 2)), + "width": 0, + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon(**kwargs) + + def test_draw_square(self): + self.draw_polygon(self.surface, RED, SQUARE, 0) + # note : there is a discussion (#234) if draw.polygon should include or + # not the right or lower border; here we stick with current behavior, + # eg include those borders ... + for x in range(4): + for y in range(4): + self.assertEqual(self.surface.get_at((x, y)), RED) + + def test_draw_diamond(self): + pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0) + self.draw_polygon(self.surface, GREEN, DIAMOND, 0) + # this diamond shape is equivalent to its four corners, plus inner square + for x, y in DIAMOND: + self.assertEqual(self.surface.get_at((x, y)), GREEN, msg=str((x, y))) + for x in range(2, 5): + for y in range(2, 5): + self.assertEqual(self.surface.get_at((x, y)), GREEN) + + def test_1_pixel_high_or_wide_shapes(self): + # 1. one-pixel-high, filled + pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0) + self.draw_polygon(self.surface, GREEN, [(x, 2) for x, _y in CROSS], 0) + cross_size = 6 # the maximum x or y coordinate of the cross + for x in range(cross_size + 1): + self.assertEqual(self.surface.get_at((x, 1)), RED) + self.assertEqual(self.surface.get_at((x, 2)), GREEN) + self.assertEqual(self.surface.get_at((x, 3)), RED) + pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0) + # 2. one-pixel-high, not filled + self.draw_polygon(self.surface, GREEN, [(x, 5) for x, _y in CROSS], 1) + for x in range(cross_size + 1): + self.assertEqual(self.surface.get_at((x, 4)), RED) + self.assertEqual(self.surface.get_at((x, 5)), GREEN) + self.assertEqual(self.surface.get_at((x, 6)), RED) + pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0) + # 3. one-pixel-wide, filled + self.draw_polygon(self.surface, GREEN, [(3, y) for _x, y in CROSS], 0) + for y in range(cross_size + 1): + self.assertEqual(self.surface.get_at((2, y)), RED) + self.assertEqual(self.surface.get_at((3, y)), GREEN) + self.assertEqual(self.surface.get_at((4, y)), RED) + pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0) + # 4. one-pixel-wide, not filled + self.draw_polygon(self.surface, GREEN, [(4, y) for _x, y in CROSS], 1) + for y in range(cross_size + 1): + self.assertEqual(self.surface.get_at((3, y)), RED) + self.assertEqual(self.surface.get_at((4, y)), GREEN) + self.assertEqual(self.surface.get_at((5, y)), RED) + + def test_draw_symetric_cross(self): + """non-regression on issue #234 : x and y where handled inconsistently. + + Also, the result is/was different whether we fill or not the polygon. + """ + # 1. case width = 1 (not filled: `polygon` calls internally the `lines` function) + pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0) + self.draw_polygon(self.surface, GREEN, CROSS, 1) + inside = [(x, 3) for x in range(1, 6)] + [(3, y) for y in range(1, 6)] + for x in range(10): + for y in range(10): + if (x, y) in inside: + self.assertEqual(self.surface.get_at((x, y)), RED) + elif (x in range(2, 5) and y < 7) or (y in range(2, 5) and x < 7): + # we are on the border of the cross: + self.assertEqual(self.surface.get_at((x, y)), GREEN) + else: + # we are outside + self.assertEqual(self.surface.get_at((x, y)), RED) + + # 2. case width = 0 (filled; this is the example from #234) + pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0) + self.draw_polygon(self.surface, GREEN, CROSS, 0) + inside = [(x, 3) for x in range(1, 6)] + [(3, y) for y in range(1, 6)] + for x in range(10): + for y in range(10): + if (x in range(2, 5) and y < 7) or (y in range(2, 5) and x < 7): + # we are on the border of the cross: + self.assertEqual( + self.surface.get_at((x, y)), GREEN, msg=str((x, y)) + ) + else: + # we are outside + self.assertEqual(self.surface.get_at((x, y)), RED) + + def test_illumine_shape(self): + """non-regression on issue #313""" + rect = pygame.Rect((0, 0, 20, 20)) + path_data = [ + (0, 0), + (rect.width - 1, 0), # upper border + (rect.width - 5, 5 - 1), + (5 - 1, 5 - 1), # upper inner + (5 - 1, rect.height - 5), + (0, rect.height - 1), + ] # lower diagonal + # The shape looks like this (the numbers are the indices of path_data) + + # 0**********************1 <-- upper border + # *********************** + # ********************** + # ********************* + # ****3**************2 <-- upper inner border + # ***** + # ***** (more lines here) + # ***** + # ****4 + # **** + # *** + # ** + # 5 + # + + # the current bug is that the "upper inner" line is not drawn, but only + # if 4 or some lower corner exists + pygame.draw.rect(self.surface, RED, (0, 0, 20, 20), 0) + + # 1. First without the corners 4 & 5 + self.draw_polygon(self.surface, GREEN, path_data[:4], 0) + for x in range(20): + self.assertEqual(self.surface.get_at((x, 0)), GREEN) # upper border + for x in range(4, rect.width - 5 + 1): + self.assertEqual(self.surface.get_at((x, 4)), GREEN) # upper inner + + # 2. with the corners 4 & 5 + pygame.draw.rect(self.surface, RED, (0, 0, 20, 20), 0) + self.draw_polygon(self.surface, GREEN, path_data, 0) + for x in range(4, rect.width - 5 + 1): + self.assertEqual(self.surface.get_at((x, 4)), GREEN) # upper inner + + def test_invalid_points(self): + self.assertRaises( + TypeError, + lambda: self.draw_polygon( + self.surface, RED, ((0, 0), (0, 20), (20, 20), 20), 0 + ), + ) + + def test_polygon__bounding_rect(self): + """Ensures draw polygon returns the correct bounding rect. + + Tests polygons on and off the surface and a range of width/thickness + values. + """ + polygon_color = pygame.Color("red") + surf_color = pygame.Color("black") + min_width = min_height = 5 + max_width = max_height = 7 + sizes = ((min_width, min_height), (max_width, max_height)) + surface = pygame.Surface((20, 20), 0, 32) + surf_rect = surface.get_rect() + # Make a rect that is bigger than the surface to help test drawing + # polygons off and partially off the surface. + big_rect = surf_rect.inflate(min_width * 2 + 1, min_height * 2 + 1) + + for pos in rect_corners_mids_and_center( + surf_rect + ) + rect_corners_mids_and_center(big_rect): + # A rect (pos_rect) is used to help create and position the + # polygon. Each of this rect's position attributes will be set to + # the pos value. + for attr in RECT_POSITION_ATTRIBUTES: + # Test using different rect sizes and thickness values. + for width, height in sizes: + pos_rect = pygame.Rect((0, 0), (width, height)) + setattr(pos_rect, attr, pos) + # Points form a triangle with no fully + # horizontal/vertical lines. + vertices = ( + pos_rect.midleft, + pos_rect.midtop, + pos_rect.bottomright, + ) + + for thickness in range(4): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_polygon( + surface, polygon_color, vertices, thickness + ) + + # Calculating the expected_rect after the polygon + # is drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect( + surface, surf_color, vertices[0] + ) + + self.assertEqual( + bounding_rect, + expected_rect, + f"thickness={thickness}", + ) + + def test_polygon__surface_clip(self): + """Ensures draw polygon respects a surface's clip area. + + Tests drawing the polygon filled and unfilled. + """ + surfw = surfh = 30 + polygon_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (8, 10)) + clip_rect.center = surface.get_rect().center + pos_rect = clip_rect.copy() # Manages the polygon's pos. + + for width in (0, 1): # Filled and unfilled. + # Test centering the polygon along the clip rect's edge. + for center in rect_corners_mids_and_center(clip_rect): + # Get the expected points by drawing the polygon without the + # clip area set. + pos_rect.center = center + vertices = ( + pos_rect.topleft, + pos_rect.topright, + pos_rect.bottomright, + pos_rect.bottomleft, + ) + surface.set_clip(None) + surface.fill(surface_color) + self.draw_polygon(surface, polygon_color, vertices, width) + expected_pts = get_color_points(surface, polygon_color, clip_rect) + + # Clear the surface and set the clip area. Redraw the polygon + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_polygon(surface, polygon_color, vertices, width) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure only the expected_pts + # are the polygon_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + expected_color = polygon_color + else: + expected_color = surface_color + + self.assertEqual(surface.get_at(pt), expected_color, pt) + + surface.unlock() + + +class DrawPolygonTest(DrawPolygonMixin, DrawTestCase): + """Test draw module function polygon. + + This class inherits the general tests from DrawPolygonMixin. It is also + the class to add any draw.polygon specific tests to. + """ + + +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever fully supports drawing polygons. +# @unittest.skip('draw_py.draw_polygon not fully supported yet') +# class PythonDrawPolygonTest(DrawPolygonMixin, PythonDrawTestCase): +# """Test draw_py module function draw_polygon. +# +# This class inherits the general tests from DrawPolygonMixin. It is also +# the class to add any draw_py.draw_polygon specific tests to. +# """ + + +### Rect Testing ############################################################## + + +class DrawRectMixin: + """Mixin tests for drawing rects. + + This class contains all the general rect drawing tests. + """ + + def test_rect__args(self): + """Ensures draw rect accepts the correct args.""" + bounds_rect = self.draw_rect( + pygame.Surface((2, 2)), + (20, 10, 20, 150), + pygame.Rect((0, 0), (1, 1)), + 2, + 1, + 2, + 3, + 4, + 5, + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__args_without_width(self): + """Ensures draw rect accepts the args without a width and borders.""" + bounds_rect = self.draw_rect( + pygame.Surface((3, 5)), (0, 0, 0, 255), pygame.Rect((0, 0), (1, 1)) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__kwargs(self): + """Ensures draw rect accepts the correct kwargs + with and without a width and border_radius arg. + """ + kwargs_list = [ + { + "surface": pygame.Surface((5, 5)), + "color": pygame.Color("red"), + "rect": pygame.Rect((0, 0), (1, 2)), + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0, + }, + { + "surface": pygame.Surface((1, 2)), + "color": (0, 100, 200), + "rect": (0, 0, 1, 1), + }, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_rect(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__kwargs_order_independent(self): + """Ensures draw rect's kwargs are not order dependent.""" + bounds_rect = self.draw_rect( + color=(0, 1, 2), + border_radius=10, + surface=pygame.Surface((2, 3)), + border_top_left_radius=5, + width=-2, + border_top_right_radius=20, + border_bottom_right_radius=0, + rect=pygame.Rect((0, 0), (0, 0)), + border_bottom_left_radius=15, + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__args_missing(self): + """Ensures draw rect detects any missing required args.""" + surface = pygame.Surface((1, 1)) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect(surface, pygame.Color("white")) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect() + + def test_rect__kwargs_missing(self): + """Ensures draw rect detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((1, 3)), + "color": pygame.Color("red"), + "rect": pygame.Rect((0, 0), (2, 2)), + "width": 5, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0, + } + + for name in ("rect", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect(**invalid_kwargs) + + def test_rect__arg_invalid_types(self): + """Ensures draw rect detects invalid arg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("white") + rect = pygame.Rect((1, 1), (1, 1)) + + with self.assertRaises(TypeError): + # Invalid border_bottom_right_radius. + bounds_rect = self.draw_rect( + surface, color, rect, 2, border_bottom_right_radius="rad" + ) + + with self.assertRaises(TypeError): + # Invalid border_bottom_left_radius. + bounds_rect = self.draw_rect( + surface, color, rect, 2, border_bottom_left_radius="rad" + ) + + with self.assertRaises(TypeError): + # Invalid border_top_right_radius. + bounds_rect = self.draw_rect( + surface, color, rect, 2, border_top_right_radius="rad" + ) + + with self.assertRaises(TypeError): + # Invalid border_top_left_radius. + bounds_rect = self.draw_rect( + surface, color, rect, 2, border_top_left_radius="draw" + ) + + with self.assertRaises(TypeError): + # Invalid border_radius. + bounds_rect = self.draw_rect(surface, color, rect, 2, "rad") + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_rect(surface, color, rect, "2", 4) + + with self.assertRaises(TypeError): + # Invalid rect. + bounds_rect = self.draw_rect(surface, color, (1, 2, 3), 2, 6) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_rect(surface, 2.3, rect, 3, 8) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_rect(rect, color, rect, 4, 10) + + def test_rect__kwarg_invalid_types(self): + """Ensures draw rect detects invalid kwarg types.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("red") + rect = pygame.Rect((0, 0), (1, 1)) + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "rect": rect, + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0, + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "rect": rect, + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0, + }, + { + "surface": surface, + "color": color, + "rect": (1, 1, 2), # Invalid rect. + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1.1, # Invalid width. + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "border_radius": 10.5, # Invalid border_radius. + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5.5, # Invalid top_left_radius. + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": "a", # Invalid top_right_radius. + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": "c", # Invalid bottom_left_radius + "border_bottom_right_radius": 0, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": "d", # Invalid bottom_right. + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect(**kwargs) + + def test_rect__kwarg_invalid_name(self): + """Ensures draw rect detects invalid kwarg names.""" + surface = pygame.Surface((2, 1)) + color = pygame.Color("green") + rect = pygame.Rect((0, 0), (3, 3)) + kwargs_list = [ + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0, + "invalid": 1, + }, + {"surface": surface, "color": color, "rect": rect, "invalid": 1}, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect(**kwargs) + + def test_rect__args_and_kwargs(self): + """Ensures draw rect accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 1)) + color = (255, 255, 255, 0) + rect = pygame.Rect((1, 0), (2, 5)) + width = 0 + kwargs = {"surface": surface, "color": color, "rect": rect, "width": width} + + for name in ("surface", "color", "rect", "width"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_rect(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_rect(surface, color, **kwargs) + elif "rect" == name: + bounds_rect = self.draw_rect(surface, color, rect, **kwargs) + else: + bounds_rect = self.draw_rect(surface, color, rect, width, **kwargs) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__valid_width_values(self): + """Ensures draw rect accepts different width values.""" + pos = (1, 1) + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + color = (1, 2, 3, 255) + kwargs = { + "surface": surface, + "color": color, + "rect": pygame.Rect(pos, (2, 2)), + "width": None, + } + + for width in (-1000, -10, -1, 0, 1, 10, 1000): + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = color if width >= 0 else surface_color + + bounds_rect = self.draw_rect(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__valid_rect_formats(self): + """Ensures draw rect accepts different rect formats.""" + pos = (1, 1) + expected_color = pygame.Color("yellow") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + kwargs = {"surface": surface, "color": expected_color, "rect": None, "width": 0} + rects = ( + pygame.Rect(pos, (1, 1)), + (pos, (2, 2)), + (pos[0], pos[1], 3, 3), + [pos, (2.1, 2.2)], + ) + + for rect in rects: + surface.fill(surface_color) # Clear for each test. + kwargs["rect"] = rect + + bounds_rect = self.draw_rect(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__invalid_rect_formats(self): + """Ensures draw rect handles invalid rect formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "rect": None, + "width": 0, + } + + invalid_fmts = ( + [], + [1], + [1, 2], + [1, 2, 3], + [1, 2, 3, 4, 5], + {1, 2, 3, 4}, + [1, 2, 3, "4"], + ) + + for rect in invalid_fmts: + kwargs["rect"] = rect + + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect(**kwargs) + + def test_rect__valid_color_formats(self): + """Ensures draw rect accepts different color formats.""" + pos = (1, 1) + red_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": None, + "rect": pygame.Rect(pos, (1, 1)), + "width": 3, + } + reds = ((255, 0, 0), (255, 0, 0, 255), surface.map_rgb(red_color), red_color) + + for color in reds: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = red_color + + bounds_rect = self.draw_rect(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__invalid_color_formats(self): + """Ensures draw rect handles invalid color formats correctly.""" + pos = (1, 1) + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": None, + "rect": pygame.Rect(pos, (1, 1)), + "width": 1, + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect(**kwargs) + + def test_rect__fill(self): + self.surf_w, self.surf_h = self.surf_size = (320, 200) + self.surf = pygame.Surface(self.surf_size, pygame.SRCALPHA) + self.color = (1, 13, 24, 205) + rect = pygame.Rect(10, 10, 25, 20) + drawn = self.draw_rect(self.surf, self.color, rect, 0) + + self.assertEqual(drawn, rect) + + # Should be colored where it's supposed to be + for pt in test_utils.rect_area_pts(rect): + color_at_pt = self.surf.get_at(pt) + + self.assertEqual(color_at_pt, self.color) + + # And not where it shouldn't + for pt in test_utils.rect_outer_bounds(rect): + color_at_pt = self.surf.get_at(pt) + + self.assertNotEqual(color_at_pt, self.color) + + # Issue #310: Cannot draw rectangles that are 1 pixel high + bgcolor = pygame.Color("black") + self.surf.fill(bgcolor) + hrect = pygame.Rect(1, 1, self.surf_w - 2, 1) + vrect = pygame.Rect(1, 3, 1, self.surf_h - 4) + + drawn = self.draw_rect(self.surf, self.color, hrect, 0) + + self.assertEqual(drawn, hrect) + + x, y = hrect.topleft + w, h = hrect.size + + self.assertEqual(self.surf.get_at((x - 1, y)), bgcolor) + self.assertEqual(self.surf.get_at((x + w, y)), bgcolor) + for i in range(x, x + w): + self.assertEqual(self.surf.get_at((i, y)), self.color) + + drawn = self.draw_rect(self.surf, self.color, vrect, 0) + + self.assertEqual(drawn, vrect) + + x, y = vrect.topleft + w, h = vrect.size + + self.assertEqual(self.surf.get_at((x, y - 1)), bgcolor) + self.assertEqual(self.surf.get_at((x, y + h)), bgcolor) + for i in range(y, y + h): + self.assertEqual(self.surf.get_at((x, i)), self.color) + + def test_rect__one_pixel_lines(self): + self.surf = pygame.Surface((320, 200), pygame.SRCALPHA) + self.color = (1, 13, 24, 205) + + rect = pygame.Rect(10, 10, 56, 20) + + drawn = self.draw_rect(self.surf, self.color, rect, 1) + + self.assertEqual(drawn, rect) + + # Should be colored where it's supposed to be + for pt in test_utils.rect_perimeter_pts(drawn): + color_at_pt = self.surf.get_at(pt) + + self.assertEqual(color_at_pt, self.color) + + # And not where it shouldn't + for pt in test_utils.rect_outer_bounds(drawn): + color_at_pt = self.surf.get_at(pt) + + self.assertNotEqual(color_at_pt, self.color) + + def test_rect__draw_line_width(self): + surface = pygame.Surface((100, 100)) + surface.fill("black") + color = pygame.Color(255, 255, 255) + rect_width = 80 + rect_height = 50 + line_width = 10 + pygame.draw.rect( + surface, color, pygame.Rect(0, 0, rect_width, rect_height), line_width + ) + for i in range(line_width): + self.assertEqual(surface.get_at((i, i)), color) + self.assertEqual(surface.get_at((rect_width - i - 1, i)), color) + self.assertEqual(surface.get_at((i, rect_height - i - 1)), color) + self.assertEqual( + surface.get_at((rect_width - i - 1, rect_height - i - 1)), color + ) + self.assertEqual(surface.get_at((line_width, line_width)), (0, 0, 0)) + self.assertEqual( + surface.get_at((rect_width - line_width - 1, line_width)), (0, 0, 0) + ) + self.assertEqual( + surface.get_at((line_width, rect_height - line_width - 1)), (0, 0, 0) + ) + self.assertEqual( + surface.get_at((rect_width - line_width - 1, rect_height - line_width - 1)), + (0, 0, 0), + ) + + def test_rect__bounding_rect(self): + """Ensures draw rect returns the correct bounding rect. + + Tests rects on and off the surface and a range of width/thickness + values. + """ + rect_color = pygame.Color("red") + surf_color = pygame.Color("black") + min_width = min_height = 5 + max_width = max_height = 7 + sizes = ((min_width, min_height), (max_width, max_height)) + surface = pygame.Surface((20, 20), 0, 32) + surf_rect = surface.get_rect() + # Make a rect that is bigger than the surface to help test drawing + # rects off and partially off the surface. + big_rect = surf_rect.inflate(min_width * 2 + 1, min_height * 2 + 1) + + for pos in rect_corners_mids_and_center( + surf_rect + ) + rect_corners_mids_and_center(big_rect): + # Each of the rect's position attributes will be set to the pos + # value. + for attr in RECT_POSITION_ATTRIBUTES: + # Test using different rect sizes and thickness values. + for width, height in sizes: + rect = pygame.Rect((0, 0), (width, height)) + setattr(rect, attr, pos) + + for thickness in range(4): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_rect( + surface, rect_color, rect, thickness + ) + + # Calculating the expected_rect after the rect is + # drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect( + surface, surf_color, rect.topleft + ) + + self.assertEqual( + bounding_rect, + expected_rect, + f"thickness={thickness}", + ) + + def test_rect__surface_clip(self): + """Ensures draw rect respects a surface's clip area. + + Tests drawing the rect filled and unfilled. + """ + surfw = surfh = 30 + rect_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (8, 10)) + clip_rect.center = surface.get_rect().center + test_rect = clip_rect.copy() # Manages the rect's pos. + + for width in (0, 1): # Filled and unfilled. + # Test centering the rect along the clip rect's edge. + for center in rect_corners_mids_and_center(clip_rect): + # Get the expected points by drawing the rect without the + # clip area set. + test_rect.center = center + surface.set_clip(None) + surface.fill(surface_color) + self.draw_rect(surface, rect_color, test_rect, width) + expected_pts = get_color_points(surface, rect_color, clip_rect) + + # Clear the surface and set the clip area. Redraw the rect + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_rect(surface, rect_color, test_rect, width) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure only the expected_pts + # are the rect_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + expected_color = rect_color + else: + expected_color = surface_color + + self.assertEqual(surface.get_at(pt), expected_color, pt) + + surface.unlock() + + +class DrawRectTest(DrawRectMixin, DrawTestCase): + """Test draw module function rect. + + This class inherits the general tests from DrawRectMixin. It is also the + class to add any draw.rect specific tests to. + """ + + +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever properly supports drawing rects. +# @unittest.skip('draw_py.draw_rect not supported yet') +# class PythonDrawRectTest(DrawRectMixin, PythonDrawTestCase): +# """Test draw_py module function draw_rect. +# +# This class inherits the general tests from DrawRectMixin. It is also the +# class to add any draw_py.draw_rect specific tests to. +# """ + + +### Circle Testing ############################################################ + + +class DrawCircleMixin: + """Mixin tests for drawing circles. + + This class contains all the general circle drawing tests. + """ + + def test_circle__args(self): + """Ensures draw circle accepts the correct args.""" + bounds_rect = self.draw_circle( + pygame.Surface((3, 3)), (0, 10, 0, 50), (0, 0), 3, 1, 1, 0, 1, 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__args_without_width(self): + """Ensures draw circle accepts the args without a width and + quadrants.""" + bounds_rect = self.draw_circle(pygame.Surface((2, 2)), (0, 0, 0, 50), (1, 1), 1) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__args_with_negative_width(self): + """Ensures draw circle accepts the args with negative width.""" + bounds_rect = self.draw_circle( + pygame.Surface((2, 2)), (0, 0, 0, 50), (1, 1), 1, -1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + self.assertEqual(bounds_rect, pygame.Rect(1, 1, 0, 0)) + + def test_circle__args_with_width_gt_radius(self): + """Ensures draw circle accepts the args with width > radius.""" + bounds_rect = self.draw_circle( + pygame.Surface((2, 2)), (0, 0, 0, 50), (1, 1), 2, 3, 0, 0, 0, 0 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + self.assertEqual(bounds_rect, pygame.Rect(0, 0, 2, 2)) + + def test_circle__kwargs(self): + """Ensures draw circle accepts the correct kwargs + with and without a width and quadrant arguments. + """ + kwargs_list = [ + { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("yellow"), + "center": (2, 2), + "radius": 2, + "width": 1, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": False, + "draw_bottom_right": True, + }, + { + "surface": pygame.Surface((2, 1)), + "color": (0, 10, 20), + "center": (1, 1), + "radius": 1, + }, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_circle(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__kwargs_order_independent(self): + """Ensures draw circle's kwargs are not order dependent.""" + bounds_rect = self.draw_circle( + draw_top_right=False, + color=(10, 20, 30), + surface=pygame.Surface((3, 2)), + width=0, + draw_bottom_left=False, + center=(1, 0), + draw_bottom_right=False, + radius=2, + draw_top_left=True, + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__args_missing(self): + """Ensures draw circle detects any missing required args.""" + surface = pygame.Surface((1, 1)) + color = pygame.Color("blue") + + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle(surface, color, (0, 0)) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle(surface, color) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle() + + def test_circle__kwargs_missing(self): + """Ensures draw circle detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((1, 2)), + "color": pygame.Color("red"), + "center": (1, 0), + "radius": 2, + "width": 1, + "draw_top_right": False, + "draw_top_left": False, + "draw_bottom_left": False, + "draw_bottom_right": True, + } + + for name in ("radius", "center", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle(**invalid_kwargs) + + def test_circle__arg_invalid_types(self): + """Ensures draw circle detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + center = (1, 1) + radius = 1 + + with self.assertRaises(TypeError): + # Invalid draw_top_right. + bounds_rect = self.draw_circle( + surface, color, center, radius, 1, "a", 1, 1, 1 + ) + + with self.assertRaises(TypeError): + # Invalid draw_top_left. + bounds_rect = self.draw_circle( + surface, color, center, radius, 1, 1, "b", 1, 1 + ) + + with self.assertRaises(TypeError): + # Invalid draw_bottom_left. + bounds_rect = self.draw_circle( + surface, color, center, radius, 1, 1, 1, "c", 1 + ) + + with self.assertRaises(TypeError): + # Invalid draw_bottom_right. + bounds_rect = self.draw_circle( + surface, color, center, radius, 1, 1, 1, 1, "d" + ) + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_circle(surface, color, center, radius, "1") + + with self.assertRaises(TypeError): + # Invalid radius. + bounds_rect = self.draw_circle(surface, color, center, "2") + + with self.assertRaises(TypeError): + # Invalid center. + bounds_rect = self.draw_circle(surface, color, (1, 2, 3), radius) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_circle(surface, 2.3, center, radius) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_circle((1, 2, 3, 4), color, center, radius) + + def test_circle__kwarg_invalid_types(self): + """Ensures draw circle detects invalid kwarg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("green") + center = (0, 1) + radius = 1 + width = 1 + quadrant = 1 + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "center": center, + "radius": radius, + "width": width, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "center": center, + "radius": radius, + "width": width, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + }, + { + "surface": surface, + "color": color, + "center": (1, 1, 1), # Invalid center. + "radius": radius, + "width": width, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + }, + { + "surface": surface, + "color": color, + "center": center, + "radius": "1", # Invalid radius. + "width": width, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + }, + { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": 1.2, # Invalid width. + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + }, + { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": width, + "draw_top_right": "True", # Invalid draw_top_right + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + }, + { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": width, + "draw_top_right": True, + "draw_top_left": "True", # Invalid draw_top_left + "draw_bottom_left": True, + "draw_bottom_right": True, + }, + { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": width, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": 3.14, # Invalid draw_bottom_left + "draw_bottom_right": True, + }, + { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": width, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": "quadrant", # Invalid draw_bottom_right + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle(**kwargs) + + def test_circle__kwarg_invalid_name(self): + """Ensures draw circle detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + center = (0, 0) + radius = 2 + kwargs_list = [ + { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": 1, + "quadrant": 1, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + }, + { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "invalid": 1, + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle(**kwargs) + + def test_circle__args_and_kwargs(self): + """Ensures draw circle accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 1)) + color = (255, 255, 0, 0) + center = (1, 0) + radius = 2 + width = 0 + draw_top_right = True + draw_top_left = False + draw_bottom_left = False + draw_bottom_right = True + kwargs = { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": width, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + } + + for name in ( + "surface", + "color", + "center", + "radius", + "width", + "draw_top_right", + "draw_top_left", + "draw_bottom_left", + "draw_bottom_right", + ): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_circle(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_circle(surface, color, **kwargs) + elif "center" == name: + bounds_rect = self.draw_circle(surface, color, center, **kwargs) + elif "radius" == name: + bounds_rect = self.draw_circle(surface, color, center, radius, **kwargs) + elif "width" == name: + bounds_rect = self.draw_circle( + surface, color, center, radius, width, **kwargs + ) + elif "draw_top_right" == name: + bounds_rect = self.draw_circle( + surface, color, center, radius, width, draw_top_right, **kwargs + ) + elif "draw_top_left" == name: + bounds_rect = self.draw_circle( + surface, + color, + center, + radius, + width, + draw_top_right, + draw_top_left, + **kwargs, + ) + elif "draw_bottom_left" == name: + bounds_rect = self.draw_circle( + surface, + color, + center, + radius, + width, + draw_top_right, + draw_top_left, + draw_bottom_left, + **kwargs, + ) + else: + bounds_rect = self.draw_circle( + surface, + color, + center, + radius, + width, + draw_top_right, + draw_top_left, + draw_bottom_left, + draw_bottom_right, + **kwargs, + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__valid_width_values(self): + """Ensures draw circle accepts different width values.""" + center = (2, 2) + radius = 1 + pos = (center[0] - radius, center[1]) + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + color = (10, 20, 30, 255) + kwargs = { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": None, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + } + + for width in (-100, -10, -1, 0, 1, 10, 100): + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = color if width >= 0 else surface_color + + bounds_rect = self.draw_circle(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__valid_radius_values(self): + """Ensures draw circle accepts different radius values.""" + pos = center = (2, 2) + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + color = (10, 20, 30, 255) + kwargs = { + "surface": surface, + "color": color, + "center": center, + "radius": None, + "width": 0, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + } + + for radius in (-10, -1, 0, 1, 10): + surface.fill(surface_color) # Clear for each test. + kwargs["radius"] = radius + expected_color = color if radius > 0 else surface_color + + bounds_rect = self.draw_circle(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__valid_center_formats(self): + """Ensures draw circle accepts different center formats.""" + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((4, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "center": None, + "radius": 1, + "width": 0, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + } + x, y = 2, 2 # center position + + # The center values can be ints or floats. + for center in ((x, y), (x + 0.1, y), (x, y + 0.1), (x + 0.1, y + 0.1)): + # The center type can be a tuple/list/Vector2. + for seq_type in (tuple, list, Vector2): + surface.fill(surface_color) # Clear for each test. + kwargs["center"] = seq_type(center) + + bounds_rect = self.draw_circle(**kwargs) + + self.assertEqual(surface.get_at((x, y)), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__valid_color_formats(self): + """Ensures draw circle accepts different color formats.""" + center = (2, 2) + radius = 1 + pos = (center[0] - radius, center[1]) + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": None, + "center": center, + "radius": radius, + "width": 0, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + } + greens = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in greens: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_circle(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__invalid_color_formats(self): + """Ensures draw circle handles invalid color formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 3)), + "color": None, + "center": (1, 2), + "radius": 1, + "width": 0, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle(**kwargs) + + def test_circle__floats(self): + """Ensure that floats are accepted.""" + draw.circle( + surface=pygame.Surface((4, 4)), + color=(255, 255, 127), + center=(1.5, 1.5), + radius=1.3, + width=0, + draw_top_right=True, + draw_top_left=True, + draw_bottom_left=True, + draw_bottom_right=True, + ) + + draw.circle( + surface=pygame.Surface((4, 4)), + color=(255, 255, 127), + center=Vector2(1.5, 1.5), + radius=1.3, + width=0, + draw_top_right=True, + draw_top_left=True, + draw_bottom_left=True, + draw_bottom_right=True, + ) + + draw.circle(pygame.Surface((2, 2)), (0, 0, 0, 50), (1.3, 1.3), 1.2) + + # def test_circle_clip(self): + # """ maybe useful to help work out circle clip algorithm.""" + # MAX = max + # MIN = min + # posx=30 + # posy=15 + # radius=1 + # l=29 + # t=14 + # r=30 + # b=16 + # clip_rect_x=0 + # clip_rect_y=0 + # clip_rect_w=30 + # clip_rect_h=30 + + # l = MAX(posx - radius, clip_rect_x) + # t = MAX(posy - radius, clip_rect_y) + # r = MIN(posx + radius, clip_rect_x + clip_rect_w) + # b = MIN(posy + radius, clip_rect_y + clip_rect_h) + + # l, t, MAX(r - l, 0), MAX(b - t, 0) + + def test_circle__bounding_rect(self): + """Ensures draw circle returns the correct bounding rect. + + Tests circles on and off the surface and a range of width/thickness + values. + """ + circle_color = pygame.Color("red") + surf_color = pygame.Color("black") + max_radius = 3 + surface = pygame.Surface((30, 30), 0, 32) + surf_rect = surface.get_rect() + # Make a rect that is bigger than the surface to help test drawing + # circles off and partially off the surface. Make this rect such that + # when centering the test circle on one of its corners, the circle is + # drawn fully off the test surface, but a rect bounding the circle + # would still overlap with the test surface. + big_rect = surf_rect.inflate(max_radius * 2 - 1, max_radius * 2 - 1) + + for pos in rect_corners_mids_and_center( + surf_rect + ) + rect_corners_mids_and_center(big_rect): + # Test using different radius and thickness values. + for radius in range(max_radius + 1): + for thickness in range(radius + 1): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_circle( + surface, circle_color, pos, radius, thickness + ) + + # Calculating the expected_rect after the circle is + # drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect(surface, surf_color, pos) + + # print("pos:%s:, radius:%s:, thickness:%s:" % (pos, radius, thickness)) + # self.assertEqual(bounding_rect, expected_rect) + with self.subTest( + surface=surface, + circle_color=circle_color, + pos=pos, + radius=radius, + thickness=thickness, + ): + self.assertEqual(bounding_rect, expected_rect) + + def test_circle_negative_radius(self): + """Ensures negative radius circles return zero sized bounding rect.""" + surf = pygame.Surface((200, 200)) + color = (0, 0, 0, 50) + center = surf.get_height() // 2, surf.get_height() // 2 + + bounding_rect = self.draw_circle(surf, color, center, radius=-1, width=1) + self.assertEqual(bounding_rect.size, (0, 0)) + + def test_circle_zero_radius(self): + """Ensures zero radius circles does not draw a center pixel. + + NOTE: This is backwards incompatible behaviour with 1.9.x. + """ + surf = pygame.Surface((200, 200)) + circle_color = pygame.Color("red") + surf_color = pygame.Color("black") + center = (100, 100) + radius = 0 + width = 1 + + bounding_rect = self.draw_circle(surf, circle_color, center, radius, width) + expected_rect = create_bounding_rect(surf, surf_color, center) + self.assertEqual(bounding_rect, expected_rect) + self.assertEqual(bounding_rect, pygame.Rect(100, 100, 0, 0)) + + def test_circle__surface_clip(self): + """Ensures draw circle respects a surface's clip area. + + Tests drawing the circle filled and unfilled. + """ + surfw = surfh = 25 + circle_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (10, 10)) + clip_rect.center = surface.get_rect().center + radius = clip_rect.w // 2 + 1 + + for width in (0, 1): # Filled and unfilled. + # Test centering the circle along the clip rect's edge. + for center in rect_corners_mids_and_center(clip_rect): + # Get the expected points by drawing the circle without the + # clip area set. + surface.set_clip(None) + surface.fill(surface_color) + self.draw_circle(surface, circle_color, center, radius, width) + expected_pts = get_color_points(surface, circle_color, clip_rect) + + # Clear the surface and set the clip area. Redraw the circle + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_circle(surface, circle_color, center, radius, width) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure only the expected_pts + # are the circle_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + expected_color = circle_color + else: + expected_color = surface_color + + self.assertEqual(surface.get_at(pt), expected_color, pt) + + surface.unlock() + + def test_circle_shape(self): + """Ensures there are no holes in the circle, and no overdrawing. + + Tests drawing a thick circle. + Measures the distance of the drawn pixels from the circle center. + """ + surfw = surfh = 100 + circle_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + (cx, cy) = center = (50, 50) + radius = 45 + width = 25 + + dest_rect = self.draw_circle(surface, circle_color, center, radius, width) + + for pt in test_utils.rect_area_pts(dest_rect): + x, y = pt + sqr_distance = (x - cx) ** 2 + (y - cy) ** 2 + if (radius - width + 1) ** 2 < sqr_distance < (radius - 1) ** 2: + self.assertEqual(surface.get_at(pt), circle_color) + if ( + sqr_distance < (radius - width - 1) ** 2 + or sqr_distance > (radius + 1) ** 2 + ): + self.assertEqual(surface.get_at(pt), surface_color) + + def test_circle__diameter(self): + """Ensures draw circle is twice size of radius high and wide.""" + surf = pygame.Surface((200, 200)) + color = (0, 0, 0, 50) + center = surf.get_height() // 2, surf.get_height() // 2 + width = 1 + radius = 6 + for radius in range(1, 65): + bounding_rect = self.draw_circle(surf, color, center, radius, width) + self.assertEqual(bounding_rect.width, radius * 2) + self.assertEqual(bounding_rect.height, radius * 2) + + def test_x_bounds(self): + """ensures a circle is drawn properly when there is a negative x, or a big x.""" + + surf = pygame.Surface((200, 200)) + bgcolor = (0, 0, 0, 255) + surf.fill(bgcolor) + color = (255, 0, 0, 255) + width = 1 + radius = 10 + + where = (0, 30) + bounding_rect1 = self.draw_circle(surf, color, where, radius=radius) + self.assertEqual( + bounding_rect1, + pygame.Rect(0, where[1] - radius, where[0] + radius, radius * 2), + ) + self.assertEqual( + surf.get_at((where[0] if where[0] > 0 else 0, where[1])), color + ) + self.assertEqual(surf.get_at((where[0] + radius + 1, where[1])), bgcolor) + self.assertEqual(surf.get_at((where[0] + radius - 1, where[1])), color) + + surf.fill(bgcolor) + where = (-1e30, 80) + bounding_rect1 = self.draw_circle(surf, color, where, radius=radius) + self.assertEqual(bounding_rect1, pygame.Rect(where[0], where[1], 0, 0)) + self.assertEqual(surf.get_at((0 + radius, where[1])), bgcolor) + + surf.fill(bgcolor) + where = (surf.get_width() + radius * 2, 80) + bounding_rect1 = self.draw_circle(surf, color, where, radius=radius) + self.assertEqual(bounding_rect1, pygame.Rect(where[0], where[1], 0, 0)) + self.assertEqual(surf.get_at((0, where[1])), bgcolor) + self.assertEqual(surf.get_at((0 + radius // 2, where[1])), bgcolor) + self.assertEqual(surf.get_at((surf.get_width() - 1, where[1])), bgcolor) + self.assertEqual(surf.get_at((surf.get_width() - radius, where[1])), bgcolor) + + surf.fill(bgcolor) + where = (-1, 80) + bounding_rect1 = self.draw_circle(surf, color, where, radius=radius) + self.assertEqual( + bounding_rect1, + pygame.Rect(0, where[1] - radius, where[0] + radius, radius * 2), + ) + self.assertEqual( + surf.get_at((where[0] if where[0] > 0 else 0, where[1])), color + ) + self.assertEqual(surf.get_at((where[0] + radius, where[1])), bgcolor) + self.assertEqual(surf.get_at((where[0] + radius - 1, where[1])), color) + + +class DrawCircleTest(DrawCircleMixin, DrawTestCase): + """Test draw module function circle. + + This class inherits the general tests from DrawCircleMixin. It is also + the class to add any draw.circle specific tests to. + """ + + +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever properly supports drawing circles. +# @unittest.skip('draw_py.draw_circle not supported yet') +# class PythonDrawCircleTest(DrawCircleMixin, PythonDrawTestCase): +# """Test draw_py module function draw_circle." +# +# This class inherits the general tests from DrawCircleMixin. It is also +# the class to add any draw_py.draw_circle specific tests to. +# """ + + +### Arc Testing ############################################################### + + +class DrawArcMixin: + """Mixin tests for drawing arcs. + + This class contains all the general arc drawing tests. + """ + + def test_arc__args(self): + """Ensures draw arc accepts the correct args.""" + bounds_rect = self.draw_arc( + pygame.Surface((3, 3)), (0, 10, 0, 50), (1, 1, 2, 2), 0, 1, 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__args_without_width(self): + """Ensures draw arc accepts the args without a width.""" + bounds_rect = self.draw_arc( + pygame.Surface((2, 2)), (1, 1, 1, 99), pygame.Rect((0, 0), (2, 2)), 1.1, 2.1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__args_with_negative_width(self): + """Ensures draw arc accepts the args with negative width.""" + bounds_rect = self.draw_arc( + pygame.Surface((3, 3)), (10, 10, 50, 50), (1, 1, 2, 2), 0, 1, -1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + self.assertEqual(bounds_rect, pygame.Rect(1, 1, 0, 0)) + + def test_arc__args_with_width_gt_radius(self): + """Ensures draw arc accepts the args with + width > rect.w // 2 and width > rect.h // 2. + """ + rect = pygame.Rect((0, 0), (4, 4)) + bounds_rect = self.draw_arc( + pygame.Surface((3, 3)), (10, 10, 50, 50), rect, 0, 45, rect.w // 2 + 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + bounds_rect = self.draw_arc( + pygame.Surface((3, 3)), (10, 10, 50, 50), rect, 0, 45, rect.h // 2 + 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__kwargs(self): + """Ensures draw arc accepts the correct kwargs + with and without a width arg. + """ + kwargs_list = [ + { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("yellow"), + "rect": pygame.Rect((0, 0), (3, 2)), + "start_angle": 0.5, + "stop_angle": 3, + "width": 1, + }, + { + "surface": pygame.Surface((2, 1)), + "color": (0, 10, 20), + "rect": (0, 0, 2, 2), + "start_angle": 1, + "stop_angle": 3.1, + }, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_arc(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__kwargs_order_independent(self): + """Ensures draw arc's kwargs are not order dependent.""" + bounds_rect = self.draw_arc( + stop_angle=1, + start_angle=2.2, + color=(1, 2, 3), + surface=pygame.Surface((3, 2)), + width=1, + rect=pygame.Rect((1, 0), (2, 3)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__args_missing(self): + """Ensures draw arc detects any missing required args.""" + surface = pygame.Surface((1, 1)) + color = pygame.Color("red") + rect = pygame.Rect((0, 0), (2, 2)) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(surface, color, rect, 0.1) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(surface, color, rect) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(surface, color) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc() + + def test_arc__kwargs_missing(self): + """Ensures draw arc detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((1, 2)), + "color": pygame.Color("red"), + "rect": pygame.Rect((1, 0), (2, 2)), + "start_angle": 0.1, + "stop_angle": 2, + "width": 1, + } + + for name in ("stop_angle", "start_angle", "rect", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(**invalid_kwargs) + + def test_arc__arg_invalid_types(self): + """Ensures draw arc detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + rect = pygame.Rect((1, 1), (3, 3)) + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_arc(surface, color, rect, 0, 1, "1") + + with self.assertRaises(TypeError): + # Invalid stop_angle. + bounds_rect = self.draw_arc(surface, color, rect, 0, "1", 1) + + with self.assertRaises(TypeError): + # Invalid start_angle. + bounds_rect = self.draw_arc(surface, color, rect, "1", 0, 1) + + with self.assertRaises(TypeError): + # Invalid rect. + bounds_rect = self.draw_arc(surface, color, (1, 2, 3, 4, 5), 0, 1, 1) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_arc(surface, 2.3, rect, 0, 1, 1) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_arc(rect, color, rect, 0, 1, 1) + + def test_arc__kwarg_invalid_types(self): + """Ensures draw arc detects invalid kwarg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("green") + rect = pygame.Rect((0, 1), (4, 2)) + start = 3 + stop = 4 + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "rect": rect, + "start_angle": start, + "stop_angle": stop, + "width": 1, + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "rect": rect, + "start_angle": start, + "stop_angle": stop, + "width": 1, + }, + { + "surface": surface, + "color": color, + "rect": (0, 0, 0), # Invalid rect. + "start_angle": start, + "stop_angle": stop, + "width": 1, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "start_angle": "1", # Invalid start_angle. + "stop_angle": stop, + "width": 1, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "start_angle": start, + "stop_angle": "1", # Invalid stop_angle. + "width": 1, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "start_angle": start, + "stop_angle": stop, + "width": 1.1, + }, + ] # Invalid width. + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(**kwargs) + + def test_arc__kwarg_invalid_name(self): + """Ensures draw arc detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + rect = pygame.Rect((0, 1), (2, 2)) + start = 0.9 + stop = 2.3 + kwargs_list = [ + { + "surface": surface, + "color": color, + "rect": rect, + "start_angle": start, + "stop_angle": stop, + "width": 1, + "invalid": 1, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "start_angle": start, + "stop_angle": stop, + "invalid": 1, + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(**kwargs) + + def test_arc__args_and_kwargs(self): + """Ensures draw arc accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 1)) + color = (255, 255, 0, 0) + rect = pygame.Rect((1, 0), (2, 3)) + start = 0.6 + stop = 2 + width = 1 + kwargs = { + "surface": surface, + "color": color, + "rect": rect, + "start_angle": start, + "stop_angle": stop, + "width": width, + } + + for name in ("surface", "color", "rect", "start_angle", "stop_angle"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_arc(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_arc(surface, color, **kwargs) + elif "rect" == name: + bounds_rect = self.draw_arc(surface, color, rect, **kwargs) + elif "start_angle" == name: + bounds_rect = self.draw_arc(surface, color, rect, start, **kwargs) + elif "stop_angle" == name: + bounds_rect = self.draw_arc(surface, color, rect, start, stop, **kwargs) + else: + bounds_rect = self.draw_arc( + surface, color, rect, start, stop, width, **kwargs + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__valid_width_values(self): + """Ensures draw arc accepts different width values.""" + arc_color = pygame.Color("yellow") + surface_color = pygame.Color("white") + surface = pygame.Surface((6, 6)) + rect = pygame.Rect((0, 0), (4, 4)) + rect.center = surface.get_rect().center + pos = rect.centerx + 1, rect.centery + 1 + kwargs = { + "surface": surface, + "color": arc_color, + "rect": rect, + "start_angle": 0, + "stop_angle": 7, + "width": None, + } + + for width in (-50, -10, -3, -2, -1, 0, 1, 2, 3, 10, 50): + msg = f"width={width}" + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = arc_color if width > 0 else surface_color + + bounds_rect = self.draw_arc(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color, msg) + self.assertIsInstance(bounds_rect, pygame.Rect, msg) + + def test_arc__valid_stop_angle_values(self): + """Ensures draw arc accepts different stop_angle values.""" + expected_color = pygame.Color("blue") + surface_color = pygame.Color("white") + surface = pygame.Surface((6, 6)) + rect = pygame.Rect((0, 0), (4, 4)) + rect.center = surface.get_rect().center + pos = rect.centerx, rect.centery + 1 + kwargs = { + "surface": surface, + "color": expected_color, + "rect": rect, + "start_angle": -17, + "stop_angle": None, + "width": 1, + } + + for stop_angle in (-10, -5.5, -1, 0, 1, 5.5, 10): + msg = f"stop_angle={stop_angle}" + surface.fill(surface_color) # Clear for each test. + kwargs["stop_angle"] = stop_angle + + bounds_rect = self.draw_arc(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color, msg) + self.assertIsInstance(bounds_rect, pygame.Rect, msg) + + def test_arc__valid_start_angle_values(self): + """Ensures draw arc accepts different start_angle values.""" + expected_color = pygame.Color("blue") + surface_color = pygame.Color("white") + surface = pygame.Surface((6, 6)) + rect = pygame.Rect((0, 0), (4, 4)) + rect.center = surface.get_rect().center + pos = rect.centerx + 1, rect.centery + 1 + kwargs = { + "surface": surface, + "color": expected_color, + "rect": rect, + "start_angle": None, + "stop_angle": 17, + "width": 1, + } + + for start_angle in (-10.0, -5.5, -1, 0, 1, 5.5, 10.0): + msg = f"start_angle={start_angle}" + surface.fill(surface_color) # Clear for each test. + kwargs["start_angle"] = start_angle + + bounds_rect = self.draw_arc(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color, msg) + self.assertIsInstance(bounds_rect, pygame.Rect, msg) + + def test_arc__valid_rect_formats(self): + """Ensures draw arc accepts different rect formats.""" + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((6, 6)) + rect = pygame.Rect((0, 0), (4, 4)) + rect.center = surface.get_rect().center + pos = rect.centerx + 1, rect.centery + 1 + kwargs = { + "surface": surface, + "color": expected_color, + "rect": None, + "start_angle": 0, + "stop_angle": 7, + "width": 1, + } + rects = (rect, (rect.topleft, rect.size), (rect.x, rect.y, rect.w, rect.h)) + + for rect in rects: + surface.fill(surface_color) # Clear for each test. + kwargs["rect"] = rect + + bounds_rect = self.draw_arc(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__valid_color_formats(self): + """Ensures draw arc accepts different color formats.""" + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((6, 6)) + rect = pygame.Rect((0, 0), (4, 4)) + rect.center = surface.get_rect().center + pos = rect.centerx + 1, rect.centery + 1 + kwargs = { + "surface": surface, + "color": None, + "rect": rect, + "start_angle": 0, + "stop_angle": 7, + "width": 1, + } + greens = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in greens: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_arc(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__invalid_color_formats(self): + """Ensures draw arc handles invalid color formats correctly.""" + pos = (1, 1) + surface = pygame.Surface((4, 3)) + kwargs = { + "surface": surface, + "color": None, + "rect": pygame.Rect(pos, (2, 2)), + "start_angle": 5, + "stop_angle": 6.1, + "width": 1, + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(**kwargs) + + def test_arc(self): + """Ensure draw arc works correctly.""" + black = pygame.Color("black") + red = pygame.Color("red") + + # create an image object of width 100, height 150, filled with black. + surface = pygame.Surface((100, 150)) + surface.fill(black) + + # rectangle that contains for the ellipse arc. + # 0 pixel from left, 0 pixel from top + # 80 pixels wide, 40 pixels high + rect = (0, 0, 80, 40) + + # angle of the arc in radians + start_angle = 0.0 + stop_angle = 3.14 + + # thickness, and it grows inward from the rectangle + width = 3 + + # draw an elliptical arc + pygame.draw.arc(surface, red, rect, start_angle, stop_angle, width) + + # Save the drawn arc + pygame.image.save(surface, "arc.png") + + # arc is red + x = 20 + for y in range(2, 5): + self.assertEqual(surface.get_at((x, y)), red) + + # the rest area in surface is black + self.assertEqual(surface.get_at((0, 0)), black) + + def test_arc__bounding_rect(self): + """Ensures draw arc returns the correct bounding rect. + + Tests arcs on and off the surface and a range of width/thickness + values. + """ + arc_color = pygame.Color("red") + surf_color = pygame.Color("black") + min_width = min_height = 5 + max_width = max_height = 7 + sizes = ((min_width, min_height), (max_width, max_height)) + surface = pygame.Surface((20, 20), 0, 32) + surf_rect = surface.get_rect() + # Make a rect that is bigger than the surface to help test drawing + # arcs off and partially off the surface. + big_rect = surf_rect.inflate(min_width * 2 + 1, min_height * 2 + 1) + + # Max angle allows for a full circle to be drawn. + start_angle = 0 + stop_angles = (0, 2, 3, 5, math.ceil(2 * math.pi)) + + for pos in rect_corners_mids_and_center( + surf_rect + ) + rect_corners_mids_and_center(big_rect): + # Each of the arc's rect position attributes will be set to the pos + # value. + for attr in RECT_POSITION_ATTRIBUTES: + # Test using different rect sizes, thickness values and stop + # angles. + for width, height in sizes: + arc_rect = pygame.Rect((0, 0), (width, height)) + setattr(arc_rect, attr, pos) + + for thickness in (0, 1, 2, 3, min(width, height)): + for stop_angle in stop_angles: + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_arc( + surface, + arc_color, + arc_rect, + start_angle, + stop_angle, + thickness, + ) + + # Calculating the expected_rect after the arc + # is drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect( + surface, surf_color, arc_rect.topleft + ) + + self.assertEqual( + bounding_rect, + expected_rect, + f"thickness={thickness}", + ) + + def test_arc__surface_clip(self): + """Ensures draw arc respects a surface's clip area.""" + surfw = surfh = 30 + start = 0.1 + end = 0 # end < start so a full circle will be drawn + arc_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (11, 11)) + clip_rect.center = surface.get_rect().center + pos_rect = clip_rect.copy() # Manages the arc's pos. + + for thickness in (1, 3): # Different line widths. + # Test centering the arc along the clip rect's edge. + for center in rect_corners_mids_and_center(clip_rect): + # Get the expected points by drawing the arc without the + # clip area set. + pos_rect.center = center + surface.set_clip(None) + surface.fill(surface_color) + self.draw_arc(surface, arc_color, pos_rect, start, end, thickness) + expected_pts = get_color_points(surface, arc_color, clip_rect) + + # Clear the surface and set the clip area. Redraw the arc + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_arc(surface, arc_color, pos_rect, start, end, thickness) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure only the expected_pts + # are the arc_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + expected_color = arc_color + else: + expected_color = surface_color + + self.assertEqual(surface.get_at(pt), expected_color, pt) + + surface.unlock() + + +class DrawArcTest(DrawArcMixin, DrawTestCase): + """Test draw module function arc. + + This class inherits the general tests from DrawArcMixin. It is also the + class to add any draw.arc specific tests to. + """ + + +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever properly supports drawing arcs. +# @unittest.skip('draw_py.draw_arc not supported yet') +# class PythonDrawArcTest(DrawArcMixin, PythonDrawTestCase): +# """Test draw_py module function draw_arc. +# +# This class inherits the general tests from DrawArcMixin. It is also the +# class to add any draw_py.draw_arc specific tests to. +# """ + + +### Draw Module Testing ####################################################### + + +class DrawModuleTest(unittest.TestCase): + """General draw module tests.""" + + def test_path_data_validation(self): + """Test validation of multi-point drawing methods. + + See bug #521 + """ + surf = pygame.Surface((5, 5)) + rect = pygame.Rect(0, 0, 5, 5) + bad_values = ( + "text", + b"bytes", + 1 + 1j, # string, bytes, complex, + object(), + (lambda x: x), + ) # object, function + bad_points = list(bad_values) + [(1,), (1, 2, 3)] # wrong tuple length + bad_points.extend((1, v) for v in bad_values) # one wrong value + good_path = [(1, 1), (1, 3), (3, 3), (3, 1)] + # A) draw.lines + check_pts = [(x, y) for x in range(5) for y in range(5)] + + for method, is_polgon in ( + (draw.lines, 0), + (draw.aalines, 0), + (draw.polygon, 1), + ): + for val in bad_values: + # 1. at the beginning + draw.rect(surf, RED, rect, 0) + with self.assertRaises(TypeError): + if is_polgon: + method(surf, GREEN, [val] + good_path, 0) + else: + method(surf, GREEN, True, [val] + good_path) + + # make sure, nothing was drawn : + self.assertTrue(all(surf.get_at(pt) == RED for pt in check_pts)) + + # 2. not at the beginning (was not checked) + draw.rect(surf, RED, rect, 0) + with self.assertRaises(TypeError): + path = good_path[:2] + [val] + good_path[2:] + if is_polgon: + method(surf, GREEN, path, 0) + else: + method(surf, GREEN, True, path) + + # make sure, nothing was drawn : + self.assertTrue(all(surf.get_at(pt) == RED for pt in check_pts)) + + def test_color_validation(self): + surf = pygame.Surface((10, 10)) + colors = 123456, (1, 10, 100), RED, "#ab12df", "red" + points = ((0, 0), (1, 1), (1, 0)) + + # 1. valid colors + for col in colors: + draw.line(surf, col, (0, 0), (1, 1)) + draw.aaline(surf, col, (0, 0), (1, 1)) + draw.aalines(surf, col, True, points) + draw.lines(surf, col, True, points) + draw.arc(surf, col, pygame.Rect(0, 0, 3, 3), 15, 150) + draw.ellipse(surf, col, pygame.Rect(0, 0, 3, 6), 1) + draw.circle(surf, col, (7, 3), 2) + draw.polygon(surf, col, points, 0) + + # 2. invalid colors + for col in (1.256, object(), None): + with self.assertRaises(TypeError): + draw.line(surf, col, (0, 0), (1, 1)) + + with self.assertRaises(TypeError): + draw.aaline(surf, col, (0, 0), (1, 1)) + + with self.assertRaises(TypeError): + draw.aalines(surf, col, True, points) + + with self.assertRaises(TypeError): + draw.lines(surf, col, True, points) + + with self.assertRaises(TypeError): + draw.arc(surf, col, pygame.Rect(0, 0, 3, 3), 15, 150) + + with self.assertRaises(TypeError): + draw.ellipse(surf, col, pygame.Rect(0, 0, 3, 6), 1) + + with self.assertRaises(TypeError): + draw.circle(surf, col, (7, 3), 2) + + with self.assertRaises(TypeError): + draw.polygon(surf, col, points, 0) + + +############################################################################### + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/event_test.py b/.venv/Lib/site-packages/pygame/tests/event_test.py new file mode 100644 index 00000000..896498a4 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/event_test.py @@ -0,0 +1,949 @@ +import collections +import time +import unittest +import os + +import pygame + +EVENT_TYPES = ( + # pygame.NOEVENT, + # pygame.ACTIVEEVENT, + pygame.KEYDOWN, + pygame.KEYUP, + pygame.MOUSEMOTION, + pygame.MOUSEBUTTONDOWN, + pygame.MOUSEBUTTONUP, + pygame.JOYAXISMOTION, + pygame.JOYBALLMOTION, + pygame.JOYHATMOTION, + pygame.JOYBUTTONDOWN, + pygame.JOYBUTTONUP, + pygame.VIDEORESIZE, + pygame.VIDEOEXPOSE, + pygame.QUIT, + pygame.SYSWMEVENT, + pygame.USEREVENT, + # pygame.NUMEVENTS, +) + +EVENT_TEST_PARAMS = collections.defaultdict(dict) +EVENT_TEST_PARAMS.update( + { + pygame.KEYDOWN: {"key": pygame.K_SPACE}, + pygame.KEYUP: {"key": pygame.K_SPACE}, + pygame.MOUSEMOTION: dict(), + pygame.MOUSEBUTTONDOWN: dict(button=1), + pygame.MOUSEBUTTONUP: dict(button=1), + } +) + + +NAMES_AND_EVENTS = ( + ("NoEvent", pygame.NOEVENT), + ("ActiveEvent", pygame.ACTIVEEVENT), + ("KeyDown", pygame.KEYDOWN), + ("KeyUp", pygame.KEYUP), + ("MouseMotion", pygame.MOUSEMOTION), + ("MouseButtonDown", pygame.MOUSEBUTTONDOWN), + ("MouseButtonUp", pygame.MOUSEBUTTONUP), + ("JoyAxisMotion", pygame.JOYAXISMOTION), + ("JoyBallMotion", pygame.JOYBALLMOTION), + ("JoyHatMotion", pygame.JOYHATMOTION), + ("JoyButtonDown", pygame.JOYBUTTONDOWN), + ("JoyButtonUp", pygame.JOYBUTTONUP), + ("VideoResize", pygame.VIDEORESIZE), + ("VideoExpose", pygame.VIDEOEXPOSE), + ("Quit", pygame.QUIT), + ("SysWMEvent", pygame.SYSWMEVENT), + ("MidiIn", pygame.MIDIIN), + ("MidiOut", pygame.MIDIOUT), + ("UserEvent", pygame.USEREVENT), + ("Unknown", 0xFFFF), + ("FingerMotion", pygame.FINGERMOTION), + ("FingerDown", pygame.FINGERDOWN), + ("FingerUp", pygame.FINGERUP), + ("MultiGesture", pygame.MULTIGESTURE), + ("MouseWheel", pygame.MOUSEWHEEL), + ("TextInput", pygame.TEXTINPUT), + ("TextEditing", pygame.TEXTEDITING), + ("ControllerAxisMotion", pygame.CONTROLLERAXISMOTION), + ("ControllerButtonDown", pygame.CONTROLLERBUTTONDOWN), + ("ControllerButtonUp", pygame.CONTROLLERBUTTONUP), + ("ControllerDeviceAdded", pygame.CONTROLLERDEVICEADDED), + ("ControllerDeviceRemoved", pygame.CONTROLLERDEVICEREMOVED), + ("ControllerDeviceMapped", pygame.CONTROLLERDEVICEREMAPPED), + ("DropFile", pygame.DROPFILE), + ("AudioDeviceAdded", pygame.AUDIODEVICEADDED), + ("AudioDeviceRemoved", pygame.AUDIODEVICEREMOVED), + ("DropText", pygame.DROPTEXT), + ("DropBegin", pygame.DROPBEGIN), + ("DropComplete", pygame.DROPCOMPLETE), +) + + +class EventTypeTest(unittest.TestCase): + def test_Event(self): + """Ensure an Event object can be created.""" + e = pygame.event.Event(pygame.USEREVENT, some_attr=1, other_attr="1") + + self.assertEqual(e.some_attr, 1) + self.assertEqual(e.other_attr, "1") + + # Event now uses tp_dictoffset and tp_members: + # https://github.com/pygame/pygame/issues/62 + self.assertEqual(e.type, pygame.USEREVENT) + self.assertIs(e.dict, e.__dict__) + + e.some_attr = 12 + + self.assertEqual(e.some_attr, 12) + + e.new_attr = 15 + + self.assertEqual(e.new_attr, 15) + + self.assertRaises(AttributeError, setattr, e, "type", 0) + self.assertRaises(AttributeError, setattr, e, "dict", None) + + # Ensure attributes are visible to dir(), part of the original + # posted request. + d = dir(e) + attrs = ("type", "dict", "__dict__", "some_attr", "other_attr", "new_attr") + + for attr in attrs: + self.assertIn(attr, d) + + # redundant type field as kwarg + self.assertRaises(ValueError, pygame.event.Event, 10, type=100) + + def test_as_str(self): + # Bug reported on Pygame mailing list July 24, 2011: + # For Python 3.x str(event) to raises an UnicodeEncodeError when + # an event attribute is a string with a non-ascii character. + try: + str(pygame.event.Event(EVENT_TYPES[0], a="\xed")) + except UnicodeEncodeError: + self.fail("Event object raised exception for non-ascii character") + # Passed. + + def test_event_bool(self): + self.assertFalse(pygame.event.Event(pygame.NOEVENT)) + for event_type in [ + pygame.MOUSEBUTTONDOWN, + pygame.ACTIVEEVENT, + pygame.WINDOWLEAVE, + pygame.USEREVENT_DROPFILE, + ]: + self.assertTrue(pygame.event.Event(event_type)) + + def test_event_equality(self): + """Ensure that events can be compared correctly.""" + a = pygame.event.Event(EVENT_TYPES[0], a=1) + b = pygame.event.Event(EVENT_TYPES[0], a=1) + c = pygame.event.Event(EVENT_TYPES[1], a=1) + d = pygame.event.Event(EVENT_TYPES[0], a=2) + + self.assertTrue(a == a) + self.assertFalse(a != a) + self.assertTrue(a == b) + self.assertFalse(a != b) + self.assertTrue(a != c) + self.assertFalse(a == c) + self.assertTrue(a != d) + self.assertFalse(a == d) + + +race_condition_notification = """ +This test is dependent on timing. The event queue is cleared in preparation for +tests. There is a small window where outside events from the OS may have effected +results. Try running the test again. +""" + + +class EventModuleArgsTest(unittest.TestCase): + def setUp(self): + pygame.display.init() + pygame.event.clear() + + def tearDown(self): + pygame.display.quit() + + def test_get(self): + pygame.event.get() + pygame.event.get(None) + pygame.event.get(None, True) + + pygame.event.get(pump=False) + pygame.event.get(pump=True) + pygame.event.get(eventtype=None) + pygame.event.get(eventtype=[pygame.KEYUP, pygame.KEYDOWN]) + pygame.event.get(eventtype=pygame.USEREVENT, pump=False) + + # event type out of range + self.assertRaises(ValueError, pygame.event.get, 0x00010000) + self.assertRaises(TypeError, pygame.event.get, 1 + 2j) + self.assertRaises(TypeError, pygame.event.get, "foo") + + def test_clear(self): + pygame.event.clear() + pygame.event.clear(None) + pygame.event.clear(None, True) + + pygame.event.clear(pump=False) + pygame.event.clear(pump=True) + pygame.event.clear(eventtype=None) + pygame.event.clear(eventtype=[pygame.KEYUP, pygame.KEYDOWN]) + pygame.event.clear(eventtype=pygame.USEREVENT, pump=False) + + # event type out of range + self.assertRaises(ValueError, pygame.event.clear, 0x0010FFFFF) + self.assertRaises(TypeError, pygame.event.get, ["a", "b", "c"]) + + def test_peek(self): + pygame.event.peek() + pygame.event.peek(None) + pygame.event.peek(None, True) + + pygame.event.peek(pump=False) + pygame.event.peek(pump=True) + pygame.event.peek(eventtype=None) + pygame.event.peek(eventtype=[pygame.KEYUP, pygame.KEYDOWN]) + pygame.event.peek(eventtype=pygame.USEREVENT, pump=False) + + class Foo: + pass + + # event type out of range + self.assertRaises(ValueError, pygame.event.peek, -1) + self.assertRaises(ValueError, pygame.event.peek, [-10]) + self.assertRaises(TypeError, pygame.event.peek, Foo()) + + +class EventCustomTypeTest(unittest.TestCase): + """Those tests are special in that they need the _custom_event counter to + be reset before and/or after being run.""" + + def setUp(self): + pygame.quit() + pygame.init() + pygame.display.init() + + def tearDown(self): + pygame.quit() + + def test_custom_type(self): + self.assertEqual(pygame.event.custom_type(), pygame.USEREVENT + 1) + atype = pygame.event.custom_type() + atype2 = pygame.event.custom_type() + + self.assertEqual(atype, atype2 - 1) + + ev = pygame.event.Event(atype) + pygame.event.post(ev) + queue = pygame.event.get(atype) + self.assertEqual(len(queue), 1) + self.assertEqual(queue[0].type, atype) + + def test_custom_type__end_boundary(self): + """Ensure custom_type() raises error when no more custom types. + + The last allowed custom type number should be (pygame.NUMEVENTS - 1). + """ + last = -1 + start = pygame.event.custom_type() + 1 + for _ in range(start, pygame.NUMEVENTS): + last = pygame.event.custom_type() + + self.assertEqual(last, pygame.NUMEVENTS - 1) + with self.assertRaises(pygame.error): + pygame.event.custom_type() + + def test_custom_type__reset(self): + """Ensure custom events get 'deregistered' by quit().""" + before = pygame.event.custom_type() + self.assertEqual(before, pygame.event.custom_type() - 1) + pygame.quit() + pygame.init() + pygame.display.init() + self.assertEqual(before, pygame.event.custom_type()) + + +class EventModuleTest(unittest.TestCase): + def _assertCountEqual(self, *args, **kwargs): + # Handle method name differences between Python versions. + # Is this still needed? + self.assertCountEqual(*args, **kwargs) + + def _assertExpectedEvents(self, expected, got): + """Find events like expected events, raise on unexpected or missing, + ignore additional event properties if expected properties are present.""" + + # This does greedy matching, don't encode an NP-hard problem + # into your input data, *please* + items_left = got[:] + for expected_element in expected: + for item in items_left: + for key in expected_element.__dict__: + if item.__dict__[key] != expected_element.__dict__[key]: + break + else: + # found item! + items_left.remove(item) + break + else: + raise AssertionError( + "Expected " + + str(expected_element) + + " among remaining events " + + str(items_left) + + " out of " + + str(got) + ) + if len(items_left) > 0: + raise AssertionError("Unexpected Events: " + str(items_left)) + + def setUp(self): + pygame.display.init() + pygame.event.clear() # flush events + + def tearDown(self): + pygame.event.clear() # flush events + pygame.display.quit() + + def test_event_numevents(self): + """Ensures NUMEVENTS does not exceed the maximum SDL number of events.""" + # Ref: https://www.libsdl.org/tmp/SDL/include/SDL_events.h + MAX_SDL_EVENTS = 0xFFFF # SDL_LASTEVENT = 0xFFFF + + self.assertLessEqual(pygame.NUMEVENTS, MAX_SDL_EVENTS) + + def test_event_attribute(self): + e1 = pygame.event.Event(pygame.USEREVENT, attr1="attr1") + self.assertEqual(e1.attr1, "attr1") + + def test_set_blocked(self): + """Ensure events can be blocked from the queue.""" + event = EVENT_TYPES[0] + unblocked_event = EVENT_TYPES[1] + pygame.event.set_blocked(event) + + self.assertTrue(pygame.event.get_blocked(event)) + self.assertFalse(pygame.event.get_blocked(unblocked_event)) + + posted = pygame.event.post( + pygame.event.Event(event, **EVENT_TEST_PARAMS[event]) + ) + self.assertFalse(posted) + + # post an unblocked event + posted = pygame.event.post( + pygame.event.Event(unblocked_event, **EVENT_TEST_PARAMS[unblocked_event]) + ) + self.assertTrue(posted) + + ret = pygame.event.get() + should_be_blocked = [e for e in ret if e.type == event] + should_be_allowed_types = [e.type for e in ret if e.type != event] + + self.assertEqual(should_be_blocked, []) + self.assertTrue(unblocked_event in should_be_allowed_types) + + def test_set_blocked__event_sequence(self): + """Ensure a sequence of event types can be blocked.""" + event_types = [ + pygame.KEYDOWN, + pygame.KEYUP, + pygame.MOUSEMOTION, + pygame.MOUSEBUTTONDOWN, + pygame.MOUSEBUTTONUP, + pygame.WINDOWFOCUSLOST, + pygame.USEREVENT, + ] + + pygame.event.set_blocked(event_types) + + for etype in event_types: + self.assertTrue(pygame.event.get_blocked(etype)) + + def test_set_blocked_all(self): + """Ensure all events can be unblocked at once.""" + pygame.event.set_blocked(None) + + for e in EVENT_TYPES: + self.assertTrue(pygame.event.get_blocked(e)) + + def test_post__and_poll(self): + """Ensure events can be posted to the queue.""" + e1 = pygame.event.Event(pygame.USEREVENT, attr1="attr1") + pygame.event.post(e1) + posted_event = pygame.event.poll() + + self.assertEqual(e1.attr1, posted_event.attr1, race_condition_notification) + + # fuzzing event types + for i in range(1, 13): + pygame.event.post( + pygame.event.Event(EVENT_TYPES[i], **EVENT_TEST_PARAMS[EVENT_TYPES[i]]) + ) + + self.assertEqual( + pygame.event.poll().type, EVENT_TYPES[i], race_condition_notification + ) + + def test_post_and_get_keydown(self): + """Ensure keydown events can be posted to the queue.""" + activemodkeys = pygame.key.get_mods() + + events = [ + pygame.event.Event(pygame.KEYDOWN, key=pygame.K_p), + pygame.event.Event(pygame.KEYDOWN, key=pygame.K_y, mod=activemodkeys), + pygame.event.Event(pygame.KEYDOWN, key=pygame.K_g, unicode="g"), + pygame.event.Event(pygame.KEYDOWN, key=pygame.K_a, unicode=None), + pygame.event.Event(pygame.KEYDOWN, key=pygame.K_m, mod=None, window=None), + pygame.event.Event( + pygame.KEYDOWN, key=pygame.K_e, mod=activemodkeys, unicode="e" + ), + ] + + for e in events: + pygame.event.post(e) + posted_event = pygame.event.poll() + self.assertEqual(e, posted_event, race_condition_notification) + + def test_post_large_user_event(self): + pygame.event.post( + pygame.event.Event( + pygame.USEREVENT, {"a": "a" * 1024}, test=list(range(100)) + ) + ) + e = pygame.event.poll() + + self.assertEqual(e.type, pygame.USEREVENT) + self.assertEqual(e.a, "a" * 1024) + self.assertEqual(e.test, list(range(100))) + + def test_post_blocked(self): + """ + Test blocked events are not posted. Also test whether post() + returns a boolean correctly + """ + pygame.event.set_blocked(pygame.USEREVENT) + self.assertFalse(pygame.event.post(pygame.event.Event(pygame.USEREVENT))) + self.assertFalse(pygame.event.poll()) + pygame.event.set_allowed(pygame.USEREVENT) + self.assertTrue(pygame.event.post(pygame.event.Event(pygame.USEREVENT))) + self.assertEqual(pygame.event.poll(), pygame.event.Event(pygame.USEREVENT)) + + def test_get(self): + """Ensure get() retrieves all the events on the queue.""" + event_cnt = 10 + for _ in range(event_cnt): + pygame.event.post(pygame.event.Event(pygame.USEREVENT)) + + queue = pygame.event.get() + + self.assertEqual(len(queue), event_cnt) + self.assertTrue(all(e.type == pygame.USEREVENT for e in queue)) + + def test_get_type(self): + ev = pygame.event.Event(pygame.USEREVENT) + pygame.event.post(ev) + queue = pygame.event.get(pygame.USEREVENT) + self.assertEqual(len(queue), 1) + self.assertEqual(queue[0].type, pygame.USEREVENT) + + TESTEVENTS = 10 + for _ in range(TESTEVENTS): + pygame.event.post(ev) + q = pygame.event.get([pygame.USEREVENT]) + self.assertEqual(len(q), TESTEVENTS) + for event in q: + self.assertEqual(event, ev) + + def test_get_exclude_throw(self): + self.assertRaises( + pygame.error, pygame.event.get, pygame.KEYDOWN, False, pygame.KEYUP + ) + + def test_get_exclude(self): + pygame.event.post(pygame.event.Event(pygame.USEREVENT)) + pygame.event.post(pygame.event.Event(pygame.KEYDOWN)) + + queue = pygame.event.get(exclude=pygame.KEYDOWN) + self.assertEqual(len(queue), 1) + self.assertEqual(queue[0].type, pygame.USEREVENT) + + pygame.event.post(pygame.event.Event(pygame.KEYUP)) + pygame.event.post(pygame.event.Event(pygame.USEREVENT)) + queue = pygame.event.get(exclude=(pygame.KEYDOWN, pygame.KEYUP)) + self.assertEqual(len(queue), 1) + self.assertEqual(queue[0].type, pygame.USEREVENT) + + queue = pygame.event.get() + self.assertEqual(len(queue), 2) + + def test_get__empty_queue(self): + """Ensure get() works correctly on an empty queue.""" + expected_events = [] + pygame.event.clear() + + # Ensure all events can be checked. + retrieved_events = pygame.event.get() + + self.assertListEqual(retrieved_events, expected_events) + + # Ensure events can be checked individually. + for event_type in EVENT_TYPES: + retrieved_events = pygame.event.get(event_type) + + self.assertListEqual(retrieved_events, expected_events) + + # Ensure events can be checked as a sequence. + retrieved_events = pygame.event.get(EVENT_TYPES) + + self.assertListEqual(retrieved_events, expected_events) + + def test_get__event_sequence(self): + """Ensure get() can handle a sequence of event types.""" + event_types = [pygame.KEYDOWN, pygame.KEYUP, pygame.MOUSEMOTION] + other_event_type = pygame.MOUSEBUTTONUP + + # Test when no events in the queue. + expected_events = [] + pygame.event.clear() + retrieved_events = pygame.event.get(event_types) + + # don't use self._assertCountEqual here. This checks for + # expected properties in events, and ignores unexpected ones, for + # forward compatibility with SDL2. + self._assertExpectedEvents(expected=expected_events, got=retrieved_events) + + # Test when an event type not in the list is in the queue. + expected_events = [] + pygame.event.clear() + pygame.event.post( + pygame.event.Event(other_event_type, **EVENT_TEST_PARAMS[other_event_type]) + ) + + retrieved_events = pygame.event.get(event_types) + + self._assertExpectedEvents(expected=expected_events, got=retrieved_events) + + # Test when 1 event type in the list is in the queue. + expected_events = [ + pygame.event.Event(event_types[0], **EVENT_TEST_PARAMS[event_types[0]]) + ] + pygame.event.clear() + pygame.event.post(expected_events[0]) + + retrieved_events = pygame.event.get(event_types) + + self._assertExpectedEvents(expected=expected_events, got=retrieved_events) + + # Test all events in the list are in the queue. + pygame.event.clear() + expected_events = [] + + for etype in event_types: + expected_events.append( + pygame.event.Event(etype, **EVENT_TEST_PARAMS[etype]) + ) + pygame.event.post(expected_events[-1]) + + retrieved_events = pygame.event.get(event_types) + + self._assertExpectedEvents(expected=expected_events, got=retrieved_events) + + def test_get_clears_queue(self): + """Ensure get() clears the event queue after a call""" + pygame.event.get() # should clear the queue completely by getting all events + self.assertEqual(pygame.event.get(), []) + + def test_clear(self): + """Ensure clear() removes all the events on the queue.""" + for e in EVENT_TYPES: + pygame.event.post(pygame.event.Event(e, **EVENT_TEST_PARAMS[e])) + poll_event = pygame.event.poll() + + self.assertNotEqual(poll_event.type, pygame.NOEVENT) + + pygame.event.clear() + poll_event = pygame.event.poll() + + self.assertEqual(poll_event.type, pygame.NOEVENT, race_condition_notification) + + def test_clear__empty_queue(self): + """Ensure clear() works correctly on an empty queue.""" + expected_events = [] + pygame.event.clear() + + # Test calling clear() on an already empty queue. + pygame.event.clear() + + retrieved_events = pygame.event.get() + + self.assertListEqual(retrieved_events, expected_events) + + def test_clear__event_sequence(self): + """Ensure a sequence of event types can be cleared from the queue.""" + cleared_event_types = EVENT_TYPES[:5] + expected_event_types = EVENT_TYPES[5:10] + expected_events = [] + + # Add the events to the queue. + for etype in cleared_event_types: + pygame.event.post(pygame.event.Event(etype, **EVENT_TEST_PARAMS[etype])) + + for etype in expected_events: + expected_events.append( + pygame.event.Event(etype, **EVENT_TEST_PARAMS[etype]) + ) + pygame.event.post(expected_events[-1]) + + # Clear the cleared_events from the queue. + pygame.event.clear(cleared_event_types) + + # Check the rest of the events in the queue. + remaining_events = pygame.event.get() + + self._assertCountEqual(remaining_events, expected_events) + + def test_event_name(self): + """Ensure event_name() returns the correct event name.""" + for expected_name, event in NAMES_AND_EVENTS: + self.assertEqual( + pygame.event.event_name(event), expected_name, f"0x{event:X}" + ) + + def test_event_name__userevent_range(self): + """Ensures event_name() returns the correct name for user events. + + Tests the full range of user events. + """ + expected_name = "UserEvent" + + for event in range(pygame.USEREVENT, pygame.NUMEVENTS): + self.assertEqual( + pygame.event.event_name(event), expected_name, f"0x{event:X}" + ) + + def test_event_name__userevent_boundary(self): + """Ensures event_name() does not return 'UserEvent' for events + just outside the user event range. + """ + unexpected_name = "UserEvent" + + for event in (pygame.USEREVENT - 1, pygame.NUMEVENTS): + self.assertNotEqual( + pygame.event.event_name(event), unexpected_name, f"0x{event:X}" + ) + + def test_event_name__kwargs(self): + """Ensure event_name() returns the correct event name when kwargs used.""" + for expected_name, event in NAMES_AND_EVENTS: + self.assertEqual( + pygame.event.event_name(type=event), expected_name, f"0x{event:X}" + ) + + def test_peek(self): + """Ensure queued events can be peeked at.""" + event_types = [pygame.KEYDOWN, pygame.KEYUP, pygame.MOUSEMOTION] + + for event_type in event_types: + pygame.event.post( + pygame.event.Event(event_type, **EVENT_TEST_PARAMS[event_type]) + ) + + # Ensure events can be checked individually. + for event_type in event_types: + self.assertTrue(pygame.event.peek(event_type)) + + # Ensure events can be checked as a sequence. + self.assertTrue(pygame.event.peek(event_types)) + + def test_peek__event_sequence(self): + """Ensure peek() can handle a sequence of event types.""" + event_types = [pygame.KEYDOWN, pygame.KEYUP, pygame.MOUSEMOTION] + other_event_type = pygame.MOUSEBUTTONUP + + # Test when no events in the queue. + pygame.event.clear() + peeked = pygame.event.peek(event_types) + + self.assertFalse(peeked) + + # Test when an event type not in the list is in the queue. + pygame.event.clear() + pygame.event.post( + pygame.event.Event(other_event_type, **EVENT_TEST_PARAMS[other_event_type]) + ) + + peeked = pygame.event.peek(event_types) + + self.assertFalse(peeked) + + # Test when 1 event type in the list is in the queue. + pygame.event.clear() + pygame.event.post( + pygame.event.Event(event_types[0], **EVENT_TEST_PARAMS[event_types[0]]) + ) + + peeked = pygame.event.peek(event_types) + + self.assertTrue(peeked) + + # Test all events in the list are in the queue. + pygame.event.clear() + for etype in event_types: + pygame.event.post(pygame.event.Event(etype, **EVENT_TEST_PARAMS[etype])) + + peeked = pygame.event.peek(event_types) + + self.assertTrue(peeked) + + def test_peek__empty_queue(self): + """Ensure peek() works correctly on an empty queue.""" + pygame.event.clear() + + # Ensure all events can be checked. + peeked = pygame.event.peek() + + self.assertFalse(peeked) + + # Ensure events can be checked individually. + for event_type in EVENT_TYPES: + peeked = pygame.event.peek(event_type) + self.assertFalse(peeked) + + # Ensure events can be checked as a sequence. + peeked = pygame.event.peek(EVENT_TYPES) + + self.assertFalse(peeked) + + def test_set_allowed(self): + """Ensure a blocked event type can be unblocked/allowed.""" + event = EVENT_TYPES[0] + pygame.event.set_blocked(event) + + self.assertTrue(pygame.event.get_blocked(event)) + + pygame.event.set_allowed(event) + + self.assertFalse(pygame.event.get_blocked(event)) + + def test_set_allowed__event_sequence(self): + """Ensure a sequence of blocked event types can be unblocked/allowed.""" + event_types = [ + pygame.KEYDOWN, + pygame.KEYUP, + pygame.MOUSEMOTION, + pygame.MOUSEBUTTONDOWN, + pygame.MOUSEBUTTONUP, + ] + pygame.event.set_blocked(event_types) + + pygame.event.set_allowed(event_types) + + for etype in event_types: + self.assertFalse(pygame.event.get_blocked(etype)) + + def test_set_allowed_all(self): + """Ensure all events can be unblocked/allowed at once.""" + pygame.event.set_blocked(None) + + for e in EVENT_TYPES: + self.assertTrue(pygame.event.get_blocked(e)) + + pygame.event.set_allowed(None) + + for e in EVENT_TYPES: + self.assertFalse(pygame.event.get_blocked(e)) + + def test_pump(self): + """Ensure pump() functions properly.""" + pygame.event.pump() + + # @unittest.skipIf( + # os.environ.get("SDL_VIDEODRIVER") == "dummy", + # 'requires the SDL_VIDEODRIVER to be a non "dummy" value', + # ) + # Fails on SDL 2.0.18 + @unittest.skip("flaky test, and broken on 2.0.18 windows") + def test_set_grab__and_get_symmetric(self): + """Ensure event grabbing can be enabled and disabled. + + WARNING: Moving the mouse off the display during this test can cause it + to fail. + """ + surf = pygame.display.set_mode((10, 10)) + pygame.event.set_grab(True) + + self.assertTrue(pygame.event.get_grab()) + + pygame.event.set_grab(False) + + self.assertFalse(pygame.event.get_grab()) + + def test_get_blocked(self): + """Ensure an event's blocked state can be retrieved.""" + # Test each event is not blocked. + pygame.event.set_allowed(None) + + for etype in EVENT_TYPES: + blocked = pygame.event.get_blocked(etype) + + self.assertFalse(blocked) + + # Test each event type is blocked. + pygame.event.set_blocked(None) + + for etype in EVENT_TYPES: + blocked = pygame.event.get_blocked(etype) + + self.assertTrue(blocked) + + def test_get_blocked__event_sequence(self): + """Ensure get_blocked() can handle a sequence of event types.""" + event_types = [ + pygame.KEYDOWN, + pygame.KEYUP, + pygame.MOUSEMOTION, + pygame.MOUSEBUTTONDOWN, + pygame.MOUSEBUTTONUP, + pygame.WINDOWMINIMIZED, + pygame.USEREVENT, + ] + + # Test no event types in the list are blocked. + blocked = pygame.event.get_blocked(event_types) + + self.assertFalse(blocked) + + # Test when 1 event type in the list is blocked. + pygame.event.set_blocked(event_types[2]) + + blocked = pygame.event.get_blocked(event_types) + + self.assertTrue(blocked) + + # Test all event types in the list are blocked. + pygame.event.set_blocked(event_types) + + blocked = pygame.event.get_blocked(event_types) + + self.assertTrue(blocked) + + # @unittest.skipIf( + # os.environ.get("SDL_VIDEODRIVER") == "dummy", + # 'requires the SDL_VIDEODRIVER to be a non "dummy" value', + # ) + # Fails on SDL 2.0.18 + @unittest.skip("flaky test, and broken on 2.0.18 windows") + def test_get_grab(self): + """Ensure get_grab() works as expected""" + surf = pygame.display.set_mode((10, 10)) + # Test 5 times + for i in range(5): + pygame.event.set_grab(i % 2) + self.assertEqual(pygame.event.get_grab(), i % 2) + + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") == "dummy", + "requires the SDL_VIDEODRIVER to be a non dummy value", + ) + @unittest.skipIf(pygame.get_sdl_version() < (2, 0, 16), "Needs at least SDL 2.0.16") + def test_set_keyboard_grab_and_get_keyboard_grab(self): + """Ensure set_keyboard_grab() and get_keyboard_grab() work as expected""" + + surf = pygame.display.set_mode((10, 10)) + + pygame.event.set_keyboard_grab(True) + self.assertTrue(pygame.event.get_keyboard_grab()) + + pygame.event.set_keyboard_grab(False) + self.assertFalse(pygame.event.get_keyboard_grab()) + + def test_poll(self): + """Ensure poll() works as expected""" + pygame.event.clear() + ev = pygame.event.poll() + # poll() on empty queue should return NOEVENT + self.assertEqual(ev.type, pygame.NOEVENT) + + # test poll returns stuff in same order + e1 = pygame.event.Event(pygame.USEREVENT) + e2 = pygame.event.Event(pygame.KEYDOWN, key=pygame.K_a) + e3 = pygame.event.Event(pygame.KEYUP, key=pygame.K_a) + pygame.event.post(e1) + pygame.event.post(e2) + pygame.event.post(e3) + + self.assertEqual(pygame.event.poll().type, e1.type) + self.assertEqual(pygame.event.poll().type, e2.type) + self.assertEqual(pygame.event.poll().type, e3.type) + self.assertEqual(pygame.event.poll().type, pygame.NOEVENT) + + +class EventModuleTestsWithTiming(unittest.TestCase): + __tags__ = ["timing"] + + def setUp(self): + pygame.display.init() + pygame.event.clear() # flush events + + def tearDown(self): + pygame.event.clear() # flush events + pygame.display.quit() + + def test_event_wait(self): + """Ensure wait() waits for an event on the queue.""" + # Test case without timeout. + event = pygame.event.Event(EVENT_TYPES[0], **EVENT_TEST_PARAMS[EVENT_TYPES[0]]) + pygame.event.post(event) + wait_event = pygame.event.wait() + + self.assertEqual(wait_event.type, event.type) + + # Test case with timeout and no event in the queue. + wait_event = pygame.event.wait(100) + self.assertEqual(wait_event.type, pygame.NOEVENT) + + # Test case with timeout and an event in the queue. + event = pygame.event.Event(EVENT_TYPES[0], **EVENT_TEST_PARAMS[EVENT_TYPES[0]]) + pygame.event.post(event) + wait_event = pygame.event.wait(100) + + self.assertEqual(wait_event.type, event.type) + + # test wait with timeout waits for the correct duration + pygame.time.set_timer(pygame.USEREVENT, 50, 3) + + for wait_time, expected_type, expected_time in ( + (60, pygame.USEREVENT, 50), + (65, pygame.USEREVENT, 50), + (20, pygame.NOEVENT, 20), + (45, pygame.USEREVENT, 30), + (70, pygame.NOEVENT, 70), + ): + start_time = time.perf_counter() + self.assertEqual(pygame.event.wait(wait_time).type, expected_type) + self.assertAlmostEqual( + time.perf_counter() - start_time, expected_time / 1000, delta=0.01 + ) + + # test wait without timeout waits for the full duration + pygame.time.set_timer(pygame.USEREVENT, 100, 1) + + start_time = time.perf_counter() + self.assertEqual(pygame.event.wait().type, pygame.USEREVENT) + self.assertAlmostEqual(time.perf_counter() - start_time, 0.1, delta=0.01) + + # test wait returns no event if event is arriving later + pygame.time.set_timer(pygame.USEREVENT, 50, 1) + self.assertEqual(pygame.event.wait(40).type, pygame.NOEVENT) + + +################################################################################ + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/A_PyGameMono-8.png b/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/A_PyGameMono-8.png new file mode 100644 index 00000000..b15961f0 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/A_PyGameMono-8.png differ diff --git a/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/PlayfairDisplaySemibold.ttf b/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/PlayfairDisplaySemibold.ttf new file mode 100644 index 00000000..c1213a7c Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/PlayfairDisplaySemibold.ttf differ diff --git a/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/PyGameMono-18-100dpi.bdf b/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/PyGameMono-18-100dpi.bdf new file mode 100644 index 00000000..a88f083e --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/PyGameMono-18-100dpi.bdf @@ -0,0 +1,165 @@ +STARTFONT 2.1 +FONT -FontForge-PyGameMono-Medium-R-Normal--25-180-100-100-M-250-ISO10646-1 +SIZE 18 100 100 +FONTBOUNDINGBOX 21 22 0 0 +COMMENT "Generated by fontforge, http://fontforge.sourceforge.net" +COMMENT "Created by Lenard Lindstrom,,, with FontForge 2.0 (http://fontforge.sf.net)" +STARTPROPERTIES 29 +FOUNDRY "FontForge" +FAMILY_NAME "PyGameMono" +WEIGHT_NAME "Medium" +SLANT "R" +SETWIDTH_NAME "Normal" +ADD_STYLE_NAME "" +PIXEL_SIZE 25 +POINT_SIZE 180 +RESOLUTION_X 100 +RESOLUTION_Y 100 +SPACING "M" +AVERAGE_WIDTH 250 +CHARSET_REGISTRY "ISO10646" +CHARSET_ENCODING "1" +FONTNAME_REGISTRY "" +CHARSET_COLLECTIONS "ISO10646-1" +FONT_NAME "PyGameMono" +FACE_NAME "PyGame Mono" +FONT_VERSION "001.000" +FONT_ASCENT 20 +FONT_DESCENT 5 +UNDERLINE_POSITION -2 +UNDERLINE_THICKNESS 2 +RAW_ASCENT 800 +RAW_DESCENT 200 +RELATIVE_WEIGHT 50 +RELATIVE_SETWIDTH 50 +FIGURE_WIDTH -1 +AVG_UPPERCASE_WIDTH 250 +ENDPROPERTIES +CHARS 5 +STARTCHAR .notdef +ENCODING 0 +SWIDTH 1000 0 +DWIDTH 25 0 +BBX 20 20 0 0 +BITMAP +FFFFF0 +FFFFF0 +FE07F0 +F801F0 +F000F0 +E00070 +E00070 +C00030 +C00030 +C00030 +C00030 +C00030 +C00030 +E00070 +E00070 +F000F0 +F801F0 +FE07F0 +FFFFF0 +FFFFF0 +ENDCHAR +STARTCHAR A +ENCODING 65 +SWIDTH 1000 0 +DWIDTH 25 0 +BBX 20 21 0 1 +BITMAP +03FC00 +1FFF80 +3FFFC0 +7C03E0 +F000F0 +E00070 +E00070 +F000F0 +FC03F0 +FFFFF0 +FFFFF0 +FFFFF0 +FF0FF0 +7C03F0 +7801E0 +7800E0 +7000E0 +700060 +600060 +200040 +200040 +ENDCHAR +STARTCHAR B +ENCODING 66 +SWIDTH 1000 0 +DWIDTH 25 0 +BBX 18 20 1 0 +BITMAP +FFFE00 +FFFF80 +7E0780 +7801C0 +7000C0 +3000C0 +3000C0 +3801C0 +3E0780 +3FFF00 +3FFF00 +3E0780 +380180 +3000C0 +3000C0 +3000C0 +7801C0 +7E07C0 +FFFF80 +FFFE00 +ENDCHAR +STARTCHAR C +ENCODING 67 +SWIDTH 1000 0 +DWIDTH 25 0 +BBX 20 20 0 0 +BITMAP +00FC00 +03FF00 +0FFF80 +1F03E0 +3E0070 +7C0010 +780000 +F80000 +F00000 +F00000 +F00000 +F00000 +F80000 +780000 +7C0010 +3E0070 +1F01E0 +0FFFC0 +03FF80 +00FE00 +ENDCHAR +STARTCHAR u13079 +ENCODING 77945 +SWIDTH 1000 0 +DWIDTH 25 0 +BBX 21 10 0 5 +BITMAP +03FC00 +0FFF80 +1E73C0 +78F8F0 +F0F878 +70F870 +3870E0 +1E03C0 +0FFF80 +03FC00 +ENDCHAR +ENDFONT diff --git a/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/PyGameMono-18-75dpi.bdf b/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/PyGameMono-18-75dpi.bdf new file mode 100644 index 00000000..127f7043 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/PyGameMono-18-75dpi.bdf @@ -0,0 +1,143 @@ +STARTFONT 2.1 +FONT -FontForge-PyGameMono-Medium-R-Normal--19-180-75-75-M-190-ISO10646-1 +SIZE 18 75 75 +FONTBOUNDINGBOX 15 17 0 0 +COMMENT "Generated by fontforge, http://fontforge.sourceforge.net" +COMMENT "Created by Lenard Lindstrom,,, with FontForge 2.0 (http://fontforge.sf.net)" +STARTPROPERTIES 29 +FOUNDRY "FontForge" +FAMILY_NAME "PyGameMono" +WEIGHT_NAME "Medium" +SLANT "R" +SETWIDTH_NAME "Normal" +ADD_STYLE_NAME "" +PIXEL_SIZE 19 +POINT_SIZE 180 +RESOLUTION_X 75 +RESOLUTION_Y 75 +SPACING "M" +AVERAGE_WIDTH 190 +CHARSET_REGISTRY "ISO10646" +CHARSET_ENCODING "1" +FONTNAME_REGISTRY "" +CHARSET_COLLECTIONS "ISO10646-1" +FONT_NAME "PyGameMono" +FACE_NAME "PyGame Mono" +FONT_VERSION "001.000" +FONT_ASCENT 15 +FONT_DESCENT 4 +UNDERLINE_POSITION -2 +UNDERLINE_THICKNESS 1 +RAW_ASCENT 800 +RAW_DESCENT 200 +RELATIVE_WEIGHT 50 +RELATIVE_SETWIDTH 50 +FIGURE_WIDTH -1 +AVG_UPPERCASE_WIDTH 190 +ENDPROPERTIES +CHARS 5 +STARTCHAR .notdef +ENCODING 0 +SWIDTH 1000 0 +DWIDTH 19 0 +BBX 15 15 0 0 +BITMAP +FFFE +FFFE +FC7E +F01E +E00E +C006 +C006 +C006 +C006 +C006 +E00E +F01E +FC7E +FFFE +FFFE +ENDCHAR +STARTCHAR A +ENCODING 65 +SWIDTH 1000 0 +DWIDTH 19 0 +BBX 15 17 0 0 +BITMAP +0FE0 +3FF8 +783C +F01E +E00E +E00E +F01E +F83E +FFFE +FFFE +FC7E +701C +701C +600C +600C +4004 +4004 +ENDCHAR +STARTCHAR B +ENCODING 66 +SWIDTH 1000 0 +DWIDTH 19 0 +BBX 15 15 0 0 +BITMAP +FFF8 +7FFC +780E +3006 +3006 +380E +3FF8 +3FF8 +3FF8 +380E +3006 +3006 +7C1E +7FFC +FFF8 +ENDCHAR +STARTCHAR C +ENCODING 67 +SWIDTH 1000 0 +DWIDTH 19 0 +BBX 15 15 0 0 +BITMAP +03E0 +0FF8 +3C1C +7806 +7000 +E000 +E000 +E000 +E000 +E000 +7000 +7806 +3C1C +0FF8 +03E0 +ENDCHAR +STARTCHAR u13079 +ENCODING 77945 +SWIDTH 1000 0 +DWIDTH 19 0 +BBX 15 7 0 4 +BITMAP +0FE0 +3838 +638C +E38E +638C +3838 +0FE0 +ENDCHAR +ENDFONT diff --git a/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/PyGameMono-8.bdf b/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/PyGameMono-8.bdf new file mode 100644 index 00000000..17bef064 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/PyGameMono-8.bdf @@ -0,0 +1,103 @@ +STARTFONT 2.1 +FONT -FontForge-PyGameMono-Medium-R-Normal--8-80-75-75-C-80-ISO10646-1 +SIZE 8 75 75 +FONTBOUNDINGBOX 6 7 0 0 +COMMENT "Generated by fontforge, http://fontforge.sourceforge.net" +COMMENT "Created by Lenard Lindstrom,,, with FontForge 2.0 (http://fontforge.sf.net)" +STARTPROPERTIES 29 +FOUNDRY "FontForge" +FAMILY_NAME "PyGameMono" +WEIGHT_NAME "Medium" +SLANT "R" +SETWIDTH_NAME "Normal" +ADD_STYLE_NAME "" +PIXEL_SIZE 8 +POINT_SIZE 80 +RESOLUTION_X 75 +RESOLUTION_Y 75 +SPACING "C" +AVERAGE_WIDTH 80 +CHARSET_REGISTRY "ISO10646" +CHARSET_ENCODING "1" +FONTNAME_REGISTRY "" +CHARSET_COLLECTIONS "ISO10646-1" +FONT_NAME "PyGameMono" +FACE_NAME "PyGame Mono" +FONT_VERSION "001.000" +FONT_ASCENT 6 +FONT_DESCENT 2 +UNDERLINE_POSITION -1 +UNDERLINE_THICKNESS 1 +RAW_ASCENT 800 +RAW_DESCENT 200 +RELATIVE_WEIGHT 50 +RELATIVE_SETWIDTH 50 +FIGURE_WIDTH -1 +AVG_UPPERCASE_WIDTH 80 +ENDPROPERTIES +CHARS 5 +STARTCHAR .notdef +ENCODING 0 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 6 6 0 0 +BITMAP +FC +84 +84 +84 +84 +FC +ENDCHAR +STARTCHAR A +ENCODING 65 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 6 7 0 0 +BITMAP +78 +84 +84 +FC +84 +84 +84 +ENDCHAR +STARTCHAR B +ENCODING 66 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 6 6 0 0 +BITMAP +FC +44 +78 +4C +44 +FC +ENDCHAR +STARTCHAR C +ENCODING 67 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 6 6 0 0 +BITMAP +78 +C4 +C0 +C0 +C4 +78 +ENDCHAR +STARTCHAR u13079 +ENCODING 77945 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 6 4 0 1 +BITMAP +78 +B4 +B4 +78 +ENDCHAR +ENDFONT diff --git a/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/PyGameMono.otf b/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/PyGameMono.otf new file mode 100644 index 00000000..5e9b66cc Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/PyGameMono.otf differ diff --git a/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/test_fixed.otf b/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/test_fixed.otf new file mode 100644 index 00000000..34888982 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/test_fixed.otf differ diff --git a/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/test_sans.ttf b/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/test_sans.ttf new file mode 100644 index 00000000..09fac2ff Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/test_sans.ttf differ diff --git a/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/u13079_PyGameMono-8.png b/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/u13079_PyGameMono-8.png new file mode 100644 index 00000000..911da8a6 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/fixtures/fonts/u13079_PyGameMono-8.png differ diff --git a/.venv/Lib/site-packages/pygame/tests/fixtures/xbm_cursors/white_sizing.xbm b/.venv/Lib/site-packages/pygame/tests/fixtures/xbm_cursors/white_sizing.xbm new file mode 100644 index 00000000..d334d8dc --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/fixtures/xbm_cursors/white_sizing.xbm @@ -0,0 +1,8 @@ +#define resize_white_width 16 +#define resize_white_height 16 +#define resize_white_x_hot 7 +#define resize_white_y_hot 7 +static unsigned char resize_white_bits[] = { + 0xff, 0x03, 0x01, 0x02, 0xfd, 0x03, 0x05, 0x00, 0xf5, 0x0f, 0x15, 0x08, + 0xd5, 0xeb, 0x55, 0xaa, 0x55, 0xaa, 0xd7, 0xab, 0x10, 0xa8, 0xf0, 0xb7, + 0x00, 0xa8, 0xc0, 0x9f, 0x40, 0x80, 0xc0, 0xff}; diff --git a/.venv/Lib/site-packages/pygame/tests/fixtures/xbm_cursors/white_sizing_mask.xbm b/.venv/Lib/site-packages/pygame/tests/fixtures/xbm_cursors/white_sizing_mask.xbm new file mode 100644 index 00000000..f00bc46a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/fixtures/xbm_cursors/white_sizing_mask.xbm @@ -0,0 +1,8 @@ +#define resize_white_mask_width 16 +#define resize_white_mask_height 16 +#define resize_white_mask_x_hot 7 +#define resize_white_mask_y_hot 7 +static unsigned char resize_white_mask_bits[] = { + 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0x07, 0x00, 0xf7, 0x0f, 0xf7, 0x0f, + 0xf7, 0xef, 0x77, 0xee, 0x77, 0xee, 0xf7, 0xef, 0xf0, 0xef, 0xf0, 0xff, + 0x00, 0xf8, 0xc0, 0xff, 0xc0, 0xff, 0xc0, 0xff}; diff --git a/.venv/Lib/site-packages/pygame/tests/font_test.py b/.venv/Lib/site-packages/pygame/tests/font_test.py new file mode 100644 index 00000000..98d6989f --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/font_test.py @@ -0,0 +1,749 @@ +# -*- coding: utf-8 -*- +from re import T +import sys +import os +import unittest +import pathlib +import platform + +import pygame +from pygame import font as pygame_font # So font can be replaced with ftfont + + +FONTDIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures", "fonts") + + +def equal_images(s1, s2): + size = s1.get_size() + if s2.get_size() != size: + return False + w, h = size + for x in range(w): + for y in range(h): + if s1.get_at((x, y)) != s2.get_at((x, y)): + return False + return True + + +IS_PYPY = "PyPy" == platform.python_implementation() + + +@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO +class FontModuleTest(unittest.TestCase): + def setUp(self): + pygame_font.init() + + def tearDown(self): + pygame_font.quit() + + def test_get_sdl_ttf_version(self): + def test_ver_tuple(ver): + self.assertIsInstance(ver, tuple) + self.assertEqual(len(ver), 3) + for i in ver: + self.assertIsInstance(i, int) + + if pygame_font.__name__ != "pygame.ftfont": + compiled = pygame_font.get_sdl_ttf_version() + linked = pygame_font.get_sdl_ttf_version(linked=True) + + test_ver_tuple(compiled) + test_ver_tuple(linked) + + self.assertTrue(linked >= compiled) + + def test_SysFont(self): + # Can only check that a font object is returned. + fonts = pygame_font.get_fonts() + if "arial" in fonts: + # Try to use arial font if it is there, rather than a random font + # which can be different depending on installed fonts on the system. + font_name = "arial" + else: + font_name = sorted(fonts)[0] + o = pygame_font.SysFont(font_name, 20) + self.assertTrue(isinstance(o, pygame_font.FontType)) + o = pygame_font.SysFont(font_name, 20, italic=True) + self.assertTrue(isinstance(o, pygame_font.FontType)) + o = pygame_font.SysFont(font_name, 20, bold=True) + self.assertTrue(isinstance(o, pygame_font.FontType)) + o = pygame_font.SysFont("thisisnotafont", 20) + self.assertTrue(isinstance(o, pygame_font.FontType)) + + def test_get_default_font(self): + self.assertEqual(pygame_font.get_default_font(), "freesansbold.ttf") + + def test_get_fonts_returns_something(self): + fnts = pygame_font.get_fonts() + self.assertTrue(fnts) + + # to test if some files exist... + # def XXtest_has_file_osx_10_5_sdk(self): + # import os + # f = "/Developer/SDKs/MacOSX10.5.sdk/usr/X11/include/ft2build.h" + # self.assertEqual(os.path.exists(f), True) + + # def XXtest_has_file_osx_10_4_sdk(self): + # import os + # f = "/Developer/SDKs/MacOSX10.4u.sdk/usr/X11R6/include/ft2build.h" + # self.assertEqual(os.path.exists(f), True) + + def test_get_fonts(self): + fnts = pygame_font.get_fonts() + + self.assertTrue(fnts, msg=repr(fnts)) + + for name in fnts: + # note, on ubuntu 2.6 they are all unicode strings. + + self.assertTrue(isinstance(name, str), name) + # Font names can be comprised of only numeric characters, so + # just checking name.islower() will not work as expected here. + self.assertFalse(any(c.isupper() for c in name)) + self.assertTrue(name.isalnum(), name) + + def test_get_init(self): + self.assertTrue(pygame_font.get_init()) + pygame_font.quit() + self.assertFalse(pygame_font.get_init()) + + def test_init(self): + pygame_font.init() + + def test_match_font_all_exist(self): + fonts = pygame_font.get_fonts() + + # Ensure all listed fonts are in fact available, and the returned file + # name is a full path. + for font in fonts: + path = pygame_font.match_font(font) + self.assertFalse(path is None) + self.assertTrue(os.path.isabs(path) and os.path.isfile(path)) + + def test_match_font_name(self): + """That match_font accepts names of various types""" + font = pygame_font.get_fonts()[0] + font_path = pygame_font.match_font(font) + self.assertIsNotNone(font_path) + font_b = font.encode() + not_a_font = "thisisnotafont" + not_a_font_b = b"thisisnotafont" + good_font_names = [ + # Check single name bytes. + font_b, + # Check string of comma-separated names. + ",".join([not_a_font, font, not_a_font]), + # Check list of names. + [not_a_font, font, not_a_font], + # Check generator: + (name for name in [not_a_font, font, not_a_font]), + # Check comma-separated bytes. + b",".join([not_a_font_b, font_b, not_a_font_b]), + # Check list of bytes. + [not_a_font_b, font_b, not_a_font_b], + # Check mixed list of bytes and string. + [font, not_a_font, font_b, not_a_font_b], + ] + for font_name in good_font_names: + self.assertEqual(pygame_font.match_font(font_name), font_path, font_name) + + def test_not_match_font_name(self): + """match_font return None when names of various types do not exist""" + not_a_font = "thisisnotafont" + not_a_font_b = b"thisisnotafont" + bad_font_names = [ + not_a_font, + ",".join([not_a_font, not_a_font, not_a_font]), + [not_a_font, not_a_font, not_a_font], + (name for name in [not_a_font, not_a_font, not_a_font]), + not_a_font_b, + b",".join([not_a_font_b, not_a_font_b, not_a_font_b]), + [not_a_font_b, not_a_font_b, not_a_font_b], + [not_a_font, not_a_font_b, not_a_font], + ] + for font_name in bad_font_names: + self.assertIsNone(pygame_font.match_font(font_name), font_name) + + def test_match_font_bold(self): + fonts = pygame_font.get_fonts() + + # Look for a bold font. + self.assertTrue(any(pygame_font.match_font(font, bold=True) for font in fonts)) + + def test_match_font_italic(self): + fonts = pygame_font.get_fonts() + + # Look for an italic font. + self.assertTrue( + any(pygame_font.match_font(font, italic=True) for font in fonts) + ) + + def test_issue_742(self): + """that the font background does not crash.""" + surf = pygame.Surface((320, 240)) + font = pygame_font.Font(None, 24) + image = font.render("Test", 0, (255, 255, 255), (0, 0, 0)) + self.assertIsNone(image.get_colorkey()) + image.set_alpha(255) + surf.blit(image, (0, 0)) + + # not issue 742, but be sure to test that background color is + # correctly issued on this mode + self.assertEqual(surf.get_at((0, 0)), pygame.Color(0, 0, 0)) + + def test_issue_font_alphablit(self): + """Check that blitting anti-aliased text doesn't + change the background blue""" + pygame.display.set_mode((600, 400)) + + font = pygame_font.Font(None, 24) + + (color, text, center, pos) = ((160, 200, 250), "Music", (190, 170), "midright") + img1 = font.render(text, True, color) + + img = pygame.Surface(img1.get_size(), depth=32) + pre_blit_corner_pixel = img.get_at((0, 0)) + img.blit(img1, (0, 0)) + post_blit_corner_pixel = img.get_at((0, 0)) + + self.assertEqual(pre_blit_corner_pixel, post_blit_corner_pixel) + + def test_segfault_after_reinit(self): + """Reinitialization of font module should not cause + segmentation fault""" + import gc + + font = pygame_font.Font(None, 20) + pygame_font.quit() + pygame_font.init() + del font + gc.collect() + + def test_quit(self): + pygame_font.quit() + + +@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO +class FontTest(unittest.TestCase): + def setUp(self): + pygame_font.init() + + def tearDown(self): + pygame_font.quit() + + def test_render_args(self): + screen = pygame.display.set_mode((600, 400)) + rect = screen.get_rect() + f = pygame_font.Font(None, 20) + screen.fill((10, 10, 10)) + font_surface = f.render(" bar", True, (0, 0, 0), (255, 255, 255)) + font_rect = font_surface.get_rect() + font_rect.topleft = rect.topleft + self.assertTrue(font_surface) + screen.blit(font_surface, font_rect, font_rect) + pygame.display.update() + self.assertEqual(tuple(screen.get_at((0, 0)))[:3], (255, 255, 255)) + self.assertEqual(tuple(screen.get_at(font_rect.topleft))[:3], (255, 255, 255)) + + # If we don't have a real display, don't do this test. + # Transparent background doesn't seem to work without a read video card. + if os.environ.get("SDL_VIDEODRIVER") != "dummy": + screen.fill((10, 10, 10)) + font_surface = f.render(" bar", True, (0, 0, 0), None) + font_rect = font_surface.get_rect() + font_rect.topleft = rect.topleft + self.assertTrue(font_surface) + screen.blit(font_surface, font_rect, font_rect) + pygame.display.update() + self.assertEqual(tuple(screen.get_at((0, 0)))[:3], (10, 10, 10)) + self.assertEqual(tuple(screen.get_at(font_rect.topleft))[:3], (10, 10, 10)) + + screen.fill((10, 10, 10)) + font_surface = f.render(" bar", True, (0, 0, 0)) + font_rect = font_surface.get_rect() + font_rect.topleft = rect.topleft + self.assertTrue(font_surface) + screen.blit(font_surface, font_rect, font_rect) + pygame.display.update(rect) + self.assertEqual(tuple(screen.get_at((0, 0)))[:3], (10, 10, 10)) + self.assertEqual(tuple(screen.get_at(font_rect.topleft))[:3], (10, 10, 10)) + + +@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO +class FontTypeTest(unittest.TestCase): + def setUp(self): + pygame_font.init() + + def tearDown(self): + pygame_font.quit() + + def test_default_parameters(self): + f = pygame_font.Font() + + def test_get_ascent(self): + # Ckecking ascent would need a custom test font to do properly. + f = pygame_font.Font(None, 20) + ascent = f.get_ascent() + self.assertTrue(isinstance(ascent, int)) + self.assertTrue(ascent > 0) + s = f.render("X", False, (255, 255, 255)) + self.assertTrue(s.get_size()[1] > ascent) + + def test_get_descent(self): + # Ckecking descent would need a custom test font to do properly. + f = pygame_font.Font(None, 20) + descent = f.get_descent() + self.assertTrue(isinstance(descent, int)) + self.assertTrue(descent < 0) + + def test_get_height(self): + # Ckecking height would need a custom test font to do properly. + f = pygame_font.Font(None, 20) + height = f.get_height() + self.assertTrue(isinstance(height, int)) + self.assertTrue(height > 0) + s = f.render("X", False, (255, 255, 255)) + self.assertTrue(s.get_size()[1] == height) + + def test_get_linesize(self): + # Ckecking linesize would need a custom test font to do properly. + # Questions: How do linesize, height and descent relate? + f = pygame_font.Font(None, 20) + linesize = f.get_linesize() + self.assertTrue(isinstance(linesize, int)) + self.assertTrue(linesize > 0) + + def test_metrics(self): + # Ensure bytes decoding works correctly. Can only compare results + # with unicode for now. + f = pygame_font.Font(None, 20) + um = f.metrics(".") + bm = f.metrics(b".") + + self.assertEqual(len(um), 1) + self.assertEqual(len(bm), 1) + self.assertIsNotNone(um[0]) + self.assertEqual(um, bm) + + u = "\u212A" + b = u.encode("UTF-16")[2:] # Keep byte order consistent. [2:] skips BOM + bm = f.metrics(b) + + self.assertEqual(len(bm), 2) + + try: # FIXME why do we do this try/except ? + um = f.metrics(u) + except pygame.error: + pass + else: + self.assertEqual(len(um), 1) + self.assertNotEqual(bm[0], um[0]) + self.assertNotEqual(bm[1], um[0]) + + u = "\U00013000" + bm = f.metrics(u) + + self.assertEqual(len(bm), 1) + self.assertIsNone(bm[0]) + + return # unfinished + # The documentation is useless here. How large a list? + # How do list positions relate to character codes? + # What about unicode characters? + + # __doc__ (as of 2008-08-02) for pygame_font.Font.metrics: + + # Font.metrics(text): return list + # Gets the metrics for each character in the passed string. + # + # The list contains tuples for each character, which contain the + # minimum X offset, the maximum X offset, the minimum Y offset, the + # maximum Y offset and the advance offset (bearing plus width) of the + # character. [(minx, maxx, miny, maxy, advance), (minx, maxx, miny, + # maxy, advance), ...] + + self.fail() + + def test_render(self): + f = pygame_font.Font(None, 20) + s = f.render("foo", True, [0, 0, 0], [255, 255, 255]) + s = f.render("xxx", True, [0, 0, 0], [255, 255, 255]) + s = f.render("", True, [0, 0, 0], [255, 255, 255]) + s = f.render("foo", False, [0, 0, 0], [255, 255, 255]) + s = f.render("xxx", False, [0, 0, 0], [255, 255, 255]) + s = f.render("xxx", False, [0, 0, 0]) + s = f.render(" ", False, [0, 0, 0]) + s = f.render(" ", False, [0, 0, 0], [255, 255, 255]) + # null text should be 0 pixel wide. + s = f.render("", False, [0, 0, 0], [255, 255, 255]) + self.assertEqual(s.get_size()[0], 0) + # None text should be 0 pixel wide. + s = f.render(None, False, [0, 0, 0], [255, 255, 255]) + self.assertEqual(s.get_size()[0], 0) + # Non-text should raise a TypeError. + self.assertRaises(TypeError, f.render, [], False, [0, 0, 0], [255, 255, 255]) + self.assertRaises(TypeError, f.render, 1, False, [0, 0, 0], [255, 255, 255]) + # is background transparent for antialiasing? + s = f.render(".", True, [255, 255, 255]) + self.assertEqual(s.get_at((0, 0))[3], 0) + # is Unicode and bytes encoding correct? + # Cannot really test if the correct characters are rendered, but + # at least can assert the encodings differ. + su = f.render(".", False, [0, 0, 0], [255, 255, 255]) + sb = f.render(b".", False, [0, 0, 0], [255, 255, 255]) + self.assertTrue(equal_images(su, sb)) + u = "\u212A" + b = u.encode("UTF-16")[2:] # Keep byte order consistent. [2:] skips BOM + sb = f.render(b, False, [0, 0, 0], [255, 255, 255]) + try: # FIXME why do we do this try/except ? + su = f.render(u, False, [0, 0, 0], [255, 255, 255]) + except pygame.error: + pass + else: + self.assertFalse(equal_images(su, sb)) + + # test for internal null bytes + self.assertRaises(ValueError, f.render, b"ab\x00cd", 0, [0, 0, 0]) + self.assertRaises(ValueError, f.render, "ab\x00cd", 0, [0, 0, 0]) + + def test_render_ucs2_ucs4(self): + """that it renders without raising if there is a new enough SDL_ttf.""" + f = pygame_font.Font(None, 20) + # If the font module is SDL_ttf < 2.0.15 based, then it only supports UCS-2 + # it will raise an exception for an out-of-range UCS-4 code point. + if hasattr(pygame_font, "UCS4"): + ucs_2 = "\uFFEE" + s = f.render(ucs_2, False, [0, 0, 0], [255, 255, 255]) + ucs_4 = "\U00010000" + s = f.render(ucs_4, False, [0, 0, 0], [255, 255, 255]) + + def test_set_bold(self): + f = pygame_font.Font(None, 20) + self.assertFalse(f.get_bold()) + f.set_bold(True) + self.assertTrue(f.get_bold()) + f.set_bold(False) + self.assertFalse(f.get_bold()) + + def test_set_italic(self): + f = pygame_font.Font(None, 20) + self.assertFalse(f.get_italic()) + f.set_italic(True) + self.assertTrue(f.get_italic()) + f.set_italic(False) + self.assertFalse(f.get_italic()) + + def test_set_underline(self): + f = pygame_font.Font(None, 20) + self.assertFalse(f.get_underline()) + f.set_underline(True) + self.assertTrue(f.get_underline()) + f.set_underline(False) + self.assertFalse(f.get_underline()) + + def test_set_strikethrough(self): + if pygame_font.__name__ != "pygame.ftfont": + f = pygame_font.Font(None, 20) + self.assertFalse(f.get_strikethrough()) + f.set_strikethrough(True) + self.assertTrue(f.get_strikethrough()) + f.set_strikethrough(False) + self.assertFalse(f.get_strikethrough()) + + def test_bold_attr(self): + f = pygame_font.Font(None, 20) + self.assertFalse(f.bold) + f.bold = True + self.assertTrue(f.bold) + f.bold = False + self.assertFalse(f.bold) + + def test_set_italic_property(self): + f = pygame_font.Font(None, 20) + self.assertFalse(f.italic) + f.italic = True + self.assertTrue(f.italic) + f.italic = False + self.assertFalse(f.italic) + + def test_set_underline_property(self): + f = pygame_font.Font(None, 20) + self.assertFalse(f.underline) + f.underline = True + self.assertTrue(f.underline) + f.underline = False + self.assertFalse(f.underline) + + def test_set_strikethrough_property(self): + if pygame_font.__name__ != "pygame.ftfont": + f = pygame_font.Font(None, 20) + self.assertFalse(f.strikethrough) + f.strikethrough = True + self.assertTrue(f.strikethrough) + f.strikethrough = False + self.assertFalse(f.strikethrough) + + def test_size(self): + f = pygame_font.Font(None, 20) + text = "Xg" + size = f.size(text) + w, h = size + s = f.render(text, False, (255, 255, 255)) + btext = text.encode("ascii") + + self.assertIsInstance(w, int) + self.assertIsInstance(h, int) + self.assertEqual(s.get_size(), size) + self.assertEqual(f.size(btext), size) + + text = "\u212A" + btext = text.encode("UTF-16")[2:] # Keep the byte order consistent. + bsize = f.size(btext) + size = f.size(text) + + self.assertNotEqual(size, bsize) + + def test_font_file_not_found(self): + # A per BUG reported by Bo Jangeborg on pygame-user mailing list, + # http://www.mail-archive.com/pygame-users@seul.org/msg11675.html + + pygame_font.init() + self.assertRaises( + FileNotFoundError, pygame_font.Font, "some-fictional-font.ttf", 20 + ) + + def test_load_from_file(self): + font_name = pygame_font.get_default_font() + font_path = os.path.join( + os.path.split(pygame.__file__)[0], pygame_font.get_default_font() + ) + f = pygame_font.Font(font_path, 20) + + def test_load_from_file_default(self): + font_name = pygame_font.get_default_font() + font_path = os.path.join( + os.path.split(pygame.__file__)[0], pygame_font.get_default_font() + ) + f = pygame_font.Font(font_path) + + def test_load_from_pathlib(self): + font_name = pygame_font.get_default_font() + font_path = os.path.join( + os.path.split(pygame.__file__)[0], pygame_font.get_default_font() + ) + f = pygame_font.Font(pathlib.Path(font_path), 20) + f = pygame_font.Font(pathlib.Path(font_path)) + + def test_load_from_pathlib_default(self): + font_name = pygame_font.get_default_font() + font_path = os.path.join( + os.path.split(pygame.__file__)[0], pygame_font.get_default_font() + ) + f = pygame_font.Font(pathlib.Path(font_path)) + + def test_load_from_file_obj(self): + font_name = pygame_font.get_default_font() + font_path = os.path.join( + os.path.split(pygame.__file__)[0], pygame_font.get_default_font() + ) + with open(font_path, "rb") as f: + font = pygame_font.Font(f, 20) + + def test_load_from_file_obj_default(self): + font_name = pygame_font.get_default_font() + font_path = os.path.join( + os.path.split(pygame.__file__)[0], pygame_font.get_default_font() + ) + with open(font_path, "rb") as f: + font = pygame_font.Font(f) + + def test_load_default_font_filename(self): + # In font_init, a special case is when the filename argument is + # identical to the default font file name. + f = pygame_font.Font(pygame_font.get_default_font(), 20) + + def test_load_default_font_filename_default(self): + # In font_init, a special case is when the filename argument is + # identical to the default font file name. + f = pygame_font.Font(pygame_font.get_default_font()) + + def _load_unicode(self, path): + import shutil + + fdir = str(FONTDIR) + temp = os.path.join(fdir, path) + pgfont = os.path.join(fdir, "test_sans.ttf") + shutil.copy(pgfont, temp) + try: + with open(temp, "rb") as f: + pass + except FileNotFoundError: + raise unittest.SkipTest("the path cannot be opened") + try: + pygame_font.Font(temp, 20) + finally: + os.remove(temp) + + def test_load_from_file_unicode_0(self): + """ASCII string as a unicode object""" + self._load_unicode("temp_file.ttf") + + def test_load_from_file_unicode_1(self): + self._load_unicode("你好.ttf") + + def test_load_from_file_bytes(self): + font_path = os.path.join( + os.path.split(pygame.__file__)[0], pygame_font.get_default_font() + ) + filesystem_encoding = sys.getfilesystemencoding() + filesystem_errors = "replace" if sys.platform == "win32" else "surrogateescape" + try: # FIXME why do we do this try/except ? + font_path = font_path.decode(filesystem_encoding, filesystem_errors) + except AttributeError: + pass + bfont_path = font_path.encode(filesystem_encoding, filesystem_errors) + f = pygame_font.Font(bfont_path, 20) + + def test_issue_3144(self): + fpath = os.path.join(FONTDIR, "PlayfairDisplaySemibold.ttf") + + # issue in SDL_ttf 2.0.18 DLL on Windows + # tested to make us aware of any regressions + for size in (60, 40, 10, 20, 70, 45, 50, 10): + font = pygame_font.Font(fpath, size) + font.render("WHERE", True, "black") + + def test_font_set_script(self): + if pygame_font.__name__ == "pygame.ftfont": + return # this ain't a pygame.ftfont thing! + + font = pygame_font.Font(None, 16) + + ttf_version = pygame_font.get_sdl_ttf_version() + if ttf_version >= (2, 20, 0): + self.assertRaises(TypeError, pygame.font.Font.set_script) + self.assertRaises(TypeError, pygame.font.Font.set_script, font) + self.assertRaises(TypeError, pygame.font.Font.set_script, "hey", "Deva") + self.assertRaises(TypeError, font.set_script, 1) + self.assertRaises(TypeError, font.set_script, ["D", "e", "v", "a"]) + + self.assertRaises(ValueError, font.set_script, "too long by far") + self.assertRaises(ValueError, font.set_script, "") + self.assertRaises(ValueError, font.set_script, "a") + + font.set_script("Deva") + else: + self.assertRaises(pygame.error, font.set_script, "Deva") + + +@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO +class VisualTests(unittest.TestCase): + __tags__ = ["interactive"] + + screen = None + aborted = False + + def setUp(self): + if self.screen is None: + pygame.init() + self.screen = pygame.display.set_mode((600, 200)) + self.screen.fill((255, 255, 255)) + pygame.display.flip() + self.f = pygame_font.Font(None, 32) + + def abort(self): + if self.screen is not None: + pygame.quit() + self.aborted = True + + def query( + self, + bold=False, + italic=False, + underline=False, + strikethrough=False, + antialiase=False, + ): + if self.aborted: + return False + spacing = 10 + offset = 20 + y = spacing + f = self.f + screen = self.screen + screen.fill((255, 255, 255)) + pygame.display.flip() + if not (bold or italic or underline or strikethrough or antialiase): + text = "normal" + else: + modes = [] + if bold: + modes.append("bold") + if italic: + modes.append("italic") + if underline: + modes.append("underlined") + if strikethrough: + modes.append("strikethrough") + if antialiase: + modes.append("antialiased") + text = f"{'-'.join(modes)} (y/n):" + f.set_bold(bold) + f.set_italic(italic) + f.set_underline(underline) + if pygame_font.__name__ != "pygame.ftfont": + f.set_strikethrough(strikethrough) + s = f.render(text, antialiase, (0, 0, 0)) + screen.blit(s, (offset, y)) + y += s.get_size()[1] + spacing + f.set_bold(False) + f.set_italic(False) + f.set_underline(False) + if pygame_font.__name__ != "pygame.ftfont": + f.set_strikethrough(False) + s = f.render("(some comparison text)", False, (0, 0, 0)) + screen.blit(s, (offset, y)) + pygame.display.flip() + while True: + for evt in pygame.event.get(): + if evt.type == pygame.KEYDOWN: + if evt.key == pygame.K_ESCAPE: + self.abort() + return False + if evt.key == pygame.K_y: + return True + if evt.key == pygame.K_n: + return False + if evt.type == pygame.QUIT: + self.abort() + return False + + def test_bold(self): + self.assertTrue(self.query(bold=True)) + + def test_italic(self): + self.assertTrue(self.query(italic=True)) + + def test_underline(self): + self.assertTrue(self.query(underline=True)) + + def test_strikethrough(self): + if pygame_font.__name__ != "pygame.ftfont": + self.assertTrue(self.query(strikethrough=True)) + + def test_antialiase(self): + self.assertTrue(self.query(antialiase=True)) + + def test_bold_antialiase(self): + self.assertTrue(self.query(bold=True, antialiase=True)) + + def test_italic_underline(self): + self.assertTrue(self.query(italic=True, underline=True)) + + def test_bold_strikethrough(self): + if pygame_font.__name__ != "pygame.ftfont": + self.assertTrue(self.query(bold=True, strikethrough=True)) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/freetype_tags.py b/.venv/Lib/site-packages/pygame/tests/freetype_tags.py new file mode 100644 index 00000000..d84cbb77 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/freetype_tags.py @@ -0,0 +1,11 @@ +__tags__ = ["development"] + +exclude = False + +try: + import pygame.freetype +except ImportError: + exclude = True + +if exclude: + __tags__.extend(["ignore", "subprocess_ignore"]) diff --git a/.venv/Lib/site-packages/pygame/tests/freetype_test.py b/.venv/Lib/site-packages/pygame/tests/freetype_test.py new file mode 100644 index 00000000..25551d8b --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/freetype_test.py @@ -0,0 +1,1796 @@ +import os + +if os.environ.get("SDL_VIDEODRIVER") == "dummy": + __tags__ = ("ignore", "subprocess_ignore") + +import unittest +import ctypes +import weakref +import gc +import pathlib +import platform + +IS_PYPY = "PyPy" == platform.python_implementation() + + +try: + from pygame.tests.test_utils import arrinter +except NameError: + pass + +import pygame + +try: + import pygame.freetype as ft +except ImportError: + ft = None + + +FONTDIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures", "fonts") + + +def nullfont(): + """return an uninitialized font instance""" + return ft.Font.__new__(ft.Font) + + +max_point_size_FX6 = 0x7FFFFFFF +max_point_size = max_point_size_FX6 >> 6 +max_point_size_f = max_point_size_FX6 * 0.015625 + + +def surf_same_image(a, b): + """Return True if a's pixel buffer is identical to b's""" + + a_sz = a.get_height() * a.get_pitch() + b_sz = b.get_height() * b.get_pitch() + if a_sz != b_sz: + return False + a_bytes = ctypes.string_at(a._pixels_address, a_sz) + b_bytes = ctypes.string_at(b._pixels_address, b_sz) + return a_bytes == b_bytes + + +class FreeTypeFontTest(unittest.TestCase): + _fixed_path = os.path.join(FONTDIR, "test_fixed.otf") + _sans_path = os.path.join(FONTDIR, "test_sans.ttf") + _mono_path = os.path.join(FONTDIR, "PyGameMono.otf") + _bmp_8_75dpi_path = os.path.join(FONTDIR, "PyGameMono-8.bdf") + _bmp_18_75dpi_path = os.path.join(FONTDIR, "PyGameMono-18-75dpi.bdf") + _bmp_18_100dpi_path = os.path.join(FONTDIR, "PyGameMono-18-100dpi.bdf") + _TEST_FONTS = {} + + @classmethod + def setUpClass(cls): + ft.init() + + # Setup the test fonts. + + # Inconsolata is an open-source font designed by Raph Levien. + # Licensed under the Open Font License. + # http://www.levien.com/type/myfonts/inconsolata.html + cls._TEST_FONTS["fixed"] = ft.Font(cls._fixed_path) + + # Liberation Sans is an open-source font designed by Steve Matteson. + # Licensed under the GNU GPL. + # https://fedorahosted.org/liberation-fonts/ + cls._TEST_FONTS["sans"] = ft.Font(cls._sans_path) + + # A scalable mono test font made for pygame. It contains only + # a few glyphs: '\0', 'A', 'B', 'C', and U+13079. + # It also contains two bitmap sizes: 8.0 X 8.0 and 19.0 X 19.0. + cls._TEST_FONTS["mono"] = ft.Font(cls._mono_path) + + # A fixed size bitmap mono test font made for pygame. + # It contains only a few glyphs: '\0', 'A', 'B', 'C', and U+13079. + # The size is 8.0 X 8.0. + cls._TEST_FONTS["bmp-8-75dpi"] = ft.Font(cls._bmp_8_75dpi_path) + + # A fixed size bitmap mono test font made for pygame. + # It contains only a few glyphs: '\0', 'A', 'B', 'C', and U+13079. + # The size is 8.0 X 8.0. + cls._TEST_FONTS["bmp-18-75dpi"] = ft.Font(cls._bmp_18_75dpi_path) + + # A fixed size bitmap mono test font made for pygame. + # It contains only a few glyphs: '\0', 'A', 'B', 'C', and U+13079. + # The size is 8.0 X 8.0. + cls._TEST_FONTS["bmp-18-100dpi"] = ft.Font(cls._bmp_18_100dpi_path) + + @classmethod + def tearDownClass(cls): + ft.quit() + + def test_freetype_defaultfont(self): + font = ft.Font(None) + self.assertEqual(font.name, "FreeSans") + + def test_freetype_Font_init(self): + self.assertRaises( + FileNotFoundError, ft.Font, os.path.join(FONTDIR, "nonexistent.ttf") + ) + + f = self._TEST_FONTS["sans"] + self.assertIsInstance(f, ft.Font) + + f = self._TEST_FONTS["fixed"] + self.assertIsInstance(f, ft.Font) + + # Test keyword arguments + f = ft.Font(size=22, file=None) + self.assertEqual(f.size, 22) + f = ft.Font(font_index=0, file=None) + self.assertNotEqual(ft.get_default_resolution(), 100) + f = ft.Font(resolution=100, file=None) + self.assertEqual(f.resolution, 100) + f = ft.Font(ucs4=True, file=None) + self.assertTrue(f.ucs4) + self.assertRaises(OverflowError, ft.Font, file=None, size=(max_point_size + 1)) + self.assertRaises(OverflowError, ft.Font, file=None, size=-1) + + f = ft.Font(None, size=24) + self.assertTrue(f.height > 0) + self.assertRaises( + FileNotFoundError, f.__init__, os.path.join(FONTDIR, "nonexistent.ttf") + ) + + # Test attribute preservation during reinitalization + f = ft.Font(self._sans_path, size=24, ucs4=True) + self.assertEqual(f.name, "Liberation Sans") + self.assertTrue(f.scalable) + self.assertFalse(f.fixed_width) + self.assertTrue(f.antialiased) + self.assertFalse(f.oblique) + self.assertTrue(f.ucs4) + f.antialiased = False + f.oblique = True + f.__init__(self._mono_path) + self.assertEqual(f.name, "PyGameMono") + self.assertTrue(f.scalable) + self.assertTrue(f.fixed_width) + self.assertFalse(f.antialiased) + self.assertTrue(f.oblique) + self.assertTrue(f.ucs4) + + # For a bitmap font, the size is automatically set to the first + # size in the available sizes list. + f = ft.Font(self._bmp_8_75dpi_path) + sizes = f.get_sizes() + self.assertEqual(len(sizes), 1) + size_pt, width_px, height_px, x_ppem, y_ppem = sizes[0] + self.assertEqual(f.size, (x_ppem, y_ppem)) + f.__init__(self._bmp_8_75dpi_path, size=12) + self.assertEqual(f.size, 12.0) + + @unittest.skipIf(IS_PYPY, "PyPy doesn't use refcounting") + def test_freetype_Font_dealloc(self): + import sys + + handle = open(self._sans_path, "rb") + + def load_font(): + tempFont = ft.Font(handle) + + try: + load_font() + + self.assertEqual(sys.getrefcount(handle), 2) + finally: + # Ensures file is closed even if test fails. + handle.close() + + def test_freetype_Font_kerning(self): + """Ensures get/set works with the kerning property.""" + ft_font = self._TEST_FONTS["sans"] + + # Test default is disabled. + self.assertFalse(ft_font.kerning) + + # Test setting to True. + ft_font.kerning = True + + self.assertTrue(ft_font.kerning) + + # Test setting to False. + ft_font.kerning = False + + self.assertFalse(ft_font.kerning) + + def test_freetype_Font_kerning__enabled(self): + """Ensures exceptions are not raised when calling freetype methods + while kerning is enabled. + + Note: This does not test what changes occur to a rendered font by + having kerning enabled. + + Related to issue #367. + """ + surface = pygame.Surface((10, 10), 0, 32) + TEST_TEXT = "Freetype Font" + ft_font = self._TEST_FONTS["bmp-8-75dpi"] + + ft_font.kerning = True + + # Call different methods to ensure they don't raise an exception. + metrics = ft_font.get_metrics(TEST_TEXT) + self.assertIsInstance(metrics, list) + + rect = ft_font.get_rect(TEST_TEXT) + self.assertIsInstance(rect, pygame.Rect) + + font_surf, rect = ft_font.render(TEST_TEXT) + self.assertIsInstance(font_surf, pygame.Surface) + self.assertIsInstance(rect, pygame.Rect) + + rect = ft_font.render_to(surface, (0, 0), TEST_TEXT) + self.assertIsInstance(rect, pygame.Rect) + + buf, size = ft_font.render_raw(TEST_TEXT) + self.assertIsInstance(buf, bytes) + self.assertIsInstance(size, tuple) + + rect = ft_font.render_raw_to(surface.get_view("2"), TEST_TEXT) + self.assertIsInstance(rect, pygame.Rect) + + def test_freetype_Font_scalable(self): + f = self._TEST_FONTS["sans"] + self.assertTrue(f.scalable) + + self.assertRaises(RuntimeError, lambda: nullfont().scalable) + + def test_freetype_Font_fixed_width(self): + f = self._TEST_FONTS["sans"] + self.assertFalse(f.fixed_width) + + f = self._TEST_FONTS["mono"] + self.assertTrue(f.fixed_width) + + self.assertRaises(RuntimeError, lambda: nullfont().fixed_width) + + def test_freetype_Font_fixed_sizes(self): + f = self._TEST_FONTS["sans"] + self.assertEqual(f.fixed_sizes, 0) + f = self._TEST_FONTS["bmp-8-75dpi"] + self.assertEqual(f.fixed_sizes, 1) + f = self._TEST_FONTS["mono"] + self.assertEqual(f.fixed_sizes, 2) + + def test_freetype_Font_get_sizes(self): + f = self._TEST_FONTS["sans"] + szlist = f.get_sizes() + self.assertIsInstance(szlist, list) + self.assertEqual(len(szlist), 0) + + f = self._TEST_FONTS["bmp-8-75dpi"] + szlist = f.get_sizes() + self.assertIsInstance(szlist, list) + self.assertEqual(len(szlist), 1) + + size8 = szlist[0] + self.assertIsInstance(size8[0], int) + self.assertEqual(size8[0], 8) + self.assertIsInstance(size8[1], int) + self.assertIsInstance(size8[2], int) + self.assertIsInstance(size8[3], float) + self.assertEqual(int(size8[3] * 64.0 + 0.5), 8 * 64) + self.assertIsInstance(size8[4], float) + self.assertEqual(int(size8[4] * 64.0 + 0.5), 8 * 64) + + f = self._TEST_FONTS["mono"] + szlist = f.get_sizes() + self.assertIsInstance(szlist, list) + self.assertEqual(len(szlist), 2) + + size8 = szlist[0] + self.assertEqual(size8[3], 8) + self.assertEqual(int(size8[3] * 64.0 + 0.5), 8 * 64) + self.assertEqual(int(size8[4] * 64.0 + 0.5), 8 * 64) + + size19 = szlist[1] + self.assertEqual(size19[3], 19) + self.assertEqual(int(size19[3] * 64.0 + 0.5), 19 * 64) + self.assertEqual(int(size19[4] * 64.0 + 0.5), 19 * 64) + + def test_freetype_Font_use_bitmap_strikes(self): + f = self._TEST_FONTS["mono"] + try: + # use_bitmap_strikes == True + # + self.assertTrue(f.use_bitmap_strikes) + + # bitmap compatible properties + s_strike, sz = f.render_raw("A", size=19) + try: + f.vertical = True + s_strike_vert, sz = f.render_raw("A", size=19) + finally: + f.vertical = False + try: + f.wide = True + s_strike_wide, sz = f.render_raw("A", size=19) + finally: + f.wide = False + try: + f.underline = True + s_strike_underline, sz = f.render_raw("A", size=19) + finally: + f.underline = False + + # bitmap incompatible properties + s_strike_rot45, sz = f.render_raw("A", size=19, rotation=45) + try: + f.strong = True + s_strike_strong, sz = f.render_raw("A", size=19) + finally: + f.strong = False + try: + f.oblique = True + s_strike_oblique, sz = f.render_raw("A", size=19) + finally: + f.oblique = False + + # compare with use_bitmap_strikes == False + # + f.use_bitmap_strikes = False + self.assertFalse(f.use_bitmap_strikes) + + # bitmap compatible properties + s_outline, sz = f.render_raw("A", size=19) + self.assertNotEqual(s_outline, s_strike) + try: + f.vertical = True + s_outline, sz = f.render_raw("A", size=19) + self.assertNotEqual(s_outline, s_strike_vert) + finally: + f.vertical = False + try: + f.wide = True + s_outline, sz = f.render_raw("A", size=19) + self.assertNotEqual(s_outline, s_strike_wide) + finally: + f.wide = False + try: + f.underline = True + s_outline, sz = f.render_raw("A", size=19) + self.assertNotEqual(s_outline, s_strike_underline) + finally: + f.underline = False + + # bitmap incompatible properties + s_outline, sz = f.render_raw("A", size=19, rotation=45) + self.assertEqual(s_outline, s_strike_rot45) + try: + f.strong = True + s_outline, sz = f.render_raw("A", size=19) + self.assertEqual(s_outline, s_strike_strong) + finally: + f.strong = False + try: + f.oblique = True + s_outline, sz = f.render_raw("A", size=19) + self.assertEqual(s_outline, s_strike_oblique) + finally: + f.oblique = False + finally: + f.use_bitmap_strikes = True + + def test_freetype_Font_bitmap_files(self): + """Ensure bitmap file restrictions are caught""" + f = self._TEST_FONTS["bmp-8-75dpi"] + f_null = nullfont() + s = pygame.Surface((10, 10), 0, 32) + a = s.get_view("3") + + exception = AttributeError + self.assertRaises(exception, setattr, f, "strong", True) + self.assertRaises(exception, setattr, f, "oblique", True) + self.assertRaises(exception, setattr, f, "style", ft.STYLE_STRONG) + self.assertRaises(exception, setattr, f, "style", ft.STYLE_OBLIQUE) + exception = RuntimeError + self.assertRaises(exception, setattr, f_null, "strong", True) + self.assertRaises(exception, setattr, f_null, "oblique", True) + self.assertRaises(exception, setattr, f_null, "style", ft.STYLE_STRONG) + self.assertRaises(exception, setattr, f_null, "style", ft.STYLE_OBLIQUE) + exception = ValueError + self.assertRaises(exception, f.render, "A", (0, 0, 0), size=8, rotation=1) + self.assertRaises( + exception, f.render, "A", (0, 0, 0), size=8, style=ft.STYLE_OBLIQUE + ) + self.assertRaises( + exception, f.render, "A", (0, 0, 0), size=8, style=ft.STYLE_STRONG + ) + self.assertRaises(exception, f.render_raw, "A", size=8, rotation=1) + self.assertRaises(exception, f.render_raw, "A", size=8, style=ft.STYLE_OBLIQUE) + self.assertRaises(exception, f.render_raw, "A", size=8, style=ft.STYLE_STRONG) + self.assertRaises( + exception, f.render_to, s, (0, 0), "A", (0, 0, 0), size=8, rotation=1 + ) + self.assertRaises( + exception, + f.render_to, + s, + (0, 0), + "A", + (0, 0, 0), + size=8, + style=ft.STYLE_OBLIQUE, + ) + self.assertRaises( + exception, + f.render_to, + s, + (0, 0), + "A", + (0, 0, 0), + size=8, + style=ft.STYLE_STRONG, + ) + self.assertRaises(exception, f.render_raw_to, a, "A", size=8, rotation=1) + self.assertRaises( + exception, f.render_raw_to, a, "A", size=8, style=ft.STYLE_OBLIQUE + ) + self.assertRaises( + exception, f.render_raw_to, a, "A", size=8, style=ft.STYLE_STRONG + ) + self.assertRaises(exception, f.get_rect, "A", size=8, rotation=1) + self.assertRaises(exception, f.get_rect, "A", size=8, style=ft.STYLE_OBLIQUE) + self.assertRaises(exception, f.get_rect, "A", size=8, style=ft.STYLE_STRONG) + + # Unsupported point size + exception = pygame.error + self.assertRaises(exception, f.get_rect, "A", size=42) + self.assertRaises(exception, f.get_metrics, "A", size=42) + self.assertRaises(exception, f.get_sized_ascender, 42) + self.assertRaises(exception, f.get_sized_descender, 42) + self.assertRaises(exception, f.get_sized_height, 42) + self.assertRaises(exception, f.get_sized_glyph_height, 42) + + def test_freetype_Font_get_metrics(self): + font = self._TEST_FONTS["sans"] + + metrics = font.get_metrics("ABCD", size=24) + self.assertEqual(len(metrics), len("ABCD")) + self.assertIsInstance(metrics, list) + + for metrics_tuple in metrics: + self.assertIsInstance(metrics_tuple, tuple, metrics_tuple) + self.assertEqual(len(metrics_tuple), 6) + + for m in metrics_tuple[:4]: + self.assertIsInstance(m, int) + + for m in metrics_tuple[4:]: + self.assertIsInstance(m, float) + + # test for empty string + metrics = font.get_metrics("", size=24) + self.assertEqual(metrics, []) + + # test for invalid string + self.assertRaises(TypeError, font.get_metrics, 24, 24) + + # raises exception when uninitialized + self.assertRaises(RuntimeError, nullfont().get_metrics, "a", size=24) + + def test_freetype_Font_get_rect(self): + font = self._TEST_FONTS["sans"] + + def test_rect(r): + self.assertIsInstance(r, pygame.Rect) + + rect_default = font.get_rect("ABCDabcd", size=24) + test_rect(rect_default) + self.assertTrue(rect_default.size > (0, 0)) + self.assertTrue(rect_default.width > rect_default.height) + + rect_bigger = font.get_rect("ABCDabcd", size=32) + test_rect(rect_bigger) + self.assertTrue(rect_bigger.size > rect_default.size) + + rect_strong = font.get_rect("ABCDabcd", size=24, style=ft.STYLE_STRONG) + test_rect(rect_strong) + self.assertTrue(rect_strong.size > rect_default.size) + + font.vertical = True + rect_vert = font.get_rect("ABCDabcd", size=24) + test_rect(rect_vert) + self.assertTrue(rect_vert.width < rect_vert.height) + font.vertical = False + + rect_oblique = font.get_rect("ABCDabcd", size=24, style=ft.STYLE_OBLIQUE) + test_rect(rect_oblique) + self.assertTrue(rect_oblique.width > rect_default.width) + self.assertTrue(rect_oblique.height == rect_default.height) + + rect_under = font.get_rect("ABCDabcd", size=24, style=ft.STYLE_UNDERLINE) + test_rect(rect_under) + self.assertTrue(rect_under.width == rect_default.width) + self.assertTrue(rect_under.height > rect_default.height) + + # Rect size should change if UTF surrogate pairs are treated as + # one code point or two. + ufont = self._TEST_FONTS["mono"] + rect_utf32 = ufont.get_rect("\U00013079", size=24) + rect_utf16 = ufont.get_rect("\uD80C\uDC79", size=24) + self.assertEqual(rect_utf16, rect_utf32) + ufont.ucs4 = True + try: + rect_utf16 = ufont.get_rect("\uD80C\uDC79", size=24) + finally: + ufont.ucs4 = False + self.assertNotEqual(rect_utf16, rect_utf32) + + self.assertRaises(RuntimeError, nullfont().get_rect, "a", size=24) + + # text stretching + rect12 = font.get_rect("A", size=12.0) + rect24 = font.get_rect("A", size=24.0) + rect_x = font.get_rect("A", size=(24.0, 12.0)) + self.assertEqual(rect_x.width, rect24.width) + self.assertEqual(rect_x.height, rect12.height) + rect_y = font.get_rect("A", size=(12.0, 24.0)) + self.assertEqual(rect_y.width, rect12.width) + self.assertEqual(rect_y.height, rect24.height) + + def test_freetype_Font_height(self): + f = self._TEST_FONTS["sans"] + self.assertEqual(f.height, 2355) + + f = self._TEST_FONTS["fixed"] + self.assertEqual(f.height, 1100) + + self.assertRaises(RuntimeError, lambda: nullfont().height) + + def test_freetype_Font_name(self): + f = self._TEST_FONTS["sans"] + self.assertEqual(f.name, "Liberation Sans") + + f = self._TEST_FONTS["fixed"] + self.assertEqual(f.name, "Inconsolata") + + nf = nullfont() + self.assertEqual(nf.name, repr(nf)) + + def test_freetype_Font_size(self): + f = ft.Font(None, size=12) + self.assertEqual(f.size, 12) + f.size = 22 + self.assertEqual(f.size, 22) + f.size = 0 + self.assertEqual(f.size, 0) + f.size = max_point_size + self.assertEqual(f.size, max_point_size) + f.size = 6.5 + self.assertEqual(f.size, 6.5) + f.size = max_point_size_f + self.assertEqual(f.size, max_point_size_f) + self.assertRaises(OverflowError, setattr, f, "size", -1) + self.assertRaises(OverflowError, setattr, f, "size", (max_point_size + 1)) + + f.size = 24.0, 0 + size = f.size + self.assertIsInstance(size, float) + self.assertEqual(size, 24.0) + + f.size = 16, 16 + size = f.size + self.assertIsInstance(size, tuple) + self.assertEqual(len(size), 2) + + x, y = size + self.assertIsInstance(x, float) + self.assertEqual(x, 16.0) + self.assertIsInstance(y, float) + self.assertEqual(y, 16.0) + + f.size = 20.5, 22.25 + x, y = f.size + self.assertEqual(x, 20.5) + self.assertEqual(y, 22.25) + + f.size = 0, 0 + size = f.size + self.assertIsInstance(size, float) + self.assertEqual(size, 0.0) + self.assertRaises(ValueError, setattr, f, "size", (0, 24.0)) + self.assertRaises(TypeError, setattr, f, "size", (24.0,)) + self.assertRaises(TypeError, setattr, f, "size", (24.0, 0, 0)) + self.assertRaises(TypeError, setattr, f, "size", (24.0j, 24.0)) + self.assertRaises(TypeError, setattr, f, "size", (24.0, 24.0j)) + self.assertRaises(OverflowError, setattr, f, "size", (-1, 16)) + self.assertRaises(OverflowError, setattr, f, "size", (max_point_size + 1, 16)) + self.assertRaises(OverflowError, setattr, f, "size", (16, -1)) + self.assertRaises(OverflowError, setattr, f, "size", (16, max_point_size + 1)) + + # bitmap files with identical point size but differing ppems. + f75 = self._TEST_FONTS["bmp-18-75dpi"] + sizes = f75.get_sizes() + self.assertEqual(len(sizes), 1) + size_pt, width_px, height_px, x_ppem, y_ppem = sizes[0] + self.assertEqual(size_pt, 18) + self.assertEqual(x_ppem, 19.0) + self.assertEqual(y_ppem, 19.0) + rect = f75.get_rect("A", size=18) + rect = f75.get_rect("A", size=19) + rect = f75.get_rect("A", size=(19.0, 19.0)) + self.assertRaises(pygame.error, f75.get_rect, "A", size=17) + f100 = self._TEST_FONTS["bmp-18-100dpi"] + sizes = f100.get_sizes() + self.assertEqual(len(sizes), 1) + size_pt, width_px, height_px, x_ppem, y_ppem = sizes[0] + self.assertEqual(size_pt, 18) + self.assertEqual(x_ppem, 25.0) + self.assertEqual(y_ppem, 25.0) + rect = f100.get_rect("A", size=18) + rect = f100.get_rect("A", size=25) + rect = f100.get_rect("A", size=(25.0, 25.0)) + self.assertRaises(pygame.error, f100.get_rect, "A", size=17) + + def test_freetype_Font_rotation(self): + test_angles = [ + (30, 30), + (360, 0), + (390, 30), + (720, 0), + (764, 44), + (-30, 330), + (-360, 0), + (-390, 330), + (-720, 0), + (-764, 316), + ] + + f = ft.Font(None) + self.assertEqual(f.rotation, 0) + for r, r_reduced in test_angles: + f.rotation = r + self.assertEqual( + f.rotation, + r_reduced, + "for angle %d: %d != %d" % (r, f.rotation, r_reduced), + ) + self.assertRaises(TypeError, setattr, f, "rotation", "12") + + def test_freetype_Font_render_to(self): + # Rendering to an existing target surface is equivalent to + # blitting a surface returned by Font.render with the target. + font = self._TEST_FONTS["sans"] + + surf = pygame.Surface((800, 600)) + color = pygame.Color(0, 0, 0) + + rrect = font.render_to(surf, (32, 32), "FoobarBaz", color, None, size=24) + self.assertIsInstance(rrect, pygame.Rect) + self.assertEqual(rrect.topleft, (32, 32)) + self.assertNotEqual(rrect.bottomright, (32, 32)) + + rcopy = rrect.copy() + rcopy.topleft = (32, 32) + self.assertTrue(surf.get_rect().contains(rcopy)) + + rect = pygame.Rect(20, 20, 2, 2) + rrect = font.render_to(surf, rect, "FoobarBax", color, None, size=24) + self.assertEqual(rect.topleft, rrect.topleft) + self.assertNotEqual(rrect.size, rect.size) + rrect = font.render_to(surf, (20.1, 18.9), "FoobarBax", color, None, size=24) + + rrect = font.render_to(surf, rect, "", color, None, size=24) + self.assertFalse(rrect) + self.assertEqual(rrect.height, font.get_sized_height(24)) + + # invalid surf test + self.assertRaises(TypeError, font.render_to, "not a surface", "text", color) + self.assertRaises(TypeError, font.render_to, pygame.Surface, "text", color) + + # invalid dest test + for dest in [ + None, + 0, + "a", + "ab", + (), + (1,), + ("a", 2), + (1, "a"), + (1 + 2j, 2), + (1, 1 + 2j), + (1, int), + (int, 1), + ]: + self.assertRaises( + TypeError, font.render_to, surf, dest, "foobar", color, size=24 + ) + + # misc parameter test + self.assertRaises(ValueError, font.render_to, surf, (0, 0), "foobar", color) + self.assertRaises( + TypeError, font.render_to, surf, (0, 0), "foobar", color, 2.3, size=24 + ) + self.assertRaises( + ValueError, + font.render_to, + surf, + (0, 0), + "foobar", + color, + None, + style=42, + size=24, + ) + self.assertRaises( + TypeError, + font.render_to, + surf, + (0, 0), + "foobar", + color, + None, + style=None, + size=24, + ) + self.assertRaises( + ValueError, + font.render_to, + surf, + (0, 0), + "foobar", + color, + None, + style=97, + size=24, + ) + + def test_freetype_Font_render(self): + font = self._TEST_FONTS["sans"] + + surf = pygame.Surface((800, 600)) + color = pygame.Color(0, 0, 0) + + rend = font.render("FoobarBaz", pygame.Color(0, 0, 0), None, size=24) + self.assertIsInstance(rend, tuple) + self.assertEqual(len(rend), 2) + self.assertIsInstance(rend[0], pygame.Surface) + self.assertIsInstance(rend[1], pygame.Rect) + self.assertEqual(rend[0].get_rect().size, rend[1].size) + + s, r = font.render("", pygame.Color(0, 0, 0), None, size=24) + self.assertEqual(r.width, 0) + self.assertEqual(r.height, font.get_sized_height(24)) + self.assertEqual(s.get_size(), r.size) + self.assertEqual(s.get_bitsize(), 32) + + # misc parameter test + self.assertRaises(ValueError, font.render, "foobar", color) + self.assertRaises(TypeError, font.render, "foobar", color, 2.3, size=24) + self.assertRaises( + ValueError, font.render, "foobar", color, None, style=42, size=24 + ) + self.assertRaises( + TypeError, font.render, "foobar", color, None, style=None, size=24 + ) + self.assertRaises( + ValueError, font.render, "foobar", color, None, style=97, size=24 + ) + + # valid surrogate pairs + font2 = self._TEST_FONTS["mono"] + ucs4 = font2.ucs4 + try: + font2.ucs4 = False + rend1 = font2.render("\uD80C\uDC79", color, size=24) + rend2 = font2.render("\U00013079", color, size=24) + self.assertEqual(rend1[1], rend2[1]) + font2.ucs4 = True + rend1 = font2.render("\uD80C\uDC79", color, size=24) + self.assertNotEqual(rend1[1], rend2[1]) + finally: + font2.ucs4 = ucs4 + + # malformed surrogate pairs + self.assertRaises(UnicodeEncodeError, font.render, "\uD80C", color, size=24) + self.assertRaises(UnicodeEncodeError, font.render, "\uDCA7", color, size=24) + self.assertRaises( + UnicodeEncodeError, font.render, "\uD7FF\uDCA7", color, size=24 + ) + self.assertRaises( + UnicodeEncodeError, font.render, "\uDC00\uDCA7", color, size=24 + ) + self.assertRaises( + UnicodeEncodeError, font.render, "\uD80C\uDBFF", color, size=24 + ) + self.assertRaises( + UnicodeEncodeError, font.render, "\uD80C\uE000", color, size=24 + ) + + # raises exception when uninitialized + self.assertRaises(RuntimeError, nullfont().render, "a", (0, 0, 0), size=24) + + # Confirm the correct glyphs are returned for a couple of + # unicode code points, 'A' and '\U00023079'. For each code point + # the rendered glyph is compared with an image of glyph bitmap + # as exported by FontForge. + path = os.path.join(FONTDIR, "A_PyGameMono-8.png") + A = pygame.image.load(path) + path = os.path.join(FONTDIR, "u13079_PyGameMono-8.png") + u13079 = pygame.image.load(path) + + font = self._TEST_FONTS["mono"] + font.ucs4 = False + A_rendered, r = font.render("A", bgcolor=pygame.Color("white"), size=8) + u13079_rendered, r = font.render( + "\U00013079", bgcolor=pygame.Color("white"), size=8 + ) + + # before comparing the surfaces, make sure they are the same + # pixel format. Use 32-bit SRCALPHA to avoid row padding and + # undefined bytes (the alpha byte will be set to 255.) + bitmap = pygame.Surface(A.get_size(), pygame.SRCALPHA, 32) + bitmap.blit(A, (0, 0)) + rendering = pygame.Surface(A_rendered.get_size(), pygame.SRCALPHA, 32) + rendering.blit(A_rendered, (0, 0)) + self.assertTrue(surf_same_image(rendering, bitmap)) + bitmap = pygame.Surface(u13079.get_size(), pygame.SRCALPHA, 32) + bitmap.blit(u13079, (0, 0)) + rendering = pygame.Surface(u13079_rendered.get_size(), pygame.SRCALPHA, 32) + rendering.blit(u13079_rendered, (0, 0)) + self.assertTrue(surf_same_image(rendering, bitmap)) + + def test_freetype_Font_render_mono(self): + font = self._TEST_FONTS["sans"] + color = pygame.Color("black") + colorkey = pygame.Color("white") + text = "." + + save_antialiased = font.antialiased + font.antialiased = False + try: + surf, r = font.render(text, color, size=24) + self.assertEqual(surf.get_bitsize(), 8) + flags = surf.get_flags() + self.assertTrue(flags & pygame.SRCCOLORKEY) + self.assertFalse(flags & (pygame.SRCALPHA | pygame.HWSURFACE)) + self.assertEqual(surf.get_colorkey(), colorkey) + self.assertIsNone(surf.get_alpha()) + + translucent_color = pygame.Color(*color) + translucent_color.a = 55 + surf, r = font.render(text, translucent_color, size=24) + self.assertEqual(surf.get_bitsize(), 8) + flags = surf.get_flags() + self.assertTrue(flags & (pygame.SRCCOLORKEY | pygame.SRCALPHA)) + self.assertFalse(flags & pygame.HWSURFACE) + self.assertEqual(surf.get_colorkey(), colorkey) + self.assertEqual(surf.get_alpha(), translucent_color.a) + + surf, r = font.render(text, color, colorkey, size=24) + self.assertEqual(surf.get_bitsize(), 32) + finally: + font.antialiased = save_antialiased + + def test_freetype_Font_render_to_mono(self): + # Blitting is done in two stages. First the target is alpha filled + # with the background color, if any. Second, the foreground + # color is alpha blitted to the background. + font = self._TEST_FONTS["sans"] + text = " ." + rect = font.get_rect(text, size=24) + size = rect.size + fg = pygame.Surface((1, 1), pygame.SRCALPHA, 32) + bg = pygame.Surface((1, 1), pygame.SRCALPHA, 32) + surrogate = pygame.Surface((1, 1), pygame.SRCALPHA, 32) + surfaces = [ + pygame.Surface(size, 0, 8), + pygame.Surface(size, 0, 16), + pygame.Surface(size, pygame.SRCALPHA, 16), + pygame.Surface(size, 0, 24), + pygame.Surface(size, 0, 32), + pygame.Surface(size, pygame.SRCALPHA, 32), + ] + fg_colors = [ + surfaces[0].get_palette_at(2), + surfaces[1].unmap_rgb(surfaces[1].map_rgb((128, 64, 200))), + surfaces[2].unmap_rgb(surfaces[2].map_rgb((99, 0, 100, 64))), + (128, 97, 213), + (128, 97, 213), + (128, 97, 213, 60), + ] + fg_colors = [pygame.Color(*c) for c in fg_colors] + self.assertEqual(len(surfaces), len(fg_colors)) # integrity check + bg_colors = [ + surfaces[0].get_palette_at(4), + surfaces[1].unmap_rgb(surfaces[1].map_rgb((220, 20, 99))), + surfaces[2].unmap_rgb(surfaces[2].map_rgb((55, 200, 0, 86))), + (255, 120, 13), + (255, 120, 13), + (255, 120, 13, 180), + ] + bg_colors = [pygame.Color(*c) for c in bg_colors] + self.assertEqual(len(surfaces), len(bg_colors)) # integrity check + + save_antialiased = font.antialiased + font.antialiased = False + try: + fill_color = pygame.Color("black") + for i, surf in enumerate(surfaces): + surf.fill(fill_color) + fg_color = fg_colors[i] + fg.set_at((0, 0), fg_color) + surf.blit(fg, (0, 0)) + r_fg_color = surf.get_at((0, 0)) + surf.set_at((0, 0), fill_color) + rrect = font.render_to(surf, (0, 0), text, fg_color, size=24) + bottomleft = 0, rrect.height - 1 + self.assertEqual( + surf.get_at(bottomleft), + fill_color, + "Position: {}. Depth: {}." + " fg_color: {}.".format(bottomleft, surf.get_bitsize(), fg_color), + ) + bottomright = rrect.width - 1, rrect.height - 1 + self.assertEqual( + surf.get_at(bottomright), + r_fg_color, + "Position: {}. Depth: {}." + " fg_color: {}.".format(bottomright, surf.get_bitsize(), fg_color), + ) + for i, surf in enumerate(surfaces): + surf.fill(fill_color) + fg_color = fg_colors[i] + bg_color = bg_colors[i] + bg.set_at((0, 0), bg_color) + fg.set_at((0, 0), fg_color) + if surf.get_bitsize() == 24: + # For a 24 bit target surface test against Pygame's alpha + # blit as there appears to be a problem with SDL's alpha + # blit: + # + # self.assertEqual(surf.get_at(bottomright), r_fg_color) + # + # raises + # + # AssertionError: (128, 97, 213, 255) != (129, 98, 213, 255) + # + surrogate.set_at((0, 0), fill_color) + surrogate.blit(bg, (0, 0)) + r_bg_color = surrogate.get_at((0, 0)) + surrogate.blit(fg, (0, 0)) + r_fg_color = surrogate.get_at((0, 0)) + else: + # Surface blit values for comparison. + surf.blit(bg, (0, 0)) + r_bg_color = surf.get_at((0, 0)) + surf.blit(fg, (0, 0)) + r_fg_color = surf.get_at((0, 0)) + surf.set_at((0, 0), fill_color) + rrect = font.render_to(surf, (0, 0), text, fg_color, bg_color, size=24) + bottomleft = 0, rrect.height - 1 + self.assertEqual(surf.get_at(bottomleft), r_bg_color) + bottomright = rrect.width - 1, rrect.height - 1 + self.assertEqual(surf.get_at(bottomright), r_fg_color) + finally: + font.antialiased = save_antialiased + + def test_freetype_Font_render_raw(self): + font = self._TEST_FONTS["sans"] + + text = "abc" + size = font.get_rect(text, size=24).size + rend = font.render_raw(text, size=24) + self.assertIsInstance(rend, tuple) + self.assertEqual(len(rend), 2) + + r, s = rend + self.assertIsInstance(r, bytes) + self.assertIsInstance(s, tuple) + self.assertTrue(len(s), 2) + + w, h = s + self.assertIsInstance(w, int) + self.assertIsInstance(h, int) + self.assertEqual(s, size) + self.assertEqual(len(r), w * h) + + r, (w, h) = font.render_raw("", size=24) + self.assertEqual(w, 0) + self.assertEqual(h, font.height) + self.assertEqual(len(r), 0) + + # bug with decenders: this would crash + rend = font.render_raw("render_raw", size=24) + + # bug with non-printable characters: this would cause a crash + # because the text length was not adjusted for skipped characters. + text = "".join([chr(i) for i in range(31, 64)]) + rend = font.render_raw(text, size=10) + + def test_freetype_Font_render_raw_to(self): + # This only checks that blits do not crash. It needs to check: + # - int values + # - invert option + # + + font = self._TEST_FONTS["sans"] + text = "abc" + + # No frills antialiased render to int1 (__render_glyph_INT) + srect = font.get_rect(text, size=24) + surf = pygame.Surface(srect.size, 0, 8) + rrect = font.render_raw_to(surf.get_view("2"), text, size=24) + self.assertEqual(rrect, srect) + + for bpp in [24, 32]: + surf = pygame.Surface(srect.size, 0, bpp) + rrect = font.render_raw_to(surf.get_view("r"), text, size=24) + self.assertEqual(rrect, srect) + + # Underlining to int1 (__fill_glyph_INT) + srect = font.get_rect(text, size=24, style=ft.STYLE_UNDERLINE) + surf = pygame.Surface(srect.size, 0, 8) + rrect = font.render_raw_to( + surf.get_view("2"), text, size=24, style=ft.STYLE_UNDERLINE + ) + self.assertEqual(rrect, srect) + + for bpp in [24, 32]: + surf = pygame.Surface(srect.size, 0, bpp) + rrect = font.render_raw_to( + surf.get_view("r"), text, size=24, style=ft.STYLE_UNDERLINE + ) + self.assertEqual(rrect, srect) + + # Unaliased (mono) rendering to int1 (__render_glyph_MONO_as_INT) + font.antialiased = False + try: + srect = font.get_rect(text, size=24) + surf = pygame.Surface(srect.size, 0, 8) + rrect = font.render_raw_to(surf.get_view("2"), text, size=24) + self.assertEqual(rrect, srect) + + for bpp in [24, 32]: + surf = pygame.Surface(srect.size, 0, bpp) + rrect = font.render_raw_to(surf.get_view("r"), text, size=24) + self.assertEqual(rrect, srect) + finally: + font.antialiased = True + + # Antialiased render to ints sized greater than 1 byte + # (__render_glyph_INT) + srect = font.get_rect(text, size=24) + + for bpp in [16, 24, 32]: + surf = pygame.Surface(srect.size, 0, bpp) + rrect = font.render_raw_to(surf.get_view("2"), text, size=24) + self.assertEqual(rrect, srect) + + # Underline render to ints sized greater than 1 byte + # (__fill_glyph_INT) + srect = font.get_rect(text, size=24, style=ft.STYLE_UNDERLINE) + + for bpp in [16, 24, 32]: + surf = pygame.Surface(srect.size, 0, bpp) + rrect = font.render_raw_to( + surf.get_view("2"), text, size=24, style=ft.STYLE_UNDERLINE + ) + self.assertEqual(rrect, srect) + + # Unaliased (mono) rendering to ints greater than 1 byte + # (__render_glyph_MONO_as_INT) + font.antialiased = False + try: + srect = font.get_rect(text, size=24) + + for bpp in [16, 24, 32]: + surf = pygame.Surface(srect.size, 0, bpp) + rrect = font.render_raw_to(surf.get_view("2"), text, size=24) + self.assertEqual(rrect, srect) + finally: + font.antialiased = True + + # Invalid dest parameter test. + srect = font.get_rect(text, size=24) + surf_buf = pygame.Surface(srect.size, 0, 32).get_view("2") + + for dest in [ + 0, + "a", + "ab", + (), + (1,), + ("a", 2), + (1, "a"), + (1 + 2j, 2), + (1, 1 + 2j), + (1, int), + (int, 1), + ]: + self.assertRaises( + TypeError, font.render_raw_to, surf_buf, text, dest, size=24 + ) + + def test_freetype_Font_text_is_None_with_arr(self): + f = ft.Font(self._sans_path, 36) + f.style = ft.STYLE_NORMAL + f.rotation = 0 + text = "ABCD" + + # reference values + get_rect = f.get_rect(text) + f.vertical = True + get_rect_vert = f.get_rect(text) + + self.assertTrue(get_rect_vert.width < get_rect.width) + self.assertTrue(get_rect_vert.height > get_rect.height) + f.vertical = False + render_to_surf = pygame.Surface(get_rect.size, pygame.SRCALPHA, 32) + + if IS_PYPY: + return + + arr = arrinter.Array(get_rect.size, "u", 1) + render = f.render(text, (0, 0, 0)) + render_to = f.render_to(render_to_surf, (0, 0), text, (0, 0, 0)) + render_raw = f.render_raw(text) + render_raw_to = f.render_raw_to(arr, text) + + # comparisons + surf = pygame.Surface(get_rect.size, pygame.SRCALPHA, 32) + self.assertEqual(f.get_rect(None), get_rect) + s, r = f.render(None, (0, 0, 0)) + self.assertEqual(r, render[1]) + self.assertTrue(surf_same_image(s, render[0])) + r = f.render_to(surf, (0, 0), None, (0, 0, 0)) + self.assertEqual(r, render_to) + self.assertTrue(surf_same_image(surf, render_to_surf)) + px, sz = f.render_raw(None) + self.assertEqual(sz, render_raw[1]) + self.assertEqual(px, render_raw[0]) + sz = f.render_raw_to(arr, None) + self.assertEqual(sz, render_raw_to) + + def test_freetype_Font_text_is_None(self): + f = ft.Font(self._sans_path, 36) + f.style = ft.STYLE_NORMAL + f.rotation = 0 + text = "ABCD" + + # reference values + get_rect = f.get_rect(text) + f.vertical = True + get_rect_vert = f.get_rect(text) + + # vertical: trigger glyph positioning. + f.vertical = True + r = f.get_rect(None) + self.assertEqual(r, get_rect_vert) + f.vertical = False + + # wide style: trigger glyph reload + r = f.get_rect(None, style=ft.STYLE_WIDE) + self.assertEqual(r.height, get_rect.height) + self.assertTrue(r.width > get_rect.width) + r = f.get_rect(None) + self.assertEqual(r, get_rect) + + # rotated: trigger glyph reload + r = f.get_rect(None, rotation=90) + self.assertEqual(r.width, get_rect.height) + self.assertEqual(r.height, get_rect.width) + + # this method will not support None text + self.assertRaises(TypeError, f.get_metrics, None) + + def test_freetype_Font_fgcolor(self): + f = ft.Font(self._bmp_8_75dpi_path) + notdef = "\0" # the PyGameMono .notdef glyph has a pixel at (0, 0) + f.origin = False + f.pad = False + black = pygame.Color("black") # initial color + green = pygame.Color("green") + alpha128 = pygame.Color(10, 20, 30, 128) + + c = f.fgcolor + self.assertIsInstance(c, pygame.Color) + self.assertEqual(c, black) + + s, r = f.render(notdef) + self.assertEqual(s.get_at((0, 0)), black) + + f.fgcolor = green + self.assertEqual(f.fgcolor, green) + + s, r = f.render(notdef) + self.assertEqual(s.get_at((0, 0)), green) + + f.fgcolor = alpha128 + s, r = f.render(notdef) + self.assertEqual(s.get_at((0, 0)), alpha128) + + surf = pygame.Surface(f.get_rect(notdef).size, pygame.SRCALPHA, 32) + f.render_to(surf, (0, 0), None) + self.assertEqual(surf.get_at((0, 0)), alpha128) + + self.assertRaises(AttributeError, setattr, f, "fgcolor", None) + + def test_freetype_Font_bgcolor(self): + f = ft.Font(None, 32) + zero = "0" # the default font 0 glyph does not have a pixel at (0, 0) + f.origin = False + f.pad = False + + transparent_black = pygame.Color(0, 0, 0, 0) # initial color + green = pygame.Color("green") + alpha128 = pygame.Color(10, 20, 30, 128) + + c = f.bgcolor + self.assertIsInstance(c, pygame.Color) + self.assertEqual(c, transparent_black) + + s, r = f.render(zero, pygame.Color(255, 255, 255)) + self.assertEqual(s.get_at((0, 0)), transparent_black) + + f.bgcolor = green + self.assertEqual(f.bgcolor, green) + + s, r = f.render(zero) + self.assertEqual(s.get_at((0, 0)), green) + + f.bgcolor = alpha128 + s, r = f.render(zero) + self.assertEqual(s.get_at((0, 0)), alpha128) + + surf = pygame.Surface(f.get_rect(zero).size, pygame.SRCALPHA, 32) + f.render_to(surf, (0, 0), None) + self.assertEqual(surf.get_at((0, 0)), alpha128) + + self.assertRaises(AttributeError, setattr, f, "bgcolor", None) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + @unittest.skipIf(IS_PYPY, "pypy no likey") + def test_newbuf(self): + from pygame.tests.test_utils import buftools + + Exporter = buftools.Exporter + font = self._TEST_FONTS["sans"] + srect = font.get_rect("Hi", size=12) + for format in [ + "b", + "B", + "h", + "H", + "i", + "I", + "l", + "L", + "q", + "Q", + "x", + "1x", + "2x", + "3x", + "4x", + "5x", + "6x", + "7x", + "8x", + "9x", + "h", + "=h", + "@h", + "!h", + "1h", + "=1h", + ]: + newbuf = Exporter(srect.size, format=format) + rrect = font.render_raw_to(newbuf, "Hi", size=12) + self.assertEqual(rrect, srect) + # Some unsupported formats + for format in ["f", "d", "2h", "?", "hh"]: + newbuf = Exporter(srect.size, format=format, itemsize=4) + self.assertRaises(ValueError, font.render_raw_to, newbuf, "Hi", size=12) + + def test_freetype_Font_style(self): + font = self._TEST_FONTS["sans"] + + # make sure STYLE_NORMAL is the default value + self.assertEqual(ft.STYLE_NORMAL, font.style) + + # make sure we check for style type + with self.assertRaises(TypeError): + font.style = "None" + with self.assertRaises(TypeError): + font.style = None + + # make sure we only accept valid constants + with self.assertRaises(ValueError): + font.style = 112 + + # make assure no assignments happened + self.assertEqual(ft.STYLE_NORMAL, font.style) + + # test assignment + font.style = ft.STYLE_UNDERLINE + self.assertEqual(ft.STYLE_UNDERLINE, font.style) + + # test complex styles + st = ft.STYLE_STRONG | ft.STYLE_UNDERLINE | ft.STYLE_OBLIQUE + + font.style = st + self.assertEqual(st, font.style) + + # and that STYLE_DEFAULT has no effect (continued from above) + self.assertNotEqual(st, ft.STYLE_DEFAULT) + font.style = ft.STYLE_DEFAULT + self.assertEqual(st, font.style) + + # revert changes + font.style = ft.STYLE_NORMAL + self.assertEqual(ft.STYLE_NORMAL, font.style) + + def test_freetype_Font_resolution(self): + text = "|" # Differs in width and height + resolution = ft.get_default_resolution() + new_font = ft.Font(self._sans_path, resolution=2 * resolution) + self.assertEqual(new_font.resolution, 2 * resolution) + size_normal = self._TEST_FONTS["sans"].get_rect(text, size=24).size + size_scaled = new_font.get_rect(text, size=24).size + size_by_2 = size_normal[0] * 2 + self.assertTrue( + size_by_2 + 2 >= size_scaled[0] >= size_by_2 - 2, + "%i not equal %i" % (size_scaled[1], size_by_2), + ) + size_by_2 = size_normal[1] * 2 + self.assertTrue( + size_by_2 + 2 >= size_scaled[1] >= size_by_2 - 2, + "%i not equal %i" % (size_scaled[1], size_by_2), + ) + new_resolution = resolution + 10 + ft.set_default_resolution(new_resolution) + try: + new_font = ft.Font(self._sans_path, resolution=0) + self.assertEqual(new_font.resolution, new_resolution) + finally: + ft.set_default_resolution() + + def test_freetype_Font_path(self): + self.assertEqual(self._TEST_FONTS["sans"].path, self._sans_path) + self.assertRaises(AttributeError, getattr, nullfont(), "path") + + # This Font cache test is conditional on freetype being built by a debug + # version of Python or with the C macro PGFT_DEBUG_CACHE defined. + def test_freetype_Font_cache(self): + glyphs = "abcde" + glen = len(glyphs) + other_glyphs = "123" + oglen = len(other_glyphs) + uempty = "" + ## many_glyphs = (uempty.join([chr(i) for i in range(32,127)] + + ## [chr(i) for i in range(161,172)] + + ## [chr(i) for i in range(174,239)])) + many_glyphs = uempty.join([chr(i) for i in range(32, 127)]) + mglen = len(many_glyphs) + + count = 0 + access = 0 + hit = 0 + miss = 0 + + f = ft.Font(None, size=24, font_index=0, resolution=72, ucs4=False) + f.style = ft.STYLE_NORMAL + f.antialiased = True + + # Ensure debug counters are zero + self.assertEqual(f._debug_cache_stats, (0, 0, 0, 0, 0)) + # Load some basic glyphs + count = access = miss = glen + f.render_raw(glyphs) + self.assertEqual(f._debug_cache_stats, (count, 0, access, hit, miss)) + # Vertical should not affect the cache + access += glen + hit += glen + f.vertical = True + f.render_raw(glyphs) + f.vertical = False + self.assertEqual(f._debug_cache_stats, (count, 0, access, hit, miss)) + # New glyphs will + count += oglen + access += oglen + miss += oglen + f.render_raw(other_glyphs) + self.assertEqual(f._debug_cache_stats, (count, 0, access, hit, miss)) + # Point size does + count += glen + access += glen + miss += glen + f.render_raw(glyphs, size=12) + self.assertEqual(f._debug_cache_stats, (count, 0, access, hit, miss)) + # Underline style does not + access += oglen + hit += oglen + f.underline = True + f.render_raw(other_glyphs) + f.underline = False + self.assertEqual(f._debug_cache_stats, (count, 0, access, hit, miss)) + # Oblique style does + count += glen + access += glen + miss += glen + f.oblique = True + f.render_raw(glyphs) + f.oblique = False + self.assertEqual(f._debug_cache_stats, (count, 0, access, hit, miss)) + # Strong style does; by this point cache clears can happen + count += glen + access += glen + miss += glen + f.strong = True + f.render_raw(glyphs) + f.strong = False + ccount, cdelete_count, caccess, chit, cmiss = f._debug_cache_stats + self.assertEqual( + (ccount + cdelete_count, caccess, chit, cmiss), (count, access, hit, miss) + ) + # Rotation does + count += glen + access += glen + miss += glen + f.render_raw(glyphs, rotation=10) + ccount, cdelete_count, caccess, chit, cmiss = f._debug_cache_stats + self.assertEqual( + (ccount + cdelete_count, caccess, chit, cmiss), (count, access, hit, miss) + ) + # aliased (mono) glyphs do + count += oglen + access += oglen + miss += oglen + f.antialiased = False + f.render_raw(other_glyphs) + f.antialiased = True + ccount, cdelete_count, caccess, chit, cmiss = f._debug_cache_stats + self.assertEqual( + (ccount + cdelete_count, caccess, chit, cmiss), (count, access, hit, miss) + ) + # Trigger a cleanup for sure. + count += 2 * mglen + access += 2 * mglen + miss += 2 * mglen + f.get_metrics(many_glyphs, size=8) + f.get_metrics(many_glyphs, size=10) + ccount, cdelete_count, caccess, chit, cmiss = f._debug_cache_stats + self.assertTrue(ccount < count) + self.assertEqual( + (ccount + cdelete_count, caccess, chit, cmiss), (count, access, hit, miss) + ) + + try: + ft.Font._debug_cache_stats + except AttributeError: + del test_freetype_Font_cache + + def test_undefined_character_code(self): + # To be consistent with pygame.font.Font, undefined codes + # are rendered as the undefined character, and has metrics + # of None. + font = self._TEST_FONTS["sans"] + + img, size1 = font.render(chr(1), (0, 0, 0), size=24) + img, size0 = font.render("", (0, 0, 0), size=24) + self.assertTrue(size1.width > size0.width) + + metrics = font.get_metrics(chr(1) + chr(48), size=24) + self.assertEqual(len(metrics), 2) + self.assertIsNone(metrics[0]) + self.assertIsInstance(metrics[1], tuple) + + def test_issue_242(self): + """Issue #242: get_rect() uses 0 as default style""" + + # Issue #242: freetype.Font.get_rect() ignores style defaults when + # the style argument is not given + # + # The text boundary rectangle returned by freetype.Font.get_rect() + # should match the boundary of the same text rendered directly to a + # surface. This permits accurate text positioning. To work properly, + # get_rect() should calculate the text boundary to reflect text style, + # such as underline. Instead, it ignores the style settings for the + # Font object when the style argument is omitted. + # + # When the style argument is not given, freetype.get_rect() uses + # unstyled text when calculating the boundary rectangle. This is + # because _ftfont_getrect(), in _freetype.c, set the default + # style to 0 rather than FT_STYLE_DEFAULT. + # + font = self._TEST_FONTS["sans"] + + # Try wide style on a wide character. + prev_style = font.wide + font.wide = True + try: + rect = font.get_rect("M", size=64) + surf, rrect = font.render(None, size=64) + self.assertEqual(rect, rrect) + finally: + font.wide = prev_style + + # Try strong style on several wide characters. + prev_style = font.strong + font.strong = True + try: + rect = font.get_rect("Mm_", size=64) + surf, rrect = font.render(None, size=64) + self.assertEqual(rect, rrect) + finally: + font.strong = prev_style + + # Try oblique style on a tall, narrow character. + prev_style = font.oblique + font.oblique = True + try: + rect = font.get_rect("|", size=64) + surf, rrect = font.render(None, size=64) + self.assertEqual(rect, rrect) + finally: + font.oblique = prev_style + + # Try underline style on a glyphless character. + prev_style = font.underline + font.underline = True + try: + rect = font.get_rect(" ", size=64) + surf, rrect = font.render(None, size=64) + self.assertEqual(rect, rrect) + finally: + font.underline = prev_style + + def test_issue_237(self): + """Issue #237: Memory overrun when rendered with underlining""" + + # Issue #237: Memory overrun when text without descenders is rendered + # with underlining + # + # The bug crashes the Python interpreter. The bug is caught with C + # assertions in ft_render_cb.c when the Pygame module is compiled + # for debugging. So far it is only known to affect Times New Roman. + # + name = "Times New Roman" + font = ft.SysFont(name, 19) + if font.name != name: + # The font is unavailable, so skip the test. + return + font.underline = True + s, r = font.render("Amazon", size=19) + + # Some other checks to make sure nothing else broke. + for adj in [-2, -1.9, -1, 0, 1.9, 2]: + font.underline_adjustment = adj + s, r = font.render("Amazon", size=19) + + def test_issue_243(self): + """Issue Y: trailing space ignored in boundary calculation""" + + # Issue #243: For a string with trailing spaces, freetype ignores the + # last space in boundary calculations + # + font = self._TEST_FONTS["fixed"] + r1 = font.get_rect(" ", size=64) + self.assertTrue(r1.width > 1) + r2 = font.get_rect(" ", size=64) + self.assertEqual(r2.width, 2 * r1.width) + + def test_garbage_collection(self): + """Check reference counting on returned new references""" + + def ref_items(seq): + return [weakref.ref(o) for o in seq] + + font = self._TEST_FONTS["bmp-8-75dpi"] + font.size = font.get_sizes()[0][0] + text = "A" + rect = font.get_rect(text) + surf = pygame.Surface(rect.size, pygame.SRCALPHA, 32) + refs = [] + refs.extend(ref_items(font.render(text, (0, 0, 0)))) + refs.append(weakref.ref(font.render_to(surf, (0, 0), text, (0, 0, 0)))) + refs.append(weakref.ref(font.get_rect(text))) + + n = len(refs) + self.assertTrue(n > 0) + + # for pypy we garbage collection twice. + for i in range(2): + gc.collect() + + for i in range(n): + self.assertIsNone(refs[i](), "ref %d not collected" % i) + + try: + from sys import getrefcount + except ImportError: + pass + else: + array = arrinter.Array(rect.size, "u", 1) + o = font.render_raw(text) + self.assertEqual(getrefcount(o), 2) + self.assertEqual(getrefcount(o[0]), 2) + self.assertEqual(getrefcount(o[1]), 2) + self.assertEqual(getrefcount(font.render_raw_to(array, text)), 1) + o = font.get_metrics("AB") + self.assertEqual(getrefcount(o), 2) + for i in range(len(o)): + self.assertEqual(getrefcount(o[i]), 2, "refcount fail for item %d" % i) + o = font.get_sizes() + self.assertEqual(getrefcount(o), 2) + for i in range(len(o)): + self.assertEqual(getrefcount(o[i]), 2, "refcount fail for item %d" % i) + + def test_display_surface_quit(self): + """Font.render_to() on a closed display surface""" + + # The Font.render_to() method checks that PySurfaceObject.surf is NULL + # and raise a exception if it is. This fixes a bug in Pygame revision + # 0600ea4f1cfb and earlier where Pygame segfaults instead. + null_surface = pygame.Surface.__new__(pygame.Surface) + f = self._TEST_FONTS["sans"] + self.assertRaises( + pygame.error, f.render_to, null_surface, (0, 0), "Crash!", size=12 + ) + + def test_issue_565(self): + """get_metrics supporting rotation/styles/size""" + + tests = [ + {"method": "size", "value": 36, "msg": "metrics same for size"}, + {"method": "rotation", "value": 90, "msg": "metrics same for rotation"}, + {"method": "oblique", "value": True, "msg": "metrics same for oblique"}, + ] + text = "|" + + def run_test(method, value, msg): + font = ft.Font(self._sans_path, size=24) + before = font.get_metrics(text) + font.__setattr__(method, value) + after = font.get_metrics(text) + self.assertNotEqual(before, after, msg) + + for test in tests: + run_test(test["method"], test["value"], test["msg"]) + + def test_freetype_SysFont_name(self): + """that SysFont accepts names of various types""" + fonts = pygame.font.get_fonts() + size = 12 + + # Check single name string: + font_name = ft.SysFont(fonts[0], size).name + self.assertFalse(font_name is None) + + # Check string of comma-separated names. + names = ",".join(fonts) + font_name_2 = ft.SysFont(names, size).name + self.assertEqual(font_name_2, font_name) + + # Check list of names. + font_name_2 = ft.SysFont(fonts, size).name + self.assertEqual(font_name_2, font_name) + + # Check generator: + names = (name for name in fonts) + font_name_2 = ft.SysFont(names, size).name + self.assertEqual(font_name_2, font_name) + + fonts_b = [f.encode() for f in fonts] + + # Check single name bytes. + font_name_2 = ft.SysFont(fonts_b[0], size).name + self.assertEqual(font_name_2, font_name) + + # Check comma-separated bytes. + names = b",".join(fonts_b) + font_name_2 = ft.SysFont(names, size).name + self.assertEqual(font_name_2, font_name) + + # Check list of bytes. + font_name_2 = ft.SysFont(fonts_b, size).name + self.assertEqual(font_name_2, font_name) + + # Check mixed list of bytes and string. + names = [fonts[0], fonts_b[1], fonts[2], fonts_b[3]] + font_name_2 = ft.SysFont(names, size).name + self.assertEqual(font_name_2, font_name) + + def test_pathlib(self): + f = ft.Font(pathlib.Path(self._fixed_path), 20) + + +class FreeTypeTest(unittest.TestCase): + def setUp(self): + ft.init() + + def tearDown(self): + ft.quit() + + def test_resolution(self): + try: + ft.set_default_resolution() + resolution = ft.get_default_resolution() + self.assertEqual(resolution, 72) + new_resolution = resolution + 10 + ft.set_default_resolution(new_resolution) + self.assertEqual(ft.get_default_resolution(), new_resolution) + ft.init(resolution=resolution + 20) + self.assertEqual(ft.get_default_resolution(), new_resolution) + finally: + ft.set_default_resolution() + + def test_autoinit_and_autoquit(self): + pygame.init() + self.assertTrue(ft.get_init()) + pygame.quit() + self.assertFalse(ft.get_init()) + + # Ensure autoquit is replaced at init time + pygame.init() + self.assertTrue(ft.get_init()) + pygame.quit() + self.assertFalse(ft.get_init()) + + def test_init(self): + # Test if module initialized after calling init(). + ft.quit() + ft.init() + + self.assertTrue(ft.get_init()) + + def test_init__multiple(self): + # Test if module initialized after multiple init() calls. + ft.init() + ft.init() + + self.assertTrue(ft.get_init()) + + def test_quit(self): + # Test if module uninitialized after calling quit(). + ft.quit() + + self.assertFalse(ft.get_init()) + + def test_quit__multiple(self): + # Test if module initialized after multiple quit() calls. + ft.quit() + ft.quit() + + self.assertFalse(ft.get_init()) + + def test_get_init(self): + # Test if get_init() gets the init state. + self.assertTrue(ft.get_init()) + + def test_cache_size(self): + DEFAULT_CACHE_SIZE = 64 + self.assertEqual(ft.get_cache_size(), DEFAULT_CACHE_SIZE) + ft.quit() + self.assertEqual(ft.get_cache_size(), 0) + new_cache_size = DEFAULT_CACHE_SIZE * 2 + ft.init(cache_size=new_cache_size) + self.assertEqual(ft.get_cache_size(), new_cache_size) + + def test_get_error(self): + """Ensures get_error() is initially empty (None).""" + error_msg = ft.get_error() + + self.assertIsNone(error_msg) + + def test_get_version(self): + # Test that get_version() can be called before init() + # Also tests get_version + ft.quit() + + # asserting not None just to have a test case + # there is no real fail condition other than + # raising an exception or a segfault, so a tuple of ints + # should be returned in all cases + self.assertIsNotNone(ft.get_version(linked=False)) + self.assertIsNotNone(ft.get_version(linked=True)) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/ftfont_tags.py b/.venv/Lib/site-packages/pygame/tests/ftfont_tags.py new file mode 100644 index 00000000..0d538f47 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/ftfont_tags.py @@ -0,0 +1,11 @@ +__tags__ = ["development"] + +exclude = False + +try: + import pygame.ftfont +except ImportError: + exclude = True + +if exclude: + __tags__.extend(["ignore", "subprocess_ignore"]) diff --git a/.venv/Lib/site-packages/pygame/tests/ftfont_test.py b/.venv/Lib/site-packages/pygame/tests/ftfont_test.py new file mode 100644 index 00000000..cda708b1 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/ftfont_test.py @@ -0,0 +1,17 @@ +import sys +import os +import unittest +from pygame.tests import font_test + +import pygame.ftfont + +font_test.pygame_font = pygame.ftfont + +for name in dir(font_test): + obj = getattr(font_test, name) + if isinstance(obj, type) and issubclass(obj, unittest.TestCase): # conditional and + new_name = f"Ft{name}" + globals()[new_name] = type(new_name, (obj,), {}) + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/gfxdraw_test.py b/.venv/Lib/site-packages/pygame/tests/gfxdraw_test.py new file mode 100644 index 00000000..33ee2c51 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/gfxdraw_test.py @@ -0,0 +1,876 @@ +import unittest +import pygame +import pygame.gfxdraw +from pygame.locals import * +from pygame.tests.test_utils import SurfaceSubclass + + +def intensity(c, i): + """Return color c changed by intensity i + + For 0 <= i <= 127 the color is a shade, with 0 being black, 127 being the + unaltered color. + + For 128 <= i <= 255 the color is a tint, with 255 being white, 128 the + unaltered color. + + """ + r, g, b = c[0:3] + if 0 <= i <= 127: + # Darken + return ((r * i) // 127, (g * i) // 127, (b * i) // 127) + # Lighten + return ( + r + ((255 - r) * (255 - i)) // 127, + g + ((255 - g) * (255 - i)) // 127, + b + ((255 - b) * (255 - i)) // 127, + ) + + +class GfxdrawDefaultTest(unittest.TestCase): + is_started = False + + foreground_color = (128, 64, 8) + background_color = (255, 255, 255) + + def make_palette(base_color): + """Return color palette that is various intensities of base_color""" + # Need this function for Python 3.x so the base_color + # is within the scope of the list comprehension. + return [intensity(base_color, i) for i in range(0, 256)] + + default_palette = make_palette(foreground_color) + + default_size = (100, 100) + + def check_at(self, surf, posn, color): + sc = surf.get_at(posn) + fail_msg = "%s != %s at %s, bitsize: %i, flags: %i, masks: %s" % ( + sc, + color, + posn, + surf.get_bitsize(), + surf.get_flags(), + surf.get_masks(), + ) + self.assertEqual(sc, color, fail_msg) + + def check_not_at(self, surf, posn, color): + sc = surf.get_at(posn) + fail_msg = "%s != %s at %s, bitsize: %i, flags: %i, masks: %s" % ( + sc, + color, + posn, + surf.get_bitsize(), + surf.get_flags(), + surf.get_masks(), + ) + self.assertNotEqual(sc, color, fail_msg) + + @classmethod + def setUpClass(cls): + # Necessary for Surface.set_palette. + pygame.init() + pygame.display.set_mode((1, 1)) + + @classmethod + def tearDownClass(cls): + pygame.quit() + + def setUp(self): + # This makes sure pygame is always initialized before each test (in + # case a test calls pygame.quit()). + if not pygame.get_init(): + pygame.init() + + Surface = pygame.Surface + size = self.default_size + palette = self.default_palette + if not self.is_started: + # Create test surfaces + self.surfaces = [ + Surface(size, 0, 8), + Surface(size, SRCALPHA, 16), + Surface(size, SRCALPHA, 32), + ] + self.surfaces[0].set_palette(palette) + nonpalette_fmts = ( + # (8, (0xe0, 0x1c, 0x3, 0x0)), + (12, (0xF00, 0xF0, 0xF, 0x0)), + (15, (0x7C00, 0x3E0, 0x1F, 0x0)), + (15, (0x1F, 0x3E0, 0x7C00, 0x0)), + (16, (0xF00, 0xF0, 0xF, 0xF000)), + (16, (0xF000, 0xF00, 0xF0, 0xF)), + (16, (0xF, 0xF0, 0xF00, 0xF000)), + (16, (0xF0, 0xF00, 0xF000, 0xF)), + (16, (0x7C00, 0x3E0, 0x1F, 0x8000)), + (16, (0xF800, 0x7C0, 0x3E, 0x1)), + (16, (0x1F, 0x3E0, 0x7C00, 0x8000)), + (16, (0x3E, 0x7C0, 0xF800, 0x1)), + (16, (0xF800, 0x7E0, 0x1F, 0x0)), + (16, (0x1F, 0x7E0, 0xF800, 0x0)), + (24, (0xFF, 0xFF00, 0xFF0000, 0x0)), + (24, (0xFF0000, 0xFF00, 0xFF, 0x0)), + (32, (0xFF0000, 0xFF00, 0xFF, 0x0)), + (32, (0xFF000000, 0xFF0000, 0xFF00, 0x0)), + (32, (0xFF, 0xFF00, 0xFF0000, 0x0)), + (32, (0xFF00, 0xFF0000, 0xFF000000, 0x0)), + (32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)), + (32, (0xFF000000, 0xFF0000, 0xFF00, 0xFF)), + (32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)), + (32, (0xFF00, 0xFF0000, 0xFF000000, 0xFF)), + ) + for bitsize, masks in nonpalette_fmts: + self.surfaces.append(Surface(size, 0, bitsize, masks)) + for surf in self.surfaces: + surf.fill(self.background_color) + + def test_gfxdraw__subclassed_surface(self): + """Ensure pygame.gfxdraw works on subclassed surfaces.""" + surface = SurfaceSubclass((11, 13), SRCALPHA, 32) + surface.fill(pygame.Color("blue")) + expected_color = pygame.Color("red") + x, y = 1, 2 + + pygame.gfxdraw.pixel(surface, x, y, expected_color) + + self.assertEqual(surface.get_at((x, y)), expected_color) + + def test_pixel(self): + """pixel(surface, x, y, color): return None""" + fg = self.foreground_color + bg = self.background_color + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.pixel(surf, 2, 2, fg) + for x in range(1, 4): + for y in range(1, 4): + if x == 2 and y == 2: + self.check_at(surf, (x, y), fg_adjusted) + else: + self.check_at(surf, (x, y), bg_adjusted) + + def test_hline(self): + """hline(surface, x1, x2, y, color): return None""" + fg = self.foreground_color + bg = self.background_color + startx = 10 + stopx = 80 + y = 50 + fg_test_points = [(startx, y), (stopx, y), ((stopx - startx) // 2, y)] + bg_test_points = [ + (startx - 1, y), + (stopx + 1, y), + (startx, y - 1), + (startx, y + 1), + (stopx, y - 1), + (stopx, y + 1), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.hline(surf, startx, stopx, y, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_vline(self): + """vline(surface, x, y1, y2, color): return None""" + fg = self.foreground_color + bg = self.background_color + x = 50 + starty = 10 + stopy = 80 + fg_test_points = [(x, starty), (x, stopy), (x, (stopy - starty) // 2)] + bg_test_points = [ + (x, starty - 1), + (x, stopy + 1), + (x - 1, starty), + (x + 1, starty), + (x - 1, stopy), + (x + 1, stopy), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.vline(surf, x, starty, stopy, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_rectangle(self): + """rectangle(surface, rect, color): return None""" + fg = self.foreground_color + bg = self.background_color + rect = pygame.Rect(10, 15, 55, 62) + rect_tuple = tuple(rect) + fg_test_points = [ + rect.topleft, + (rect.right - 1, rect.top), + (rect.left, rect.bottom - 1), + (rect.right - 1, rect.bottom - 1), + ] + bg_test_points = [ + (rect.left - 1, rect.top - 1), + (rect.left + 1, rect.top + 1), + (rect.right, rect.top - 1), + (rect.right - 2, rect.top + 1), + (rect.left - 1, rect.bottom), + (rect.left + 1, rect.bottom - 2), + (rect.right, rect.bottom), + (rect.right - 2, rect.bottom - 2), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.rectangle(surf, rect, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + surf.fill(bg) + pygame.gfxdraw.rectangle(surf, rect_tuple, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_box(self): + """box(surface, rect, color): return None""" + fg = self.foreground_color + bg = self.background_color + rect = pygame.Rect(10, 15, 55, 62) + rect_tuple = tuple(rect) + fg_test_points = [ + rect.topleft, + (rect.left + 1, rect.top + 1), + (rect.right - 1, rect.top), + (rect.right - 2, rect.top + 1), + (rect.left, rect.bottom - 1), + (rect.left + 1, rect.bottom - 2), + (rect.right - 1, rect.bottom - 1), + (rect.right - 2, rect.bottom - 2), + ] + bg_test_points = [ + (rect.left - 1, rect.top - 1), + (rect.right, rect.top - 1), + (rect.left - 1, rect.bottom), + (rect.right, rect.bottom), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.box(surf, rect, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + surf.fill(bg) + pygame.gfxdraw.box(surf, rect_tuple, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_line(self): + """line(surface, x1, y1, x2, y2, color): return None""" + fg = self.foreground_color + bg = self.background_color + x1 = 10 + y1 = 15 + x2 = 92 + y2 = 77 + fg_test_points = [(x1, y1), (x2, y2)] + bg_test_points = [ + (x1 - 1, y1), + (x1, y1 - 1), + (x1 - 1, y1 - 1), + (x2 + 1, y2), + (x2, y2 + 1), + (x2 + 1, y2 + 1), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.line(surf, x1, y1, x2, y2, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_circle(self): + """circle(surface, x, y, r, color): return None""" + fg = self.foreground_color + bg = self.background_color + x = 45 + y = 40 + r = 30 + fg_test_points = [(x, y - r), (x, y + r), (x - r, y), (x + r, y)] + bg_test_points = [ + (x, y), + (x, y - r + 1), + (x, y - r - 1), + (x, y + r + 1), + (x, y + r - 1), + (x - r - 1, y), + (x - r + 1, y), + (x + r + 1, y), + (x + r - 1, y), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.circle(surf, x, y, r, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_arc(self): + """arc(surface, x, y, r, start, end, color): return None""" + fg = self.foreground_color + bg = self.background_color + x = 45 + y = 40 + r = 30 + start = 0 # +x direction, but not (x + r, y) (?) + end = 90 # -y direction, including (x, y + r) + fg_test_points = [(x, y + r), (x + r, y + 1)] + bg_test_points = [ + (x, y), + (x, y - r), + (x - r, y), + (x, y + r + 1), + (x, y + r - 1), + (x - 1, y + r), + (x + r + 1, y), + (x + r - 1, y), + (x + r, y), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.arc(surf, x, y, r, start, end, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_aacircle(self): + """aacircle(surface, x, y, r, color): return None""" + fg = self.foreground_color + bg = self.background_color + x = 45 + y = 40 + r = 30 + fg_test_points = [(x, y - r), (x, y + r), (x - r, y), (x + r, y)] + bg_test_points = [ + (x, y), + (x, y - r + 1), + (x, y - r - 1), + (x, y + r + 1), + (x, y + r - 1), + (x - r - 1, y), + (x - r + 1, y), + (x + r + 1, y), + (x + r - 1, y), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.aacircle(surf, x, y, r, fg) + for posn in fg_test_points: + self.check_not_at(surf, posn, bg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_filled_circle(self): + """filled_circle(surface, x, y, r, color): return None""" + fg = self.foreground_color + bg = self.background_color + x = 45 + y = 40 + r = 30 + fg_test_points = [ + (x, y - r), + (x, y - r + 1), + (x, y + r), + (x, y + r - 1), + (x - r, y), + (x - r + 1, y), + (x + r, y), + (x + r - 1, y), + (x, y), + ] + bg_test_points = [ + (x, y - r - 1), + (x, y + r + 1), + (x - r - 1, y), + (x + r + 1, y), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.filled_circle(surf, x, y, r, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_ellipse(self): + """ellipse(surface, x, y, rx, ry, color): return None""" + fg = self.foreground_color + bg = self.background_color + x = 45 + y = 40 + rx = 30 + ry = 35 + fg_test_points = [(x, y - ry), (x, y + ry), (x - rx, y), (x + rx, y)] + bg_test_points = [ + (x, y), + (x, y - ry + 1), + (x, y - ry - 1), + (x, y + ry + 1), + (x, y + ry - 1), + (x - rx - 1, y), + (x - rx + 1, y), + (x + rx + 1, y), + (x + rx - 1, y), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.ellipse(surf, x, y, rx, ry, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_aaellipse(self): + """aaellipse(surface, x, y, rx, ry, color): return None""" + fg = self.foreground_color + bg = self.background_color + x = 45 + y = 40 + rx = 30 + ry = 35 + fg_test_points = [(x, y - ry), (x, y + ry), (x - rx, y), (x + rx, y)] + bg_test_points = [ + (x, y), + (x, y - ry + 1), + (x, y - ry - 1), + (x, y + ry + 1), + (x, y + ry - 1), + (x - rx - 1, y), + (x - rx + 1, y), + (x + rx + 1, y), + (x + rx - 1, y), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.aaellipse(surf, x, y, rx, ry, fg) + for posn in fg_test_points: + self.check_not_at(surf, posn, bg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_filled_ellipse(self): + """filled_ellipse(surface, x, y, rx, ry, color): return None""" + fg = self.foreground_color + bg = self.background_color + x = 45 + y = 40 + rx = 30 + ry = 35 + fg_test_points = [ + (x, y - ry), + (x, y - ry + 1), + (x, y + ry), + (x, y + ry - 1), + (x - rx, y), + (x - rx + 1, y), + (x + rx, y), + (x + rx - 1, y), + (x, y), + ] + bg_test_points = [ + (x, y - ry - 1), + (x, y + ry + 1), + (x - rx - 1, y), + (x + rx + 1, y), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.filled_ellipse(surf, x, y, rx, ry, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_pie(self): + """pie(surface, x, y, r, start, end, color): return None""" + fg = self.foreground_color + bg = self.background_color + x = 45 + y = 40 + r = 30 + start = 0 # +x direction, including (x + r, y) + end = 90 # -y direction, but not (x, y + r) (?) + fg_test_points = [(x, y), (x + 1, y), (x, y + 1), (x + r, y)] + bg_test_points = [ + (x - 1, y), + (x, y - 1), + (x - 1, y - 1), + (x + 1, y + 1), + (x + r + 1, y), + (x + r, y - 1), + (x, y + r + 1), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.pie(surf, x, y, r, start, end, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_trigon(self): + """trigon(surface, x1, y1, x2, y2, x3, y3, color): return None""" + fg = self.foreground_color + bg = self.background_color + x1 = 10 + y1 = 15 + x2 = 92 + y2 = 77 + x3 = 20 + y3 = 60 + fg_test_points = [(x1, y1), (x2, y2), (x3, y3)] + bg_test_points = [ + (x1 - 1, y1 - 1), + (x2 + 1, y2 + 1), + (x3 - 1, y3 + 1), + (x1 + 10, y1 + 30), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.trigon(surf, x1, y1, x2, y2, x3, y3, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_aatrigon(self): + """aatrigon(surface, x1, y1, x2, y2, x3, y3, color): return None""" + fg = self.foreground_color + bg = self.background_color + x1 = 10 + y1 = 15 + x2 = 92 + y2 = 77 + x3 = 20 + y3 = 60 + fg_test_points = [(x1, y1), (x2, y2), (x3, y3)] + bg_test_points = [ + (x1 - 1, y1 - 1), + (x2 + 1, y2 + 1), + (x3 - 1, y3 + 1), + (x1 + 10, y1 + 30), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.aatrigon(surf, x1, y1, x2, y2, x3, y3, fg) + for posn in fg_test_points: + self.check_not_at(surf, posn, bg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_aatrigon__with_horizontal_edge(self): + """Ensure aatrigon draws horizontal edges correctly. + + This test creates 2 surfaces and draws an aatrigon on each. The pixels + on each surface are compared to ensure they are the same. The only + difference between the 2 aatrigons is the order the points are drawn. + The order of the points should have no impact on the final drawing. + + Related to issue #622. + """ + bg_color = pygame.Color("white") + line_color = pygame.Color("black") + width, height = 11, 10 + expected_surface = pygame.Surface((width, height), 0, 32) + expected_surface.fill(bg_color) + surface = pygame.Surface((width, height), 0, 32) + surface.fill(bg_color) + + x1, y1 = width - 1, 0 + x2, y2 = (width - 1) // 2, height - 1 + x3, y3 = 0, 0 + + # The points in this order draw as expected. + pygame.gfxdraw.aatrigon(expected_surface, x1, y1, x2, y2, x3, y3, line_color) + + # The points in reverse order fail to draw the horizontal edge along + # the top. + pygame.gfxdraw.aatrigon(surface, x3, y3, x2, y2, x1, y1, line_color) + + # The surfaces are locked for a possible speed up of pixel access. + expected_surface.lock() + surface.lock() + for x in range(width): + for y in range(height): + self.assertEqual( + expected_surface.get_at((x, y)), + surface.get_at((x, y)), + f"pos=({x}, {y})", + ) + + surface.unlock() + expected_surface.unlock() + + def test_filled_trigon(self): + """filled_trigon(surface, x1, y1, x2, y2, x3, y3, color): return None""" + fg = self.foreground_color + bg = self.background_color + x1 = 10 + y1 = 15 + x2 = 92 + y2 = 77 + x3 = 20 + y3 = 60 + fg_test_points = [(x1, y1), (x2, y2), (x3, y3), (x1 + 10, y1 + 30)] + bg_test_points = [(x1 - 1, y1 - 1), (x2 + 1, y2 + 1), (x3 - 1, y3 + 1)] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.filled_trigon(surf, x1, y1, x2, y2, x3, y3, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_polygon(self): + """polygon(surface, points, color): return None""" + fg = self.foreground_color + bg = self.background_color + points = [(10, 80), (10, 15), (92, 25), (92, 80)] + fg_test_points = points + [ + (points[0][0], points[0][1] - 1), + (points[0][0] + 1, points[0][1]), + (points[3][0] - 1, points[3][1]), + (points[3][0], points[3][1] - 1), + (points[2][0], points[2][1] + 1), + ] + bg_test_points = [ + (points[0][0] - 1, points[0][1]), + (points[0][0], points[0][1] + 1), + (points[0][0] - 1, points[0][1] + 1), + (points[0][0] + 1, points[0][1] - 1), + (points[3][0] + 1, points[3][1]), + (points[3][0], points[3][1] + 1), + (points[3][0] + 1, points[3][1] + 1), + (points[3][0] - 1, points[3][1] - 1), + (points[2][0] + 1, points[2][1]), + (points[2][0] - 1, points[2][1] + 1), + (points[1][0] - 1, points[1][1]), + (points[1][0], points[1][1] - 1), + (points[1][0] - 1, points[1][1] - 1), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.polygon(surf, points, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_aapolygon(self): + """aapolygon(surface, points, color): return None""" + fg = self.foreground_color + bg = self.background_color + points = [(10, 80), (10, 15), (92, 25), (92, 80)] + fg_test_points = points + bg_test_points = [ + (points[0][0] - 1, points[0][1]), + (points[0][0], points[0][1] + 1), + (points[0][0] - 1, points[0][1] + 1), + (points[0][0] + 1, points[0][1] - 1), + (points[3][0] + 1, points[3][1]), + (points[3][0], points[3][1] + 1), + (points[3][0] + 1, points[3][1] + 1), + (points[3][0] - 1, points[3][1] - 1), + (points[2][0] + 1, points[2][1]), + (points[2][0] - 1, points[2][1] + 1), + (points[1][0] - 1, points[1][1]), + (points[1][0], points[1][1] - 1), + (points[1][0] - 1, points[1][1] - 1), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.aapolygon(surf, points, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_not_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_aapolygon__with_horizontal_edge(self): + """Ensure aapolygon draws horizontal edges correctly. + + This test creates 2 surfaces and draws a polygon on each. The pixels + on each surface are compared to ensure they are the same. The only + difference between the 2 polygons is that one is drawn using + aapolygon() and the other using multiple line() calls. They should + produce the same final drawing. + + Related to issue #622. + """ + bg_color = pygame.Color("white") + line_color = pygame.Color("black") + width, height = 11, 10 + expected_surface = pygame.Surface((width, height), 0, 32) + expected_surface.fill(bg_color) + surface = pygame.Surface((width, height), 0, 32) + surface.fill(bg_color) + + points = ((0, 0), (0, height - 1), (width - 1, height - 1), (width - 1, 0)) + + # The points are used to draw the expected aapolygon using the line() + # function. + for (x1, y1), (x2, y2) in zip(points, points[1:] + points[:1]): + pygame.gfxdraw.line(expected_surface, x1, y1, x2, y2, line_color) + + # The points in this order fail to draw the horizontal edge along + # the top. + pygame.gfxdraw.aapolygon(surface, points, line_color) + + # The surfaces are locked for a possible speed up of pixel access. + expected_surface.lock() + surface.lock() + for x in range(width): + for y in range(height): + self.assertEqual( + expected_surface.get_at((x, y)), + surface.get_at((x, y)), + f"pos=({x}, {y})", + ) + + surface.unlock() + expected_surface.unlock() + + def test_filled_polygon(self): + """filled_polygon(surface, points, color): return None""" + fg = self.foreground_color + bg = self.background_color + points = [(10, 80), (10, 15), (92, 25), (92, 80)] + fg_test_points = points + [ + (points[0][0], points[0][1] - 1), + (points[0][0] + 1, points[0][1]), + (points[0][0] + 1, points[0][1] - 1), + (points[3][0] - 1, points[3][1]), + (points[3][0], points[3][1] - 1), + (points[3][0] - 1, points[3][1] - 1), + (points[2][0], points[2][1] + 1), + (points[2][0] - 1, points[2][1] + 1), + ] + bg_test_points = [ + (points[0][0] - 1, points[0][1]), + (points[0][0], points[0][1] + 1), + (points[0][0] - 1, points[0][1] + 1), + (points[3][0] + 1, points[3][1]), + (points[3][0], points[3][1] + 1), + (points[3][0] + 1, points[3][1] + 1), + (points[2][0] + 1, points[2][1]), + (points[1][0] - 1, points[1][1]), + (points[1][0], points[1][1] - 1), + (points[1][0] - 1, points[1][1] - 1), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.filled_polygon(surf, points, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + def test_textured_polygon(self): + """textured_polygon(surface, points, texture, tx, ty): return None""" + w, h = self.default_size + fg = self.foreground_color + bg = self.background_color + tx = 0 + ty = 0 + texture = pygame.Surface((w + tx, h + ty), 0, 24) + texture.fill(fg, (0, 0, w, h)) + points = [(10, 80), (10, 15), (92, 25), (92, 80)] + # Don't know how to really check this as boarder points may + # or may not be included in the textured polygon. + fg_test_points = [(points[1][0] + 30, points[1][1] + 40)] + bg_test_points = [ + (points[0][0] - 1, points[0][1]), + (points[0][0], points[0][1] + 1), + (points[0][0] - 1, points[0][1] + 1), + (points[3][0] + 1, points[3][1]), + (points[3][0], points[3][1] + 1), + (points[3][0] + 1, points[3][1] + 1), + (points[2][0] + 1, points[2][1]), + (points[1][0] - 1, points[1][1]), + (points[1][0], points[1][1] - 1), + (points[1][0] - 1, points[1][1] - 1), + ] + for surf in self.surfaces[1:]: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.textured_polygon(surf, points, texture, -tx, -ty) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + # Alpha blit to 8 bits-per-pixel surface forbidden. + texture = pygame.Surface(self.default_size, SRCALPHA, 32) + self.assertRaises( + ValueError, + pygame.gfxdraw.textured_polygon, + self.surfaces[0], + points, + texture, + 0, + 0, + ) + + def test_bezier(self): + """bezier(surface, points, steps, color): return None""" + fg = self.foreground_color + bg = self.background_color + points = [(10, 50), (25, 15), (60, 80), (92, 30)] + fg_test_points = [points[0], points[3]] + bg_test_points = [ + (points[0][0] - 1, points[0][1]), + (points[3][0] + 1, points[3][1]), + (points[1][0], points[1][1] + 3), + (points[2][0], points[2][1] - 3), + ] + for surf in self.surfaces: + fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) + bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) + pygame.gfxdraw.bezier(surf, points, 30, fg) + for posn in fg_test_points: + self.check_at(surf, posn, fg_adjusted) + for posn in bg_test_points: + self.check_at(surf, posn, bg_adjusted) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/image__save_gl_surface_test.py b/.venv/Lib/site-packages/pygame/tests/image__save_gl_surface_test.py new file mode 100644 index 00000000..2932f422 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/image__save_gl_surface_test.py @@ -0,0 +1,46 @@ +import os +import unittest + +from pygame.tests import test_utils +import pygame +from pygame.locals import * + + +@unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") == "dummy", + 'OpenGL requires a non-"dummy" SDL_VIDEODRIVER', +) +class GL_ImageSave(unittest.TestCase): + def test_image_save_works_with_opengl_surfaces(self): + """ + |tags:display,slow,opengl| + """ + + pygame.display.init() + screen = pygame.display.set_mode((640, 480), OPENGL | DOUBLEBUF) + pygame.display.flip() + + tmp_dir = test_utils.get_tmp_dir() + # Try the imageext module. + tmp_file = os.path.join(tmp_dir, "opengl_save_surface_test.png") + pygame.image.save(screen, tmp_file) + + self.assertTrue(os.path.exists(tmp_file)) + + os.remove(tmp_file) + + # Only test the image module. + tmp_file = os.path.join(tmp_dir, "opengl_save_surface_test.bmp") + pygame.image.save(screen, tmp_file) + + self.assertTrue(os.path.exists(tmp_file)) + + os.remove(tmp_file) + + # stops tonnes of tmp dirs building up in trunk dir + os.rmdir(tmp_dir) + pygame.display.quit() + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/image_tags.py b/.venv/Lib/site-packages/pygame/tests/image_tags.py new file mode 100644 index 00000000..d847903d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/image_tags.py @@ -0,0 +1,7 @@ +__tags__ = [] + +import pygame +import sys + +if "pygame.image" not in sys.modules: + __tags__.extend(("ignore", "subprocess_ignore")) diff --git a/.venv/Lib/site-packages/pygame/tests/image_test.py b/.venv/Lib/site-packages/pygame/tests/image_test.py new file mode 100644 index 00000000..f81674ef --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/image_test.py @@ -0,0 +1,1271 @@ +import array +import binascii +import io +import os +import tempfile +import unittest +import glob +import pathlib + +from pygame.tests.test_utils import example_path, png, tostring +import pygame, pygame.image, pygame.pkgdata + +sdl_image_svg_jpeg_save_bug = False +_sdl_image_ver = pygame.image.get_sdl_image_version() +if _sdl_image_ver is not None: + sdl_image_svg_jpeg_save_bug = ( + _sdl_image_ver <= (2, 0, 5) and pygame.get_sdl_byteorder() == pygame.BIG_ENDIAN + ) + + +def test_magic(f, magic_hexes): + """Tests a given file to see if the magic hex matches.""" + data = f.read(len(magic_hexes)) + if len(data) != len(magic_hexes): + return 0 + for i, magic_hex in enumerate(magic_hexes): + if magic_hex != data[i]: + return 0 + return 1 + + +class ImageModuleTest(unittest.TestCase): + def testLoadIcon(self): + """see if we can load the pygame icon.""" + f = pygame.pkgdata.getResource("pygame_icon.bmp") + self.assertEqual(f.mode, "rb") + + surf = pygame.image.load_basic(f) + + self.assertEqual(surf.get_at((0, 0)), (5, 4, 5, 255)) + self.assertEqual(surf.get_height(), 32) + self.assertEqual(surf.get_width(), 32) + + def testLoadPNG(self): + """see if we can load a png with color values in the proper channels.""" + # Create a PNG file with known colors + reddish_pixel = (210, 0, 0, 255) + greenish_pixel = (0, 220, 0, 255) + bluish_pixel = (0, 0, 230, 255) + greyish_pixel = (110, 120, 130, 140) + pixel_array = [reddish_pixel + greenish_pixel, bluish_pixel + greyish_pixel] + + f_descriptor, f_path = tempfile.mkstemp(suffix=".png") + + with os.fdopen(f_descriptor, "wb") as f: + w = png.Writer(2, 2, alpha=True) + w.write(f, pixel_array) + + # Read the PNG file and verify that pygame interprets it correctly + surf = pygame.image.load(f_path) + + self.assertEqual(surf.get_at((0, 0)), reddish_pixel) + self.assertEqual(surf.get_at((1, 0)), greenish_pixel) + self.assertEqual(surf.get_at((0, 1)), bluish_pixel) + self.assertEqual(surf.get_at((1, 1)), greyish_pixel) + + # Read the PNG file obj. and verify that pygame interprets it correctly + with open(f_path, "rb") as f: + surf = pygame.image.load(f) + + self.assertEqual(surf.get_at((0, 0)), reddish_pixel) + self.assertEqual(surf.get_at((1, 0)), greenish_pixel) + self.assertEqual(surf.get_at((0, 1)), bluish_pixel) + self.assertEqual(surf.get_at((1, 1)), greyish_pixel) + + os.remove(f_path) + + def testLoadJPG(self): + """to see if we can load a jpg.""" + f = example_path("data/alien1.jpg") + surf = pygame.image.load(f) + + with open(f, "rb") as f: + surf = pygame.image.load(f) + + def testLoadBytesIO(self): + """to see if we can load images with BytesIO.""" + files = [ + "data/alien1.png", + "data/alien1.jpg", + "data/alien1.gif", + "data/asprite.bmp", + ] + + for fname in files: + with self.subTest(fname=fname): + with open(example_path(fname), "rb") as f: + img_bytes = f.read() + img_file = io.BytesIO(img_bytes) + image = pygame.image.load(img_file) + + @unittest.skipIf( + sdl_image_svg_jpeg_save_bug, + "SDL_image 2.0.5 and older has a big endian bug in jpeg saving", + ) + def testSaveJPG(self): + """JPG equivalent to issue #211 - color channel swapping + + Make sure the SDL surface color masks represent the rgb memory format + required by the JPG library. The masks are machine endian dependent + """ + + from pygame import Color, Rect + + # The source image is a 2 by 2 square of four colors. Since JPEG is + # lossy, there can be color bleed. Make each color square 16 by 16, + # to avoid the significantly color value distorts found at color + # boundaries due to the compression value set by Pygame. + square_len = 16 + sz = 2 * square_len, 2 * square_len + + # +---------------------------------+ + # | red | green | + # |----------------+----------------| + # | blue | (255, 128, 64) | + # +---------------------------------+ + # + # as (rect, color) pairs. + def as_rect(square_x, square_y): + return Rect( + square_x * square_len, square_y * square_len, square_len, square_len + ) + + squares = [ + (as_rect(0, 0), Color("red")), + (as_rect(1, 0), Color("green")), + (as_rect(0, 1), Color("blue")), + (as_rect(1, 1), Color(255, 128, 64)), + ] + + # A surface format which is not directly usable with libjpeg. + surf = pygame.Surface(sz, 0, 32) + for rect, color in squares: + surf.fill(color, rect) + + # Assume pygame.image.Load works correctly as it is handled by the + # third party SDL_image library. + f_path = tempfile.mktemp(suffix=".jpg") + pygame.image.save(surf, f_path) + jpg_surf = pygame.image.load(f_path) + + # Allow for small differences in the restored colors. + def approx(c): + mask = 0xFC + return pygame.Color(c.r & mask, c.g & mask, c.b & mask) + + offset = square_len // 2 + for rect, color in squares: + posn = rect.move((offset, offset)).topleft + self.assertEqual(approx(jpg_surf.get_at(posn)), approx(color)) + + os.remove(f_path) + + def testSavePNG32(self): + """see if we can save a png with color values in the proper channels.""" + # Create a PNG file with known colors + reddish_pixel = (215, 0, 0, 255) + greenish_pixel = (0, 225, 0, 255) + bluish_pixel = (0, 0, 235, 255) + greyish_pixel = (115, 125, 135, 145) + + surf = pygame.Surface((1, 4), pygame.SRCALPHA, 32) + surf.set_at((0, 0), reddish_pixel) + surf.set_at((0, 1), greenish_pixel) + surf.set_at((0, 2), bluish_pixel) + surf.set_at((0, 3), greyish_pixel) + + f_path = tempfile.mktemp(suffix=".png") + pygame.image.save(surf, f_path) + + try: + # Read the PNG file and verify that pygame saved it correctly + reader = png.Reader(filename=f_path) + width, height, pixels, metadata = reader.asRGBA8() + + # pixels is a generator + self.assertEqual(tuple(next(pixels)), reddish_pixel) + self.assertEqual(tuple(next(pixels)), greenish_pixel) + self.assertEqual(tuple(next(pixels)), bluish_pixel) + self.assertEqual(tuple(next(pixels)), greyish_pixel) + + finally: + # Ensures proper clean up. + if not reader.file.closed: + reader.file.close() + del reader + os.remove(f_path) + + def testSavePNG24(self): + """see if we can save a png with color values in the proper channels.""" + # Create a PNG file with known colors + reddish_pixel = (215, 0, 0) + greenish_pixel = (0, 225, 0) + bluish_pixel = (0, 0, 235) + greyish_pixel = (115, 125, 135) + + surf = pygame.Surface((1, 4), 0, 24) + surf.set_at((0, 0), reddish_pixel) + surf.set_at((0, 1), greenish_pixel) + surf.set_at((0, 2), bluish_pixel) + surf.set_at((0, 3), greyish_pixel) + + f_path = tempfile.mktemp(suffix=".png") + pygame.image.save(surf, f_path) + + try: + # Read the PNG file and verify that pygame saved it correctly + reader = png.Reader(filename=f_path) + width, height, pixels, metadata = reader.asRGB8() + + # pixels is a generator + self.assertEqual(tuple(next(pixels)), reddish_pixel) + self.assertEqual(tuple(next(pixels)), greenish_pixel) + self.assertEqual(tuple(next(pixels)), bluish_pixel) + self.assertEqual(tuple(next(pixels)), greyish_pixel) + + finally: + # Ensures proper clean up. + if not reader.file.closed: + reader.file.close() + del reader + os.remove(f_path) + + def testSavePNG8(self): + """see if we can save an 8 bit png correctly""" + # Create an 8-bit PNG file with known colors + set_pixels = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (170, 146, 170)] + + size = (1, len(set_pixels)) + surf = pygame.Surface(size, depth=8) + for cnt, pix in enumerate(set_pixels): + surf.set_at((0, cnt), pix) + + f_path = tempfile.mktemp(suffix=".png") + pygame.image.save(surf, f_path) + + try: + # Read the PNG file and verify that pygame saved it correctly + reader = png.Reader(filename=f_path) + width, height, pixels, _ = reader.asRGB8() + + self.assertEqual(size, (width, height)) + + # pixels is a generator + self.assertEqual(list(map(tuple, pixels)), set_pixels) + + finally: + # Ensures proper clean up. + if not reader.file.closed: + reader.file.close() + del reader + os.remove(f_path) + + def testSavePaletteAsPNG8(self): + """see if we can save a png with color values in the proper channels.""" + # Create a PNG file with known colors + pygame.display.init() + + reddish_pixel = (215, 0, 0) + greenish_pixel = (0, 225, 0) + bluish_pixel = (0, 0, 235) + greyish_pixel = (115, 125, 135) + + surf = pygame.Surface((1, 4), 0, 8) + surf.set_palette_at(0, reddish_pixel) + surf.set_palette_at(1, greenish_pixel) + surf.set_palette_at(2, bluish_pixel) + surf.set_palette_at(3, greyish_pixel) + + f_path = tempfile.mktemp(suffix=".png") + pygame.image.save(surf, f_path) + try: + # Read the PNG file and verify that pygame saved it correctly + reader = png.Reader(filename=f_path) + reader.read() + palette = reader.palette() + + # pixels is a generator + self.assertEqual(tuple(next(palette)), reddish_pixel) + self.assertEqual(tuple(next(palette)), greenish_pixel) + self.assertEqual(tuple(next(palette)), bluish_pixel) + self.assertEqual(tuple(next(palette)), greyish_pixel) + + finally: + # Ensures proper clean up. + if not reader.file.closed: + reader.file.close() + del reader + os.remove(f_path) + + def test_save(self): + s = pygame.Surface((10, 10)) + s.fill((23, 23, 23)) + magic_hex = {} + magic_hex["jpg"] = [0xFF, 0xD8, 0xFF, 0xE0] + magic_hex["png"] = [0x89, 0x50, 0x4E, 0x47] + # magic_hex['tga'] = [0x0, 0x0, 0xa] + magic_hex["bmp"] = [0x42, 0x4D] + + formats = ["jpg", "png", "bmp"] + # uppercase too... JPG + formats = formats + [x.upper() for x in formats] + + for fmt in formats: + try: + temp_filename = f"tmpimg.{fmt}" + pygame.image.save(s, temp_filename) + + # Using 'with' ensures the file is closed even if test fails. + with open(temp_filename, "rb") as handle: + # Test the magic numbers at the start of the file to ensure + # they are saved as the correct file type. + self.assertEqual( + (1, fmt), (test_magic(handle, magic_hex[fmt.lower()]), fmt) + ) + + # load the file to make sure it was saved correctly. + # Note load can load a jpg saved with a .png file name. + s2 = pygame.image.load(temp_filename) + # compare contents, might only work reliably for png... + # but because it's all one color it seems to work with jpg. + self.assertEqual(s2.get_at((0, 0)), s.get_at((0, 0))) + finally: + # clean up the temp file, comment out to leave tmp file after run. + os.remove(temp_filename) + + def test_save_to_fileobject(self): + s = pygame.Surface((1, 1)) + s.fill((23, 23, 23)) + bytes_stream = io.BytesIO() + + pygame.image.save(s, bytes_stream) + bytes_stream.seek(0) + s2 = pygame.image.load(bytes_stream, "tga") + self.assertEqual(s.get_at((0, 0)), s2.get_at((0, 0))) + + def test_save_tga(self): + s = pygame.Surface((1, 1)) + s.fill((23, 23, 23)) + with tempfile.NamedTemporaryFile(suffix=".tga", delete=False) as f: + temp_filename = f.name + + try: + pygame.image.save(s, temp_filename) + s2 = pygame.image.load(temp_filename) + self.assertEqual(s2.get_at((0, 0)), s.get_at((0, 0))) + finally: + # clean up the temp file, even if test fails + os.remove(temp_filename) + + def test_save_pathlib(self): + surf = pygame.Surface((1, 1)) + surf.fill((23, 23, 23)) + with tempfile.NamedTemporaryFile(suffix=".tga", delete=False) as f: + temp_filename = f.name + + path = pathlib.Path(temp_filename) + try: + pygame.image.save(surf, path) + s2 = pygame.image.load(path) + self.assertEqual(s2.get_at((0, 0)), surf.get_at((0, 0))) + finally: + os.remove(temp_filename) + + def test_save__to_fileobject_w_namehint_argument(self): + s = pygame.Surface((10, 10)) + s.fill((23, 23, 23)) + magic_hex = {} + magic_hex["jpg"] = [0xFF, 0xD8, 0xFF, 0xE0] + magic_hex["png"] = [0x89, 0x50, 0x4E, 0x47] + magic_hex["bmp"] = [0x42, 0x4D] + + formats = ["tga", "jpg", "bmp", "png"] + # uppercase too... JPG + formats = formats + [x.upper() for x in formats] + + SDL_Im_version = pygame.image.get_sdl_image_version() + # We assume here that minor version and patch level of SDL_Image + # never goes above 99 + isAtLeastSDL_image_2_0_2 = (SDL_Im_version is not None) and ( + SDL_Im_version[0] * 10000 + SDL_Im_version[1] * 100 + SDL_Im_version[2] + ) >= 20002 + for fmt in formats: + tmp_file, tmp_filename = tempfile.mkstemp(suffix=f".{fmt}") + if not isAtLeastSDL_image_2_0_2 and fmt.lower() == "jpg": + with os.fdopen(tmp_file, "wb") as handle: + with self.assertRaises(pygame.error): + pygame.image.save(s, handle, tmp_filename) + else: + with os.fdopen(tmp_file, "r+b") as handle: + pygame.image.save(s, handle, tmp_filename) + + if fmt.lower() in magic_hex: + # Test the magic numbers at the start of the file to + # ensure they are saved as the correct file type. + handle.seek(0) + self.assertEqual( + (1, fmt), (test_magic(handle, magic_hex[fmt.lower()]), fmt) + ) + # load the file to make sure it was saved correctly. + handle.flush() + handle.seek(0) + s2 = pygame.image.load(handle, tmp_filename) + self.assertEqual(s2.get_at((0, 0)), s.get_at((0, 0))) + os.remove(tmp_filename) + + def test_save_colorkey(self): + """make sure the color key is not changed when saving.""" + s = pygame.Surface((10, 10), pygame.SRCALPHA, 32) + s.fill((23, 23, 23)) + s.set_colorkey((0, 0, 0)) + colorkey1 = s.get_colorkey() + p1 = s.get_at((0, 0)) + + temp_filename = "tmpimg.png" + try: + pygame.image.save(s, temp_filename) + s2 = pygame.image.load(temp_filename) + finally: + os.remove(temp_filename) + + colorkey2 = s.get_colorkey() + # check that the pixel and the colorkey is correct. + self.assertEqual(colorkey1, colorkey2) + self.assertEqual(p1, s2.get_at((0, 0))) + + def test_load_unicode_path(self): + import shutil + + orig = example_path("data/asprite.bmp") + temp = os.path.join(example_path("data"), "你好.bmp") + shutil.copy(orig, temp) + try: + im = pygame.image.load(temp) + finally: + os.remove(temp) + + def _unicode_save(self, temp_file): + im = pygame.Surface((10, 10), 0, 32) + try: + with open(temp_file, "w") as f: + pass + os.remove(temp_file) + except OSError: + raise unittest.SkipTest("the path cannot be opened") + + self.assertFalse(os.path.exists(temp_file)) + + try: + pygame.image.save(im, temp_file) + + self.assertGreater(os.path.getsize(temp_file), 10) + finally: + try: + os.remove(temp_file) + except OSError: + pass + + def test_save_unicode_path(self): + """save unicode object with non-ASCII chars""" + self._unicode_save("你好.bmp") + + def assertPremultipliedAreEqual(self, string1, string2, source_string): + self.assertEqual(len(string1), len(string2)) + block_size = 20 + if string1 != string2: + for block_start in range(0, len(string1), block_size): + block_end = min(block_start + block_size, len(string1)) + block1 = string1[block_start:block_end] + block2 = string2[block_start:block_end] + if block1 != block2: + source_block = source_string[block_start:block_end] + msg = ( + "string difference in %d to %d of %d:\n%s\n%s\nsource:\n%s" + % ( + block_start, + block_end, + len(string1), + binascii.hexlify(block1), + binascii.hexlify(block2), + binascii.hexlify(source_block), + ) + ) + self.fail(msg) + + def test_to_string__premultiplied(self): + """test to make sure we can export a surface to a premultiplied alpha string""" + + def convertRGBAtoPremultiplied(surface_to_modify): + for x in range(surface_to_modify.get_width()): + for y in range(surface_to_modify.get_height()): + color = surface_to_modify.get_at((x, y)) + premult_color = ( + color[0] * color[3] / 255, + color[1] * color[3] / 255, + color[2] * color[3] / 255, + color[3], + ) + surface_to_modify.set_at((x, y), premult_color) + + test_surface = pygame.Surface((256, 256), pygame.SRCALPHA, 32) + for x in range(test_surface.get_width()): + for y in range(test_surface.get_height()): + i = x + y * test_surface.get_width() + test_surface.set_at( + (x, y), ((i * 7) % 256, (i * 13) % 256, (i * 27) % 256, y) + ) + premultiplied_copy = test_surface.copy() + convertRGBAtoPremultiplied(premultiplied_copy) + self.assertPremultipliedAreEqual( + pygame.image.tostring(test_surface, "RGBA_PREMULT"), + pygame.image.tostring(premultiplied_copy, "RGBA"), + pygame.image.tostring(test_surface, "RGBA"), + ) + self.assertPremultipliedAreEqual( + pygame.image.tostring(test_surface, "ARGB_PREMULT"), + pygame.image.tostring(premultiplied_copy, "ARGB"), + pygame.image.tostring(test_surface, "ARGB"), + ) + + no_alpha_surface = pygame.Surface((256, 256), 0, 24) + self.assertRaises( + ValueError, pygame.image.tostring, no_alpha_surface, "RGBA_PREMULT" + ) + + # Custom assert method to check for identical surfaces. + def _assertSurfaceEqual(self, surf_a, surf_b, msg=None): + a_width, a_height = surf_a.get_width(), surf_a.get_height() + + # Check a few things to see if the surfaces are equal. + self.assertEqual(a_width, surf_b.get_width(), msg) + self.assertEqual(a_height, surf_b.get_height(), msg) + self.assertEqual(surf_a.get_size(), surf_b.get_size(), msg) + self.assertEqual(surf_a.get_rect(), surf_b.get_rect(), msg) + self.assertEqual(surf_a.get_colorkey(), surf_b.get_colorkey(), msg) + self.assertEqual(surf_a.get_alpha(), surf_b.get_alpha(), msg) + self.assertEqual(surf_a.get_flags(), surf_b.get_flags(), msg) + self.assertEqual(surf_a.get_bitsize(), surf_b.get_bitsize(), msg) + self.assertEqual(surf_a.get_bytesize(), surf_b.get_bytesize(), msg) + # Anything else? + + # Making the method lookups local for a possible speed up. + surf_a_get_at = surf_a.get_at + surf_b_get_at = surf_b.get_at + for y in range(a_height): + for x in range(a_width): + self.assertEqual( + surf_a_get_at((x, y)), + surf_b_get_at((x, y)), + "%s (pixel: %d, %d)" % (msg, x, y), + ) + + def test_fromstring__and_tostring(self): + """Ensure methods tostring() and fromstring() are symmetric.""" + + import itertools + + fmts = ("RGBA", "ARGB", "BGRA") + fmt_permutations = itertools.permutations(fmts, 2) + fmt_combinations = itertools.combinations(fmts, 2) + + def convert(fmt1, fmt2, str_buf): + pos_fmt1 = {k: v for v, k in enumerate(fmt1)} + pos_fmt2 = {k: v for v, k in enumerate(fmt2)} + byte_buf = array.array("B", str_buf) + num_quads = len(byte_buf) // 4 + for i in range(num_quads): + i4 = i * 4 + R = byte_buf[i4 + pos_fmt1["R"]] + G = byte_buf[i4 + pos_fmt1["G"]] + B = byte_buf[i4 + pos_fmt1["B"]] + A = byte_buf[i4 + pos_fmt1["A"]] + byte_buf[i4 + pos_fmt2["R"]] = R + byte_buf[i4 + pos_fmt2["G"]] = G + byte_buf[i4 + pos_fmt2["B"]] = B + byte_buf[i4 + pos_fmt2["A"]] = A + return tostring(byte_buf) + + #################################################################### + test_surface = pygame.Surface((64, 256), flags=pygame.SRCALPHA, depth=32) + for i in range(256): + for j in range(16): + intensity = j * 16 + 15 + test_surface.set_at((j + 0, i), (intensity, i, i, i)) + test_surface.set_at((j + 16, i), (i, intensity, i, i)) + test_surface.set_at((j + 32, i), (i, i, intensity, i)) + test_surface.set_at((j + 32, i), (i, i, i, intensity)) + + self._assertSurfaceEqual( + test_surface, test_surface, "failing with identical surfaces" + ) + + for pair in fmt_combinations: + fmt1_buf = pygame.image.tostring(test_surface, pair[0]) + fmt1_convert_buf = convert( + pair[1], pair[0], convert(pair[0], pair[1], fmt1_buf) + ) + test_convert_two_way = pygame.image.fromstring( + fmt1_convert_buf, test_surface.get_size(), pair[0] + ) + + self._assertSurfaceEqual( + test_surface, + test_convert_two_way, + f"converting {pair[0]} to {pair[1]} and back is not symmetric", + ) + + for pair in fmt_permutations: + fmt1_buf = pygame.image.tostring(test_surface, pair[0]) + fmt2_convert_buf = convert(pair[0], pair[1], fmt1_buf) + test_convert_one_way = pygame.image.fromstring( + fmt2_convert_buf, test_surface.get_size(), pair[1] + ) + + self._assertSurfaceEqual( + test_surface, + test_convert_one_way, + f"converting {pair[0]} to {pair[1]} failed", + ) + + for fmt in fmts: + test_buf = pygame.image.tostring(test_surface, fmt) + test_to_from_fmt_string = pygame.image.fromstring( + test_buf, test_surface.get_size(), fmt + ) + + self._assertSurfaceEqual( + test_surface, + test_to_from_fmt_string, + "tostring/fromstring functions are not " + f"symmetric with '{fmt}' format", + ) + + def test_tostring_depth_24(self): + test_surface = pygame.Surface((64, 256), depth=24) + for i in range(256): + for j in range(16): + intensity = j * 16 + 15 + test_surface.set_at((j + 0, i), (intensity, i, i, i)) + test_surface.set_at((j + 16, i), (i, intensity, i, i)) + test_surface.set_at((j + 32, i), (i, i, intensity, i)) + test_surface.set_at((j + 32, i), (i, i, i, intensity)) + + fmt = "RGB" + fmt_buf = pygame.image.tostring(test_surface, fmt) + test_to_from_fmt_string = pygame.image.fromstring( + fmt_buf, test_surface.get_size(), fmt + ) + + self._assertSurfaceEqual( + test_surface, + test_to_from_fmt_string, + f'tostring/fromstring functions are not symmetric with "{fmt}" format', + ) + + def test_frombuffer_8bit(self): + """test reading pixel data from a bytes buffer""" + pygame.display.init() + eight_bit_palette_buffer = bytearray( + [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3] + ) + + eight_bit_surf = pygame.image.frombuffer(eight_bit_palette_buffer, (4, 4), "P") + eight_bit_surf.set_palette( + [(255, 10, 20), (255, 255, 255), (0, 0, 0), (50, 200, 20)] + ) + self.assertEqual(eight_bit_surf.get_at((0, 0)), pygame.Color(255, 10, 20)) + self.assertEqual(eight_bit_surf.get_at((1, 1)), pygame.Color(255, 255, 255)) + self.assertEqual(eight_bit_surf.get_at((2, 2)), pygame.Color(0, 0, 0)) + self.assertEqual(eight_bit_surf.get_at((3, 3)), pygame.Color(50, 200, 20)) + + def test_frombuffer_RGB(self): + rgb_buffer = bytearray( + [ + 255, + 10, + 20, + 255, + 10, + 20, + 255, + 10, + 20, + 255, + 10, + 20, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 50, + 200, + 20, + 50, + 200, + 20, + 50, + 200, + 20, + 50, + 200, + 20, + ] + ) + + rgb_surf = pygame.image.frombuffer(rgb_buffer, (4, 4), "RGB") + self.assertEqual(rgb_surf.get_at((0, 0)), pygame.Color(255, 10, 20)) + self.assertEqual(rgb_surf.get_at((1, 1)), pygame.Color(255, 255, 255)) + self.assertEqual(rgb_surf.get_at((2, 2)), pygame.Color(0, 0, 0)) + self.assertEqual(rgb_surf.get_at((3, 3)), pygame.Color(50, 200, 20)) + + def test_frombuffer_BGR(self): + bgr_buffer = bytearray( + [ + 20, + 10, + 255, + 20, + 10, + 255, + 20, + 10, + 255, + 20, + 10, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 20, + 200, + 50, + 20, + 200, + 50, + 20, + 200, + 50, + 20, + 200, + 50, + ] + ) + + bgr_surf = pygame.image.frombuffer(bgr_buffer, (4, 4), "BGR") + self.assertEqual(bgr_surf.get_at((0, 0)), pygame.Color(255, 10, 20)) + self.assertEqual(bgr_surf.get_at((1, 1)), pygame.Color(255, 255, 255)) + self.assertEqual(bgr_surf.get_at((2, 2)), pygame.Color(0, 0, 0)) + self.assertEqual(bgr_surf.get_at((3, 3)), pygame.Color(50, 200, 20)) + + def test_frombuffer_BGRA(self): + bgra_buffer = bytearray( + [ + 255, + 10, + 20, + 200, + 255, + 10, + 20, + 200, + 255, + 10, + 20, + 200, + 255, + 10, + 20, + 200, + 255, + 255, + 255, + 127, + 255, + 255, + 255, + 127, + 255, + 255, + 255, + 127, + 255, + 255, + 255, + 127, + 0, + 0, + 0, + 79, + 0, + 0, + 0, + 79, + 0, + 0, + 0, + 79, + 0, + 0, + 0, + 79, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + ] + ) + + bgra_surf = pygame.image.frombuffer(bgra_buffer, (4, 4), "BGRA") + self.assertEqual(bgra_surf.get_at((0, 0)), pygame.Color(20, 10, 255, 200)) + self.assertEqual(bgra_surf.get_at((1, 1)), pygame.Color(255, 255, 255, 127)) + self.assertEqual(bgra_surf.get_at((2, 2)), pygame.Color(0, 0, 0, 79)) + self.assertEqual(bgra_surf.get_at((3, 3)), pygame.Color(20, 200, 50, 255)) + + def test_frombuffer_RGBX(self): + rgbx_buffer = bytearray( + [ + 255, + 10, + 20, + 255, + 255, + 10, + 20, + 255, + 255, + 10, + 20, + 255, + 255, + 10, + 20, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 0, + 0, + 0, + 255, + 0, + 0, + 0, + 255, + 0, + 0, + 0, + 255, + 0, + 0, + 0, + 255, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + ] + ) + + rgbx_surf = pygame.image.frombuffer(rgbx_buffer, (4, 4), "RGBX") + self.assertEqual(rgbx_surf.get_at((0, 0)), pygame.Color(255, 10, 20, 255)) + self.assertEqual(rgbx_surf.get_at((1, 1)), pygame.Color(255, 255, 255, 255)) + self.assertEqual(rgbx_surf.get_at((2, 2)), pygame.Color(0, 0, 0, 255)) + self.assertEqual(rgbx_surf.get_at((3, 3)), pygame.Color(50, 200, 20, 255)) + + def test_frombuffer_RGBA(self): + rgba_buffer = bytearray( + [ + 255, + 10, + 20, + 200, + 255, + 10, + 20, + 200, + 255, + 10, + 20, + 200, + 255, + 10, + 20, + 200, + 255, + 255, + 255, + 127, + 255, + 255, + 255, + 127, + 255, + 255, + 255, + 127, + 255, + 255, + 255, + 127, + 0, + 0, + 0, + 79, + 0, + 0, + 0, + 79, + 0, + 0, + 0, + 79, + 0, + 0, + 0, + 79, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + ] + ) + + rgba_surf = pygame.image.frombuffer(rgba_buffer, (4, 4), "RGBA") + self.assertEqual(rgba_surf.get_at((0, 0)), pygame.Color(255, 10, 20, 200)) + self.assertEqual(rgba_surf.get_at((1, 1)), pygame.Color(255, 255, 255, 127)) + self.assertEqual(rgba_surf.get_at((2, 2)), pygame.Color(0, 0, 0, 79)) + self.assertEqual(rgba_surf.get_at((3, 3)), pygame.Color(50, 200, 20, 255)) + + def test_frombuffer_ARGB(self): + argb_buffer = bytearray( + [ + 200, + 255, + 10, + 20, + 200, + 255, + 10, + 20, + 200, + 255, + 10, + 20, + 200, + 255, + 10, + 20, + 127, + 255, + 255, + 255, + 127, + 255, + 255, + 255, + 127, + 255, + 255, + 255, + 127, + 255, + 255, + 255, + 79, + 0, + 0, + 0, + 79, + 0, + 0, + 0, + 79, + 0, + 0, + 0, + 79, + 0, + 0, + 0, + 255, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + ] + ) + + argb_surf = pygame.image.frombuffer(argb_buffer, (4, 4), "ARGB") + self.assertEqual(argb_surf.get_at((0, 0)), pygame.Color(255, 10, 20, 200)) + self.assertEqual(argb_surf.get_at((1, 1)), pygame.Color(255, 255, 255, 127)) + self.assertEqual(argb_surf.get_at((2, 2)), pygame.Color(0, 0, 0, 79)) + self.assertEqual(argb_surf.get_at((3, 3)), pygame.Color(50, 200, 20, 255)) + + def test_get_extended(self): + # Create a png file and try to load it. If it cannot, get_extended() should return False + raw_image = [] + raw_image.append((200, 200, 200, 255, 100, 100, 100, 255)) + + f_descriptor, f_path = tempfile.mkstemp(suffix=".png") + + with os.fdopen(f_descriptor, "wb") as file: + w = png.Writer(2, 1, alpha=True) + w.write(file, raw_image) + + try: + surf = pygame.image.load(f_path) + loaded = True + except pygame.error: + loaded = False + + self.assertEqual(pygame.image.get_extended(), loaded) + os.remove(f_path) + + def test_get_sdl_image_version(self): + # If get_extended() returns False then get_sdl_image_version() should + # return None + if not pygame.image.get_extended(): + self.assertIsNone(pygame.image.get_sdl_image_version()) + else: + expected_length = 3 + expected_type = tuple + expected_item_type = int + + version = pygame.image.get_sdl_image_version() + + self.assertIsInstance(version, expected_type) + self.assertEqual(len(version), expected_length) + + for item in version: + self.assertIsInstance(item, expected_item_type) + + def test_load_basic(self): + """to see if we can load bmp from files and/or file-like objects in memory""" + + # pygame.image.load(filename): return Surface + + # test loading from a file + s = pygame.image.load_basic(example_path("data/asprite.bmp")) + self.assertEqual(s.get_at((0, 0)), (255, 255, 255, 255)) + + # test loading from io.BufferedReader + f = pygame.pkgdata.getResource("pygame_icon.bmp") + self.assertEqual(f.mode, "rb") + + surf = pygame.image.load_basic(f) + + self.assertEqual(surf.get_at((0, 0)), (5, 4, 5, 255)) + self.assertEqual(surf.get_height(), 32) + self.assertEqual(surf.get_width(), 32) + + f.close() + + def test_load_extended(self): + """can load different format images. + + We test loading the following file types: + bmp, png, jpg, gif (non-animated), pcx, tga (uncompressed), tif, xpm, ppm, pgm + Following file types are tested when using SDL 2 + svg, pnm, webp + All the loaded images are smaller than 32 x 32 pixels. + """ + + filename_expected_color = [ + ("asprite.bmp", (255, 255, 255, 255)), + ("laplacian.png", (10, 10, 70, 255)), + ("red.jpg", (254, 0, 0, 255)), + ("blue.gif", (0, 0, 255, 255)), + ("green.pcx", (0, 255, 0, 255)), + ("yellow.tga", (255, 255, 0, 255)), + ("turquoise.tif", (0, 255, 255, 255)), + ("purple.xpm", (255, 0, 255, 255)), + ("black.ppm", (0, 0, 0, 255)), + ("grey.pgm", (120, 120, 120, 255)), + ("teal.svg", (0, 128, 128, 255)), + ("crimson.pnm", (220, 20, 60, 255)), + ("scarlet.webp", (252, 14, 53, 255)), + ] + + for filename, expected_color in filename_expected_color: + if filename.endswith("svg") and sdl_image_svg_jpeg_save_bug: + # SDL_image 2.0.5 and older has an svg loading bug on big + # endian platforms + continue + + with self.subTest( + f'Test loading a {filename.split(".")[-1]}', + filename="examples/data/" + filename, + expected_color=expected_color, + ): + surf = pygame.image.load_extended(example_path("data/" + filename)) + self.assertEqual(surf.get_at((0, 0)), expected_color) + + def test_load_pathlib(self): + """works loading using a Path argument.""" + path = pathlib.Path(example_path("data/asprite.bmp")) + surf = pygame.image.load_extended(path) + self.assertEqual(surf.get_at((0, 0)), (255, 255, 255, 255)) + + def test_save_extended(self): + surf = pygame.Surface((5, 5)) + surf.fill((23, 23, 23)) + + passing_formats = ["jpg", "png"] + passing_formats += [fmt.upper() for fmt in passing_formats] + + magic_hex = {} + magic_hex["jpg"] = [0xFF, 0xD8, 0xFF, 0xE0] + magic_hex["png"] = [0x89, 0x50, 0x4E, 0x47] + + failing_formats = ["bmp", "tga"] + failing_formats += [fmt.upper() for fmt in failing_formats] + + # check that .jpg and .png save + for fmt in passing_formats: + temp_file_name = f"temp_file.{fmt}" + # save image as .jpg and .png + pygame.image.save_extended(surf, temp_file_name) + with open(temp_file_name, "rb") as file: + # Test the magic numbers at the start of the file to ensure + # they are saved as the correct file type. + self.assertEqual(1, (test_magic(file, magic_hex[fmt.lower()]))) + # load the file to make sure it was saved correctly + loaded_file = pygame.image.load(temp_file_name) + self.assertEqual(loaded_file.get_at((0, 0)), surf.get_at((0, 0))) + # clean up the temp file + os.remove(temp_file_name) + # check that .bmp and .tga do not save + for fmt in failing_formats: + self.assertRaises( + pygame.error, pygame.image.save_extended, surf, f"temp_file.{fmt}" + ) + + def threads_load(self, images): + import pygame.threads + + for i in range(10): + surfs = pygame.threads.tmap(pygame.image.load, images) + for s in surfs: + self.assertIsInstance(s, pygame.Surface) + + def test_load_png_threads(self): + self.threads_load(glob.glob(example_path("data/*.png"))) + + def test_load_jpg_threads(self): + self.threads_load(glob.glob(example_path("data/*.jpg"))) + + def test_load_bmp_threads(self): + self.threads_load(glob.glob(example_path("data/*.bmp"))) + + def test_load_gif_threads(self): + self.threads_load(glob.glob(example_path("data/*.gif"))) + + def test_from_to_bytes_exists(self): + getattr(pygame.image, "frombytes") + getattr(pygame.image, "tobytes") + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/imageext_tags.py b/.venv/Lib/site-packages/pygame/tests/imageext_tags.py new file mode 100644 index 00000000..25cff743 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/imageext_tags.py @@ -0,0 +1,7 @@ +__tags__ = [] + +import pygame +import sys + +if "pygame.imageext" not in sys.modules: + __tags__.extend(("ignore", "subprocess_ignore")) diff --git a/.venv/Lib/site-packages/pygame/tests/imageext_test.py b/.venv/Lib/site-packages/pygame/tests/imageext_test.py new file mode 100644 index 00000000..c5ce7591 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/imageext_test.py @@ -0,0 +1,93 @@ +import os +import os.path +import sys +import unittest + +from pygame.tests.test_utils import example_path +import pygame, pygame.image, pygame.pkgdata + + +imageext = sys.modules["pygame.imageext"] + + +class ImageextModuleTest(unittest.TestCase): + # Most of the testing is done indirectly through image_test.py + # This just confirms file path encoding and error handling. + def test_save_non_string_file(self): + im = pygame.Surface((10, 10), 0, 32) + self.assertRaises(TypeError, imageext.save_extended, im, []) + + def test_load_non_string_file(self): + self.assertRaises(TypeError, imageext.load_extended, []) + + @unittest.skip("SDL silently removes invalid characters") + def test_save_bad_filename(self): + im = pygame.Surface((10, 10), 0, 32) + u = "a\x00b\x00c.png" + self.assertRaises(pygame.error, imageext.save_extended, im, u) + + @unittest.skip("SDL silently removes invalid characters") + def test_load_bad_filename(self): + u = "a\x00b\x00c.png" + self.assertRaises(pygame.error, imageext.load_extended, u) + + def test_save_unknown_extension(self): + im = pygame.Surface((10, 10), 0, 32) + s = "foo.bar" + self.assertRaises(pygame.error, imageext.save_extended, im, s) + + def test_load_unknown_extension(self): + s = "foo.bar" + self.assertRaises(FileNotFoundError, imageext.load_extended, s) + + def test_load_unknown_file(self): + s = "nonexistent.png" + self.assertRaises(FileNotFoundError, imageext.load_extended, s) + + def test_load_unicode_path_0(self): + u = example_path("data/alien1.png") + im = imageext.load_extended(u) + + def test_load_unicode_path_1(self): + """non-ASCII unicode""" + import shutil + + orig = example_path("data/alien1.png") + temp = os.path.join(example_path("data"), "你好.png") + shutil.copy(orig, temp) + try: + im = imageext.load_extended(temp) + finally: + os.remove(temp) + + def _unicode_save(self, temp_file): + im = pygame.Surface((10, 10), 0, 32) + try: + with open(temp_file, "w") as f: + pass + os.remove(temp_file) + except OSError: + raise unittest.SkipTest("the path cannot be opened") + + self.assertFalse(os.path.exists(temp_file)) + + try: + imageext.save_extended(im, temp_file) + + self.assertGreater(os.path.getsize(temp_file), 10) + finally: + try: + os.remove(temp_file) + except OSError: + pass + + def test_save_unicode_path_0(self): + """unicode object with ASCII chars""" + self._unicode_save("temp_file.png") + + def test_save_unicode_path_1(self): + self._unicode_save("你好.png") + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/joystick_test.py b/.venv/Lib/site-packages/pygame/tests/joystick_test.py new file mode 100644 index 00000000..47ce3f84 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/joystick_test.py @@ -0,0 +1,166 @@ +import unittest +from pygame.tests.test_utils import question, prompt + +import pygame +import pygame._sdl2.controller + + +class JoystickTypeTest(unittest.TestCase): + def todo_test_Joystick(self): + # __doc__ (as of 2008-08-02) for pygame.joystick.Joystick: + + # pygame.joystick.Joystick(id): return Joystick + # create a new Joystick object + # + # Create a new joystick to access a physical device. The id argument + # must be a value from 0 to pygame.joystick.get_count()-1. + # + # To access most of the Joystick methods, you'll need to init() the + # Joystick. This is separate from making sure the joystick module is + # initialized. When multiple Joysticks objects are created for the + # same physical joystick device (i.e., they have the same ID number), + # the state and values for those Joystick objects will be shared. + # + # The Joystick object allows you to get information about the types of + # controls on a joystick device. Once the device is initialized the + # Pygame event queue will start receiving events about its input. + # + # You can call the Joystick.get_name() and Joystick.get_id() functions + # without initializing the Joystick object. + # + + self.fail() + + +class JoystickModuleTest(unittest.TestCase): + def test_get_init(self): + # Check that get_init() matches what is actually happening + def error_check_get_init(): + try: + pygame.joystick.get_count() + except pygame.error: + return False + return True + + # Start uninitialised + self.assertEqual(pygame.joystick.get_init(), False) + + pygame.joystick.init() + self.assertEqual(pygame.joystick.get_init(), error_check_get_init()) # True + pygame.joystick.quit() + self.assertEqual(pygame.joystick.get_init(), error_check_get_init()) # False + + pygame.joystick.init() + pygame.joystick.init() + self.assertEqual(pygame.joystick.get_init(), error_check_get_init()) # True + pygame.joystick.quit() + self.assertEqual(pygame.joystick.get_init(), error_check_get_init()) # False + + pygame.joystick.quit() + self.assertEqual(pygame.joystick.get_init(), error_check_get_init()) # False + + for i in range(100): + pygame.joystick.init() + self.assertEqual(pygame.joystick.get_init(), error_check_get_init()) # True + pygame.joystick.quit() + self.assertEqual(pygame.joystick.get_init(), error_check_get_init()) # False + + for i in range(100): + pygame.joystick.quit() + self.assertEqual(pygame.joystick.get_init(), error_check_get_init()) # False + + def test_init(self): + """ + This unit test is for joystick.init() + It was written to help reduce maintenance costs + and to help test against changes to the code or + different platforms. + """ + pygame.quit() + # test that pygame.init automatically calls joystick.init + pygame.init() + self.assertEqual(pygame.joystick.get_init(), True) + + # Controller module interferes with the joystick module. + pygame._sdl2.controller.quit() + + # test that get_count doesn't work w/o joystick init + # this is done before and after an init to test + # that init activates the joystick functions + pygame.joystick.quit() + with self.assertRaises(pygame.error): + pygame.joystick.get_count() + + # test explicit call(s) to joystick.init. + # Also test that get_count works once init is called + iterations = 20 + for i in range(iterations): + pygame.joystick.init() + self.assertEqual(pygame.joystick.get_init(), True) + self.assertIsNotNone(pygame.joystick.get_count()) + + def test_quit(self): + """Test if joystick.quit works.""" + + pygame.joystick.init() + + self.assertIsNotNone(pygame.joystick.get_count()) # Is not None before quit + + pygame.joystick.quit() + + with self.assertRaises(pygame.error): # Raises error if quit worked + pygame.joystick.get_count() + + def test_get_count(self): + # Test that get_count correctly returns a non-negative number of joysticks + pygame.joystick.init() + + try: + count = pygame.joystick.get_count() + self.assertGreaterEqual( + count, 0, ("joystick.get_count() must " "return a value >= 0") + ) + finally: + pygame.joystick.quit() + + +class JoystickInteractiveTest(unittest.TestCase): + __tags__ = ["interactive"] + + def test_get_count_interactive(self): + # Test get_count correctly identifies number of connected joysticks + prompt( + "Please connect any joysticks/controllers now before starting the " + "joystick.get_count() test." + ) + + pygame.joystick.init() + # pygame.joystick.get_count(): return count + # number of joysticks on the system, 0 means no joysticks connected + count = pygame.joystick.get_count() + + response = question( + "NOTE: Having Steam open may add an extra virtual controller for " + "each joystick/controller physically plugged in.\n" + f"joystick.get_count() thinks there is [{count}] joystick(s)/controller(s)" + "connected to this system. Is this correct?" + ) + + self.assertTrue(response) + + # When you create Joystick objects using Joystick(id), you pass an + # integer that must be lower than this count. + # Test Joystick(id) for each connected joystick + if count != 0: + for x in range(count): + pygame.joystick.Joystick(x) + with self.assertRaises(pygame.error): + pygame.joystick.Joystick(count) + + pygame.joystick.quit() + + +################################################################################ + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/key_test.py b/.venv/Lib/site-packages/pygame/tests/key_test.py new file mode 100644 index 00000000..1899c73f --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/key_test.py @@ -0,0 +1,306 @@ +import os +import time +import unittest + +import pygame +import pygame.key + +# keys that are not tested for const-name match +SKIPPED_KEYS = {"K_UNKNOWN"} + +# This is the expected compat output +KEY_NAME_COMPAT = { + "K_0": "0", + "K_1": "1", + "K_2": "2", + "K_3": "3", + "K_4": "4", + "K_5": "5", + "K_6": "6", + "K_7": "7", + "K_8": "8", + "K_9": "9", + "K_AC_BACK": "AC Back", + "K_AMPERSAND": "&", + "K_ASTERISK": "*", + "K_AT": "@", + "K_BACKQUOTE": "`", + "K_BACKSLASH": "\\", + "K_BACKSPACE": "backspace", + "K_BREAK": "break", + "K_CAPSLOCK": "caps lock", + "K_CARET": "^", + "K_CLEAR": "clear", + "K_COLON": ":", + "K_COMMA": ",", + "K_CURRENCYSUBUNIT": "CurrencySubUnit", + "K_CURRENCYUNIT": "euro", + "K_DELETE": "delete", + "K_DOLLAR": "$", + "K_DOWN": "down", + "K_END": "end", + "K_EQUALS": "=", + "K_ESCAPE": "escape", + "K_EURO": "euro", + "K_EXCLAIM": "!", + "K_F1": "f1", + "K_F10": "f10", + "K_F11": "f11", + "K_F12": "f12", + "K_F13": "f13", + "K_F14": "f14", + "K_F15": "f15", + "K_F2": "f2", + "K_F3": "f3", + "K_F4": "f4", + "K_F5": "f5", + "K_F6": "f6", + "K_F7": "f7", + "K_F8": "f8", + "K_F9": "f9", + "K_GREATER": ">", + "K_HASH": "#", + "K_HELP": "help", + "K_HOME": "home", + "K_INSERT": "insert", + "K_KP0": "[0]", + "K_KP1": "[1]", + "K_KP2": "[2]", + "K_KP3": "[3]", + "K_KP4": "[4]", + "K_KP5": "[5]", + "K_KP6": "[6]", + "K_KP7": "[7]", + "K_KP8": "[8]", + "K_KP9": "[9]", + "K_KP_0": "[0]", + "K_KP_1": "[1]", + "K_KP_2": "[2]", + "K_KP_3": "[3]", + "K_KP_4": "[4]", + "K_KP_5": "[5]", + "K_KP_6": "[6]", + "K_KP_7": "[7]", + "K_KP_8": "[8]", + "K_KP_9": "[9]", + "K_KP_DIVIDE": "[/]", + "K_KP_ENTER": "enter", + "K_KP_EQUALS": "equals", + "K_KP_MINUS": "[-]", + "K_KP_MULTIPLY": "[*]", + "K_KP_PERIOD": "[.]", + "K_KP_PLUS": "[+]", + "K_LALT": "left alt", + "K_LCTRL": "left ctrl", + "K_LEFT": "left", + "K_LEFTBRACKET": "[", + "K_LEFTPAREN": "(", + "K_LESS": "<", + "K_LGUI": "left meta", + "K_LMETA": "left meta", + "K_LSHIFT": "left shift", + "K_LSUPER": "left meta", + "K_MENU": "menu", + "K_MINUS": "-", + "K_MODE": "alt gr", + "K_NUMLOCK": "numlock", + "K_NUMLOCKCLEAR": "numlock", + "K_PAGEDOWN": "page down", + "K_PAGEUP": "page up", + "K_PAUSE": "break", + "K_PERCENT": "%", + "K_PERIOD": ".", + "K_PLUS": "+", + "K_POWER": "power", + "K_PRINT": "print screen", + "K_PRINTSCREEN": "print screen", + "K_QUESTION": "?", + "K_QUOTE": "'", + "K_QUOTEDBL": '"', + "K_RALT": "right alt", + "K_RCTRL": "right ctrl", + "K_RETURN": "return", + "K_RGUI": "right meta", + "K_RIGHT": "right", + "K_RIGHTBRACKET": "]", + "K_RIGHTPAREN": ")", + "K_RMETA": "right meta", + "K_RSHIFT": "right shift", + "K_RSUPER": "right meta", + "K_SCROLLLOCK": "scroll lock", + "K_SCROLLOCK": "scroll lock", + "K_SEMICOLON": ";", + "K_SLASH": "/", + "K_SPACE": "space", + "K_SYSREQ": "sys req", + "K_TAB": "tab", + "K_UNDERSCORE": "_", + "K_UP": "up", + "K_a": "a", + "K_b": "b", + "K_c": "c", + "K_d": "d", + "K_e": "e", + "K_f": "f", + "K_g": "g", + "K_h": "h", + "K_i": "i", + "K_j": "j", + "K_k": "k", + "K_l": "l", + "K_m": "m", + "K_n": "n", + "K_o": "o", + "K_p": "p", + "K_q": "q", + "K_r": "r", + "K_s": "s", + "K_t": "t", + "K_u": "u", + "K_v": "v", + "K_w": "w", + "K_x": "x", + "K_y": "y", + "K_z": "z", +} + + +class KeyModuleTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + pygame.init() + + @classmethod + def tearDownClass(cls): + pygame.quit() + + def setUp(self): + # This makes sure pygame is always initialized before each test (in + # case a test calls pygame.quit()). + if not pygame.get_init(): + pygame.init() + if not pygame.display.get_init(): + pygame.display.init() + + def test_import(self): + """does it import?""" + import pygame.key + + # fixme: test_get_focused failing systematically in some linux + # fixme: test_get_focused failing on SDL 2.0.18 on Windows + @unittest.skip("flaky test, and broken on 2.0.18 windows") + def test_get_focused(self): + # This test fails in SDL2 in some linux + # This test was skipped in SDL1. + focused = pygame.key.get_focused() + self.assertFalse(focused) # No window to focus + self.assertIsInstance(focused, int) + # Dummy video driver never gets keyboard focus. + if os.environ.get("SDL_VIDEODRIVER") != "dummy": + # Positive test, fullscreen with events grabbed + display_sizes = pygame.display.list_modes() + if display_sizes == -1: + display_sizes = [(500, 500)] + pygame.display.set_mode(size=display_sizes[-1], flags=pygame.FULLSCREEN) + pygame.event.set_grab(True) + # Pump event queue to get window focus on macos + pygame.event.pump() + focused = pygame.key.get_focused() + self.assertIsInstance(focused, int) + self.assertTrue(focused) + # Now test negative, iconify takes away focus + pygame.event.clear() + # TODO: iconify test fails in windows + if os.name != "nt": + pygame.display.iconify() + # Apparent need to pump event queue in order to make sure iconify + # happens. See display_test.py's test_get_active_iconify + for _ in range(50): + time.sleep(0.01) + pygame.event.pump() + self.assertFalse(pygame.key.get_focused()) + # Test if focus is returned when iconify is gone + pygame.display.set_mode(size=display_sizes[-1], flags=pygame.FULLSCREEN) + for i in range(50): + time.sleep(0.01) + pygame.event.pump() + self.assertTrue(pygame.key.get_focused()) + # Test if a quit display raises an error: + pygame.display.quit() + with self.assertRaises(pygame.error) as cm: + pygame.key.get_focused() + + def test_get_pressed(self): + states = pygame.key.get_pressed() + self.assertEqual(states[pygame.K_RIGHT], 0) + + # def test_get_pressed_not_iter(self): + # states = pygame.key.get_pressed() + # with self.assertRaises(TypeError): + # next(states) + # with self.assertRaises(TypeError): + # for k in states: + # pass + + def test_name_and_key_code(self): + for const_name in dir(pygame): + if not const_name.startswith("K_") or const_name in SKIPPED_KEYS: + continue + + try: + expected_str_name = KEY_NAME_COMPAT[const_name] + except KeyError: + self.fail( + "If you are seeing this error in a test run, you probably added a " + "new pygame key constant, but forgot to update key_test unitests" + ) + + const_val = getattr(pygame, const_name) + + # with these tests below, we also make sure that key.name and key.key_code + # can work together and handle each others outputs + + # test positional args + self.assertEqual(pygame.key.name(const_val), expected_str_name) + # test kwarg + self.assertEqual(pygame.key.name(key=const_val), expected_str_name) + + # test positional args + self.assertEqual(pygame.key.key_code(expected_str_name), const_val) + # test kwarg + self.assertEqual(pygame.key.key_code(name=expected_str_name), const_val) + + alt_name = pygame.key.name(const_val, use_compat=False) + self.assertIsInstance(alt_name, str) + + # This is a test for an implementation detail of name with use_compat=False + # If this test breaks in the future for any key, it is safe to put skips on + # failing keys (the implementation detail is documented as being unreliable) + self.assertEqual(pygame.key.key_code(alt_name), const_val) + + self.assertRaises(TypeError, pygame.key.name, "fizzbuzz") + self.assertRaises(TypeError, pygame.key.key_code, pygame.K_a) + + self.assertRaises(ValueError, pygame.key.key_code, "fizzbuzz") + + def test_set_and_get_mods(self): + pygame.key.set_mods(pygame.KMOD_CTRL) + self.assertEqual(pygame.key.get_mods(), pygame.KMOD_CTRL) + + pygame.key.set_mods(pygame.KMOD_ALT) + self.assertEqual(pygame.key.get_mods(), pygame.KMOD_ALT) + pygame.key.set_mods(pygame.KMOD_CTRL | pygame.KMOD_ALT) + self.assertEqual(pygame.key.get_mods(), pygame.KMOD_CTRL | pygame.KMOD_ALT) + + def test_set_and_get_repeat(self): + self.assertEqual(pygame.key.get_repeat(), (0, 0)) + + pygame.key.set_repeat(10, 15) + self.assertEqual(pygame.key.get_repeat(), (10, 15)) + + pygame.key.set_repeat() + self.assertEqual(pygame.key.get_repeat(), (0, 0)) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/locals_test.py b/.venv/Lib/site-packages/pygame/tests/locals_test.py new file mode 100644 index 00000000..973c46d4 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/locals_test.py @@ -0,0 +1,17 @@ +import unittest + +import pygame.constants +import pygame.locals + + +class LocalsTest(unittest.TestCase): + def test_locals_has_all_constants(self): + constants_set = set(pygame.constants.__all__) + locals_set = set(pygame.locals.__all__) + + # locals should have everything that constants has + self.assertEqual(constants_set - locals_set, set()) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/mask_test.py b/.venv/Lib/site-packages/pygame/tests/mask_test.py new file mode 100644 index 00000000..bd7daf5f --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/mask_test.py @@ -0,0 +1,6441 @@ +from collections import OrderedDict +import copy +import platform +import random +import unittest +import sys + +import pygame +from pygame.locals import * +from pygame.math import Vector2 + + +IS_PYPY = "PyPy" == platform.python_implementation() + + +def random_mask(size=(100, 100)): + """random_mask(size=(100,100)): return Mask + Create a mask of the given size, with roughly half the bits set at random.""" + m = pygame.Mask(size) + for i in range(size[0] * size[1] // 2): + x, y = random.randint(0, size[0] - 1), random.randint(0, size[1] - 1) + m.set_at((x, y)) + return m + + +def maskFromSurface(surface, threshold=127): + mask = pygame.Mask(surface.get_size()) + key = surface.get_colorkey() + if key: + for y in range(surface.get_height()): + for x in range(surface.get_width()): + if surface.get_at((x + 0.1, y + 0.1)) != key: + mask.set_at((x, y), 1) + else: + for y in range(surface.get_height()): + for x in range(surface.get_width()): + if surface.get_at((x, y))[3] > threshold: + mask.set_at((x, y), 1) + return mask + + +def create_bounding_rect(points): + """Creates a bounding rect from the given points.""" + xmin = xmax = points[0][0] + ymin = ymax = points[0][1] + + for x, y in points[1:]: + xmin = min(x, xmin) + xmax = max(x, xmax) + ymin = min(y, ymin) + ymax = max(y, ymax) + + return pygame.Rect((xmin, ymin), (xmax - xmin + 1, ymax - ymin + 1)) + + +def zero_size_pairs(width, height): + """Creates a generator which yields pairs of sizes. + + For each pair of sizes at least one of the sizes will have a 0 in it. + """ + sizes = ((width, height), (width, 0), (0, height), (0, 0)) + + return ((a, b) for a in sizes for b in sizes if 0 in a or 0 in b) + + +def corners(mask): + """Returns a tuple with the corner positions of the given mask. + + Clockwise from the top left corner. + """ + width, height = mask.get_size() + return ((0, 0), (width - 1, 0), (width - 1, height - 1), (0, height - 1)) + + +def off_corners(rect): + """Returns a tuple with the positions off of the corners of the given rect. + + Clockwise from the top left corner. + """ + return ( + (rect.left - 1, rect.top), + (rect.left - 1, rect.top - 1), + (rect.left, rect.top - 1), + (rect.right - 1, rect.top - 1), + (rect.right, rect.top - 1), + (rect.right, rect.top), + (rect.right, rect.bottom - 1), + (rect.right, rect.bottom), + (rect.right - 1, rect.bottom), + (rect.left, rect.bottom), + (rect.left - 1, rect.bottom), + (rect.left - 1, rect.bottom - 1), + ) + + +def assertSurfaceFilled(testcase, surface, expected_color, area_rect=None): + """Checks to see if the given surface is filled with the given color. + + If an area_rect is provided, only check that area of the surface. + """ + if area_rect is None: + x_range = range(surface.get_width()) + y_range = range(surface.get_height()) + else: + area_rect.normalize() + area_rect = area_rect.clip(surface.get_rect()) + x_range = range(area_rect.left, area_rect.right) + y_range = range(area_rect.top, area_rect.bottom) + + surface.lock() # Lock for possible speed up. + for pos in ((x, y) for y in y_range for x in x_range): + testcase.assertEqual(surface.get_at(pos), expected_color, pos) + surface.unlock() + + +def assertSurfaceFilledIgnoreArea(testcase, surface, expected_color, ignore_rect): + """Checks if the surface is filled with the given color. The + ignore_rect area is not checked. + """ + x_range = range(surface.get_width()) + y_range = range(surface.get_height()) + ignore_rect.normalize() + + surface.lock() # Lock for possible speed up. + for pos in ((x, y) for y in y_range for x in x_range): + if not ignore_rect.collidepoint(pos): + testcase.assertEqual(surface.get_at(pos), expected_color, pos) + surface.unlock() + + +def assertMaskEqual(testcase, m1, m2, msg=None): + """Checks to see if the 2 given masks are equal.""" + m1_count = m1.count() + + testcase.assertEqual(m1.get_size(), m2.get_size(), msg=msg) + testcase.assertEqual(m1_count, m2.count(), msg=msg) + testcase.assertEqual(m1_count, m1.overlap_area(m2, (0, 0)), msg=msg) + + # This can be used to help debug exact locations. + ##for i in range(m1.get_size()[0]): + ## for j in range(m1.get_size()[1]): + ## testcase.assertEqual(m1.get_at((i, j)), m2.get_at((i, j))) + + +# @unittest.skipIf(IS_PYPY, "pypy has lots of mask failures") # TODO +class MaskTypeTest(unittest.TestCase): + ORIGIN_OFFSETS = ( + (0, 0), + (0, 1), + (1, 1), + (1, 0), + (1, -1), + (0, -1), + (-1, -1), + (-1, 0), + (-1, 1), + ) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_mask(self): + """Ensure masks are created correctly without fill parameter.""" + expected_count = 0 + expected_size = (11, 23) + + mask1 = pygame.mask.Mask(expected_size) + mask2 = pygame.mask.Mask(size=expected_size) + + self.assertIsInstance(mask1, pygame.mask.Mask) + self.assertEqual(mask1.count(), expected_count) + self.assertEqual(mask1.get_size(), expected_size) + + self.assertIsInstance(mask2, pygame.mask.Mask) + self.assertEqual(mask2.count(), expected_count) + self.assertEqual(mask2.get_size(), expected_size) + + def test_mask__negative_size(self): + """Ensure the mask constructor handles negative sizes correctly.""" + for size in ((1, -1), (-1, 1), (-1, -1)): + with self.assertRaises(ValueError): + mask = pygame.Mask(size) + + def test_mask__fill_kwarg(self): + """Ensure masks are created correctly using the fill keyword.""" + width, height = 37, 47 + expected_size = (width, height) + fill_counts = {True: width * height, False: 0} + + for fill, expected_count in fill_counts.items(): + msg = f"fill={fill}" + + mask = pygame.mask.Mask(expected_size, fill=fill) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.count(), expected_count, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_mask__fill_kwarg_bit_boundaries(self): + """Ensures masks are created correctly using the fill keyword + over a range of sizes. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for height in range(1, 4): + for width in range(1, 66): + expected_count = width * height + expected_size = (width, height) + msg = f"size={expected_size}" + + mask = pygame.mask.Mask(expected_size, fill=True) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.count(), expected_count, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + + def test_mask__fill_arg(self): + """Ensure masks are created correctly using a fill arg.""" + width, height = 59, 71 + expected_size = (width, height) + fill_counts = {True: width * height, False: 0} + + for fill, expected_count in fill_counts.items(): + msg = f"fill={fill}" + + mask = pygame.mask.Mask(expected_size, fill) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.count(), expected_count, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + + def test_mask__size_kwarg(self): + """Ensure masks are created correctly using the size keyword.""" + width, height = 73, 83 + expected_size = (width, height) + fill_counts = {True: width * height, False: 0} + + for fill, expected_count in fill_counts.items(): + msg = f"fill={fill}" + + mask1 = pygame.mask.Mask(fill=fill, size=expected_size) + mask2 = pygame.mask.Mask(size=expected_size, fill=fill) + + self.assertIsInstance(mask1, pygame.mask.Mask, msg) + self.assertIsInstance(mask2, pygame.mask.Mask, msg) + self.assertEqual(mask1.count(), expected_count, msg) + self.assertEqual(mask2.count(), expected_count, msg) + self.assertEqual(mask1.get_size(), expected_size, msg) + self.assertEqual(mask2.get_size(), expected_size, msg) + + def test_copy(self): + """Ensures copy works correctly with some bits set and unset.""" + # Test different widths and heights. + for width in (31, 32, 33, 63, 64, 65): + for height in (31, 32, 33, 63, 64, 65): + mask = pygame.mask.Mask((width, height)) + + # Create a checkerboard pattern of set/unset bits. + for x in range(width): + for y in range(x & 1, height, 2): + mask.set_at((x, y)) + + # Test both the copy() and __copy__() methods. + for mask_copy in (mask.copy(), copy.copy(mask)): + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + + def test_copy__full(self): + """Ensures copy works correctly on a filled masked.""" + # Test different widths and heights. + for width in (31, 32, 33, 63, 64, 65): + for height in (31, 32, 33, 63, 64, 65): + mask = pygame.mask.Mask((width, height), fill=True) + + # Test both the copy() and __copy__() methods. + for mask_copy in (mask.copy(), copy.copy(mask)): + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + + def test_copy__empty(self): + """Ensures copy works correctly on an empty mask.""" + for width in (31, 32, 33, 63, 64, 65): + for height in (31, 32, 33, 63, 64, 65): + mask = pygame.mask.Mask((width, height)) + + # Test both the copy() and __copy__() methods. + for mask_copy in (mask.copy(), copy.copy(mask)): + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + + def test_copy__independent(self): + """Ensures copy makes an independent copy of the mask.""" + mask_set_pos = (64, 1) + mask_copy_set_pos = (64, 2) + mask = pygame.mask.Mask((65, 3)) + + # Test both the copy() and __copy__() methods. + mask_copies = (mask.copy(), copy.copy(mask)) + mask.set_at(mask_set_pos) + + for mask_copy in mask_copies: + mask_copy.set_at(mask_copy_set_pos) + + self.assertIsNot(mask_copy, mask) + self.assertNotEqual( + mask_copy.get_at(mask_set_pos), mask.get_at(mask_set_pos) + ) + self.assertNotEqual( + mask_copy.get_at(mask_copy_set_pos), mask.get_at(mask_copy_set_pos) + ) + + def test_get_size(self): + """Ensure a mask's size is correctly retrieved.""" + expected_size = (93, 101) + mask = pygame.mask.Mask(expected_size) + + self.assertEqual(mask.get_size(), expected_size) + + def test_get_rect(self): + """Ensures get_rect works correctly.""" + expected_rect = pygame.Rect((0, 0), (11, 13)) + + # Test on full and empty masks. + for fill in (True, False): + mask = pygame.mask.Mask(expected_rect.size, fill=fill) + + rect = mask.get_rect() + + self.assertEqual(rect, expected_rect) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_get_rect__one_kwarg(self): + """Ensures get_rect supports a single rect attribute kwarg. + + Tests all the rect attributes. + """ + # Rect attributes that take a single value. + RECT_SINGLE_VALUE_ATTRIBUTES = ( + "x", + "y", + "top", + "left", + "bottom", + "right", + "centerx", + "centery", + "width", + "height", + "w", + "h", + ) + + # Rect attributes that take 2 values. + RECT_DOUBLE_VALUE_ATTRIBUTES = ( + "topleft", + "bottomleft", + "topright", + "bottomright", + "midtop", + "midleft", + "midbottom", + "midright", + "center", + "size", + ) + + # Testing ints/floats and tuples/lists/Vector2s. + # {attribute_names : attribute_values} + rect_attributes = { + RECT_SINGLE_VALUE_ATTRIBUTES: (3, 5.1), + RECT_DOUBLE_VALUE_ATTRIBUTES: ((1, 2.2), [2.3, 3], Vector2(0, 1)), + } + + size = (7, 3) + mask = pygame.mask.Mask(size) + + for attributes, values in rect_attributes.items(): + for attribute in attributes: + for value in values: + expected_rect = pygame.Rect((0, 0), size) + setattr(expected_rect, attribute, value) + + rect = mask.get_rect(**{attribute: value}) + + self.assertEqual(rect, expected_rect) + + def test_get_rect__multiple_kwargs(self): + """Ensures get_rect supports multiple rect attribute kwargs.""" + mask = pygame.mask.Mask((5, 4)) + expected_rect = pygame.Rect((0, 0), (0, 0)) + kwargs = {"x": 7.1, "top": -1, "size": Vector2(2, 3.2)} + + for attrib, value in kwargs.items(): + setattr(expected_rect, attrib, value) + + rect = mask.get_rect(**kwargs) + + self.assertEqual(rect, expected_rect) + + def test_get_rect__no_arg_support(self): + """Ensures get_rect only supports kwargs.""" + mask = pygame.mask.Mask((4, 5)) + + with self.assertRaises(TypeError): + rect = mask.get_rect(3) + + with self.assertRaises(TypeError): + rect = mask.get_rect((1, 2)) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_get_rect__invalid_kwarg_name(self): + """Ensures get_rect detects invalid kwargs.""" + mask = pygame.mask.Mask((1, 2)) + + with self.assertRaises(AttributeError): + rect = mask.get_rect(righte=11) + + with self.assertRaises(AttributeError): + rect = mask.get_rect(toplef=(1, 1)) + + with self.assertRaises(AttributeError): + rect = mask.get_rect(move=(3, 2)) + + def test_get_rect__invalid_kwarg_format(self): + """Ensures get_rect detects invalid kwarg formats.""" + mask = pygame.mask.Mask((3, 11)) + + with self.assertRaises(TypeError): + rect = mask.get_rect(right="1") # Wrong type. + + with self.assertRaises(TypeError): + rect = mask.get_rect(bottom=(1,)) # Wrong type. + + with self.assertRaises(TypeError): + rect = mask.get_rect(centerx=(1, 1)) # Wrong type. + + with self.assertRaises(TypeError): + rect = mask.get_rect(midleft=(1, "1")) # Wrong type. + + with self.assertRaises(TypeError): + rect = mask.get_rect(topright=(1,)) # Too few. + + with self.assertRaises(TypeError): + rect = mask.get_rect(bottomleft=(1, 2, 3)) # Too many. + + with self.assertRaises(TypeError): + rect = mask.get_rect(midbottom=1) # Wrong type. + + def test_get_at(self): + """Ensure individual mask bits are correctly retrieved.""" + width, height = 5, 7 + mask0 = pygame.mask.Mask((width, height)) + mask1 = pygame.mask.Mask((width, height), fill=True) + mask0_expected_bit = 0 + mask1_expected_bit = 1 + pos = (width - 1, height - 1) + + # Check twice to make sure bits aren't toggled. + self.assertEqual(mask0.get_at(pos), mask0_expected_bit) + self.assertEqual(mask0.get_at(pos=pos), mask0_expected_bit) + self.assertEqual(mask1.get_at(Vector2(pos)), mask1_expected_bit) + self.assertEqual(mask1.get_at(pos=Vector2(pos)), mask1_expected_bit) + + def test_get_at__out_of_bounds(self): + """Ensure get_at() checks bounds.""" + width, height = 11, 3 + mask = pygame.mask.Mask((width, height)) + + with self.assertRaises(IndexError): + mask.get_at((width, 0)) + + with self.assertRaises(IndexError): + mask.get_at((0, height)) + + with self.assertRaises(IndexError): + mask.get_at((-1, 0)) + + with self.assertRaises(IndexError): + mask.get_at((0, -1)) + + def test_set_at(self): + """Ensure individual mask bits are set to 1.""" + width, height = 13, 17 + mask0 = pygame.mask.Mask((width, height)) + mask1 = pygame.mask.Mask((width, height), fill=True) + mask0_expected_count = 1 + mask1_expected_count = mask1.count() + expected_bit = 1 + pos = (width - 1, height - 1) + + mask0.set_at(pos, expected_bit) # set 0 to 1 + mask1.set_at(pos=Vector2(pos), value=expected_bit) # set 1 to 1 + + self.assertEqual(mask0.get_at(pos), expected_bit) + self.assertEqual(mask0.count(), mask0_expected_count) + self.assertEqual(mask1.get_at(pos), expected_bit) + self.assertEqual(mask1.count(), mask1_expected_count) + + def test_set_at__to_0(self): + """Ensure individual mask bits are set to 0.""" + width, height = 11, 7 + mask0 = pygame.mask.Mask((width, height)) + mask1 = pygame.mask.Mask((width, height), fill=True) + mask0_expected_count = 0 + mask1_expected_count = mask1.count() - 1 + expected_bit = 0 + pos = (width - 1, height - 1) + + mask0.set_at(pos, expected_bit) # set 0 to 0 + mask1.set_at(pos, expected_bit) # set 1 to 0 + + self.assertEqual(mask0.get_at(pos), expected_bit) + self.assertEqual(mask0.count(), mask0_expected_count) + self.assertEqual(mask1.get_at(pos), expected_bit) + self.assertEqual(mask1.count(), mask1_expected_count) + + def test_set_at__default_value(self): + """Ensure individual mask bits are set using the default value.""" + width, height = 3, 21 + mask0 = pygame.mask.Mask((width, height)) + mask1 = pygame.mask.Mask((width, height), fill=True) + mask0_expected_count = 1 + mask1_expected_count = mask1.count() + expected_bit = 1 + pos = (width - 1, height - 1) + + mask0.set_at(pos) # set 0 to 1 + mask1.set_at(pos) # set 1 to 1 + + self.assertEqual(mask0.get_at(pos), expected_bit) + self.assertEqual(mask0.count(), mask0_expected_count) + self.assertEqual(mask1.get_at(pos), expected_bit) + self.assertEqual(mask1.count(), mask1_expected_count) + + def test_set_at__out_of_bounds(self): + """Ensure set_at() checks bounds.""" + width, height = 11, 3 + mask = pygame.mask.Mask((width, height)) + + with self.assertRaises(IndexError): + mask.set_at((width, 0)) + + with self.assertRaises(IndexError): + mask.set_at((0, height)) + + with self.assertRaises(IndexError): + mask.set_at((-1, 0)) + + with self.assertRaises(IndexError): + mask.set_at((0, -1)) + + def test_overlap(self): + """Ensure the overlap intersection is correctly calculated. + + Testing the different combinations of full/empty masks: + (mask1-filled) 1 overlap 1 (mask2-filled) + (mask1-empty) 0 overlap 1 (mask2-filled) + (mask1-filled) 1 overlap 0 (mask2-empty) + (mask1-empty) 0 overlap 0 (mask2-empty) + """ + expected_size = (4, 4) + offset = (0, 0) + expected_default = None + expected_overlaps = {(True, True): offset} + + for fill2 in (True, False): + mask2 = pygame.mask.Mask(expected_size, fill=fill2) + mask2_count = mask2.count() + + for fill1 in (True, False): + key = (fill1, fill2) + msg = f"key={key}" + mask1 = pygame.mask.Mask(expected_size, fill=fill1) + mask1_count = mask1.count() + expected_pos = expected_overlaps.get(key, expected_default) + + overlap_pos = mask1.overlap(mask2, offset) + + self.assertEqual(overlap_pos, expected_pos, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), expected_size, msg) + self.assertEqual(mask2.get_size(), expected_size, msg) + + def test_overlap__offset(self): + """Ensure an offset overlap intersection is correctly calculated.""" + mask1 = pygame.mask.Mask((65, 3), fill=True) + mask2 = pygame.mask.Mask((66, 4), fill=True) + mask1_count = mask1.count() + mask2_count = mask2.count() + mask1_size = mask1.get_size() + mask2_size = mask2.get_size() + + for offset in self.ORIGIN_OFFSETS: + msg = f"offset={offset}" + expected_pos = (max(offset[0], 0), max(offset[1], 0)) + + overlap_pos = mask1.overlap(other=mask2, offset=offset) + + self.assertEqual(overlap_pos, expected_pos, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), mask1_size, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + + def test_overlap__offset_with_unset_bits(self): + """Ensure an offset overlap intersection is correctly calculated + when (0, 0) bits not set.""" + mask1 = pygame.mask.Mask((65, 3), fill=True) + mask2 = pygame.mask.Mask((66, 4), fill=True) + unset_pos = (0, 0) + mask1.set_at(unset_pos, 0) + mask2.set_at(unset_pos, 0) + mask1_count = mask1.count() + mask2_count = mask2.count() + mask1_size = mask1.get_size() + mask2_size = mask2.get_size() + + for offset in self.ORIGIN_OFFSETS: + msg = f"offset={offset}" + x, y = offset + expected_y = max(y, 0) + if 0 == y: + expected_x = max(x + 1, 1) + elif 0 < y: + expected_x = max(x + 1, 0) + else: + expected_x = max(x, 1) + + overlap_pos = mask1.overlap(mask2, Vector2(offset)) + + self.assertEqual(overlap_pos, (expected_x, expected_y), msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), mask1_size, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + self.assertEqual(mask1.get_at(unset_pos), 0, msg) + self.assertEqual(mask2.get_at(unset_pos), 0, msg) + + def test_overlap__no_overlap(self): + """Ensure an offset overlap intersection is correctly calculated + when there is no overlap.""" + mask1 = pygame.mask.Mask((65, 3), fill=True) + mask1_count = mask1.count() + mask1_size = mask1.get_size() + + mask2_w, mask2_h = 67, 5 + mask2_size = (mask2_w, mask2_h) + mask2 = pygame.mask.Mask(mask2_size) + set_pos = (mask2_w - 1, mask2_h - 1) + mask2.set_at(set_pos) + mask2_count = 1 + + for offset in self.ORIGIN_OFFSETS: + msg = f"offset={offset}" + + overlap_pos = mask1.overlap(mask2, offset) + + self.assertIsNone(overlap_pos, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), mask1_size, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + self.assertEqual(mask2.get_at(set_pos), 1, msg) + + def test_overlap__offset_boundary(self): + """Ensures overlap handles offsets and boundaries correctly.""" + mask1 = pygame.mask.Mask((13, 3), fill=True) + mask2 = pygame.mask.Mask((7, 5), fill=True) + mask1_count = mask1.count() + mask2_count = mask2.count() + mask1_size = mask1.get_size() + mask2_size = mask2.get_size() + + # Check the 4 boundaries. + offsets = ( + (mask1_size[0], 0), # off right + (0, mask1_size[1]), # off bottom + (-mask2_size[0], 0), # off left + (0, -mask2_size[1]), + ) # off top + + for offset in offsets: + msg = f"offset={offset}" + + overlap_pos = mask1.overlap(mask2, offset) + + self.assertIsNone(overlap_pos, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), mask1_size, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_overlap__bit_boundaries(self): + """Ensures overlap handles masks of different sizes correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for height in range(2, 4): + for width in range(2, 66): + mask_size = (width, height) + mask_count = width * height + mask1 = pygame.mask.Mask(mask_size, fill=True) + mask2 = pygame.mask.Mask(mask_size, fill=True) + + # Testing masks offset from each other. + for offset in self.ORIGIN_OFFSETS: + msg = f"size={mask_size}, offset={offset}" + expected_pos = (max(offset[0], 0), max(offset[1], 0)) + + overlap_pos = mask1.overlap(mask2, offset) + + self.assertEqual(overlap_pos, expected_pos, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask_count, msg) + self.assertEqual(mask2.count(), mask_count, msg) + self.assertEqual(mask1.get_size(), mask_size, msg) + self.assertEqual(mask2.get_size(), mask_size, msg) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_overlap__invalid_mask_arg(self): + """Ensure overlap handles invalid mask arguments correctly.""" + size = (5, 3) + offset = (0, 0) + mask = pygame.mask.Mask(size) + invalid_mask = pygame.Surface(size) + + with self.assertRaises(TypeError): + overlap_pos = mask.overlap(invalid_mask, offset) + + def test_overlap__invalid_offset_arg(self): + """Ensure overlap handles invalid offset arguments correctly.""" + size = (2, 7) + offset = "(0, 0)" + mask1 = pygame.mask.Mask(size) + mask2 = pygame.mask.Mask(size) + + with self.assertRaises(TypeError): + overlap_pos = mask1.overlap(mask2, offset) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_overlap_area(self): + """Ensure the overlap_area is correctly calculated. + + Testing the different combinations of full/empty masks: + (mask1-filled) 1 overlap_area 1 (mask2-filled) + (mask1-empty) 0 overlap_area 1 (mask2-filled) + (mask1-filled) 1 overlap_area 0 (mask2-empty) + (mask1-empty) 0 overlap_area 0 (mask2-empty) + """ + expected_size = width, height = (4, 4) + offset = (0, 0) + expected_default = 0 + expected_counts = {(True, True): width * height} + + for fill2 in (True, False): + mask2 = pygame.mask.Mask(expected_size, fill=fill2) + mask2_count = mask2.count() + + for fill1 in (True, False): + key = (fill1, fill2) + msg = f"key={key}" + mask1 = pygame.mask.Mask(expected_size, fill=fill1) + mask1_count = mask1.count() + expected_count = expected_counts.get(key, expected_default) + + overlap_count = mask1.overlap_area(mask2, offset) + + self.assertEqual(overlap_count, expected_count, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), expected_size, msg) + self.assertEqual(mask2.get_size(), expected_size, msg) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_overlap_area__offset(self): + """Ensure an offset overlap_area is correctly calculated.""" + mask1 = pygame.mask.Mask((65, 3), fill=True) + mask2 = pygame.mask.Mask((66, 4), fill=True) + mask1_count = mask1.count() + mask2_count = mask2.count() + mask1_size = mask1.get_size() + mask2_size = mask2.get_size() + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + for offset in self.ORIGIN_OFFSETS: + msg = f"offset={offset}" + rect2.topleft = offset + overlap_rect = rect1.clip(rect2) + expected_count = overlap_rect.w * overlap_rect.h + + overlap_count = mask1.overlap_area(other=mask2, offset=offset) + + self.assertEqual(overlap_count, expected_count, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), mask1_size, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + + def test_overlap_area__offset_boundary(self): + """Ensures overlap_area handles offsets and boundaries correctly.""" + mask1 = pygame.mask.Mask((11, 3), fill=True) + mask2 = pygame.mask.Mask((5, 7), fill=True) + mask1_count = mask1.count() + mask2_count = mask2.count() + mask1_size = mask1.get_size() + mask2_size = mask2.get_size() + expected_count = 0 + + # Check the 4 boundaries. + offsets = ( + (mask1_size[0], 0), # off right + (0, mask1_size[1]), # off bottom + (-mask2_size[0], 0), # off left + (0, -mask2_size[1]), + ) # off top + + for offset in offsets: + msg = f"offset={offset}" + + overlap_count = mask1.overlap_area(mask2, Vector2(offset)) + + self.assertEqual(overlap_count, expected_count, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), mask1_size, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_overlap_area__bit_boundaries(self): + """Ensures overlap_area handles masks of different sizes correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for height in range(2, 4): + for width in range(2, 66): + mask_size = (width, height) + mask_count = width * height + mask1 = pygame.mask.Mask(mask_size, fill=True) + mask2 = pygame.mask.Mask(mask_size, fill=True) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + # Testing masks offset from each other. + for offset in self.ORIGIN_OFFSETS: + msg = f"size={mask_size}, offset={offset}" + rect2.topleft = offset + overlap_rect = rect1.clip(rect2) + expected_overlap_count = overlap_rect.w * overlap_rect.h + + overlap_count = mask1.overlap_area(mask2, offset) + + self.assertEqual(overlap_count, expected_overlap_count, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask_count, msg) + self.assertEqual(mask2.count(), mask_count, msg) + self.assertEqual(mask1.get_size(), mask_size, msg) + self.assertEqual(mask2.get_size(), mask_size, msg) + + def test_overlap_area__invalid_mask_arg(self): + """Ensure overlap_area handles invalid mask arguments correctly.""" + size = (3, 5) + offset = (0, 0) + mask = pygame.mask.Mask(size) + invalid_mask = pygame.Surface(size) + + with self.assertRaises(TypeError): + overlap_count = mask.overlap_area(invalid_mask, offset) + + def test_overlap_area__invalid_offset_arg(self): + """Ensure overlap_area handles invalid offset arguments correctly.""" + size = (7, 2) + offset = "(0, 0)" + mask1 = pygame.mask.Mask(size) + mask2 = pygame.mask.Mask(size) + + with self.assertRaises(TypeError): + overlap_count = mask1.overlap_area(mask2, offset) + + def test_overlap_mask(self): + """Ensure overlap_mask's mask has correct bits set. + + Testing the different combinations of full/empty masks: + (mask1-filled) 1 overlap_mask 1 (mask2-filled) + (mask1-empty) 0 overlap_mask 1 (mask2-filled) + (mask1-filled) 1 overlap_mask 0 (mask2-empty) + (mask1-empty) 0 overlap_mask 0 (mask2-empty) + """ + expected_size = (4, 4) + offset = (0, 0) + expected_default = pygame.mask.Mask(expected_size) + expected_masks = {(True, True): pygame.mask.Mask(expected_size, fill=True)} + + for fill2 in (True, False): + mask2 = pygame.mask.Mask(expected_size, fill=fill2) + mask2_count = mask2.count() + + for fill1 in (True, False): + key = (fill1, fill2) + msg = f"key={key}" + mask1 = pygame.mask.Mask(expected_size, fill=fill1) + mask1_count = mask1.count() + expected_mask = expected_masks.get(key, expected_default) + + overlap_mask = mask1.overlap_mask(other=mask2, offset=offset) + + self.assertIsInstance(overlap_mask, pygame.mask.Mask, msg) + assertMaskEqual(self, overlap_mask, expected_mask, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), expected_size, msg) + self.assertEqual(mask2.get_size(), expected_size, msg) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_overlap_mask__bits_set(self): + """Ensure overlap_mask's mask has correct bits set.""" + mask1 = pygame.mask.Mask((50, 50), fill=True) + mask2 = pygame.mask.Mask((300, 10), fill=True) + mask1_count = mask1.count() + mask2_count = mask2.count() + mask1_size = mask1.get_size() + mask2_size = mask2.get_size() + + mask3 = mask1.overlap_mask(mask2, (-1, 0)) + + for i in range(50): + for j in range(10): + self.assertEqual(mask3.get_at((i, j)), 1, f"({i}, {j})") + + for i in range(50): + for j in range(11, 50): + self.assertEqual(mask3.get_at((i, j)), 0, f"({i}, {j})") + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count) + self.assertEqual(mask2.count(), mask2_count) + self.assertEqual(mask1.get_size(), mask1_size) + self.assertEqual(mask2.get_size(), mask2_size) + + def test_overlap_mask__offset(self): + """Ensure an offset overlap_mask's mask is correctly calculated.""" + mask1 = pygame.mask.Mask((65, 3), fill=True) + mask2 = pygame.mask.Mask((66, 4), fill=True) + mask1_count = mask1.count() + mask2_count = mask2.count() + mask1_size = mask1.get_size() + mask2_size = mask2.get_size() + expected_mask = pygame.Mask(mask1_size) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + for offset in self.ORIGIN_OFFSETS: + msg = f"offset={offset}" + rect2.topleft = offset + overlap_rect = rect1.clip(rect2) + expected_mask.clear() + expected_mask.draw( + pygame.Mask(overlap_rect.size, fill=True), overlap_rect.topleft + ) + + overlap_mask = mask1.overlap_mask(mask2, offset) + + self.assertIsInstance(overlap_mask, pygame.mask.Mask, msg) + assertMaskEqual(self, overlap_mask, expected_mask, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), mask1_size, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_overlap_mask__specific_offsets(self): + """Ensure an offset overlap_mask's mask is correctly calculated. + + Testing the specific case of: + -both masks are wider than 32 bits + -a positive offset is used + -the mask calling overlap_mask() is wider than the mask passed in + """ + mask1 = pygame.mask.Mask((65, 5), fill=True) + mask2 = pygame.mask.Mask((33, 3), fill=True) + expected_mask = pygame.Mask(mask1.get_size()) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + # This rect's corners are used to move rect2 around the inside of + # rect1. + corner_rect = rect1.inflate(-2, -2) + + for corner in ("topleft", "topright", "bottomright", "bottomleft"): + setattr(rect2, corner, getattr(corner_rect, corner)) + offset = rect2.topleft + msg = f"offset={offset}" + overlap_rect = rect1.clip(rect2) + expected_mask.clear() + expected_mask.draw( + pygame.Mask(overlap_rect.size, fill=True), overlap_rect.topleft + ) + + overlap_mask = mask1.overlap_mask(mask2, offset) + + self.assertIsInstance(overlap_mask, pygame.mask.Mask, msg) + assertMaskEqual(self, overlap_mask, expected_mask, msg) + + def test_overlap_mask__offset_boundary(self): + """Ensures overlap_mask handles offsets and boundaries correctly.""" + mask1 = pygame.mask.Mask((9, 3), fill=True) + mask2 = pygame.mask.Mask((11, 5), fill=True) + mask1_count = mask1.count() + mask2_count = mask2.count() + mask1_size = mask1.get_size() + mask2_size = mask2.get_size() + expected_count = 0 + expected_size = mask1_size + + # Check the 4 boundaries. + offsets = ( + (mask1_size[0], 0), # off right + (0, mask1_size[1]), # off bottom + (-mask2_size[0], 0), # off left + (0, -mask2_size[1]), + ) # off top + + for offset in offsets: + msg = f"offset={offset}" + + overlap_mask = mask1.overlap_mask(mask2, offset) + + self.assertIsInstance(overlap_mask, pygame.mask.Mask, msg) + self.assertEqual(overlap_mask.count(), expected_count, msg) + self.assertEqual(overlap_mask.get_size(), expected_size, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), mask1_size, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_overlap_mask__bit_boundaries(self): + """Ensures overlap_mask handles masks of different sizes correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for height in range(2, 4): + for width in range(2, 66): + mask_size = (width, height) + mask_count = width * height + mask1 = pygame.mask.Mask(mask_size, fill=True) + mask2 = pygame.mask.Mask(mask_size, fill=True) + expected_mask = pygame.Mask(mask_size) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + # Testing masks offset from each other. + for offset in self.ORIGIN_OFFSETS: + msg = f"size={mask_size}, offset={offset}" + rect2.topleft = offset + overlap_rect = rect1.clip(rect2) + expected_mask.clear() + expected_mask.draw( + pygame.Mask(overlap_rect.size, fill=True), overlap_rect.topleft + ) + + overlap_mask = mask1.overlap_mask(mask2, offset) + + self.assertIsInstance(overlap_mask, pygame.mask.Mask, msg) + assertMaskEqual(self, overlap_mask, expected_mask, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask_count, msg) + self.assertEqual(mask2.count(), mask_count, msg) + self.assertEqual(mask1.get_size(), mask_size, msg) + self.assertEqual(mask2.get_size(), mask_size, msg) + + def test_overlap_mask__invalid_mask_arg(self): + """Ensure overlap_mask handles invalid mask arguments correctly.""" + size = (3, 2) + offset = (0, 0) + mask = pygame.mask.Mask(size) + invalid_mask = pygame.Surface(size) + + with self.assertRaises(TypeError): + overlap_mask = mask.overlap_mask(invalid_mask, offset) + + def test_overlap_mask__invalid_offset_arg(self): + """Ensure overlap_mask handles invalid offset arguments correctly.""" + size = (5, 2) + offset = "(0, 0)" + mask1 = pygame.mask.Mask(size) + mask2 = pygame.mask.Mask(size) + + with self.assertRaises(TypeError): + overlap_mask = mask1.overlap_mask(mask2, offset) + + def test_mask_access(self): + """do the set_at, and get_at parts work correctly?""" + m = pygame.Mask((10, 10)) + m.set_at((0, 0), 1) + self.assertEqual(m.get_at((0, 0)), 1) + m.set_at((9, 0), 1) + self.assertEqual(m.get_at((9, 0)), 1) + + # s = pygame.Surface((10,10)) + # s.set_at((1,0), (0, 0, 1, 255)) + # self.assertEqual(s.get_at((1,0)), (0, 0, 1, 255)) + # s.set_at((-1,0), (0, 0, 1, 255)) + + # out of bounds, should get IndexError + self.assertRaises(IndexError, lambda: m.get_at((-1, 0))) + self.assertRaises(IndexError, lambda: m.set_at((-1, 0), 1)) + self.assertRaises(IndexError, lambda: m.set_at((10, 0), 1)) + self.assertRaises(IndexError, lambda: m.set_at((0, 10), 1)) + + def test_fill(self): + """Ensure a mask can be filled.""" + width, height = 11, 23 + expected_count = width * height + expected_size = (width, height) + mask = pygame.mask.Mask(expected_size) + + mask.fill() + + self.assertEqual(mask.count(), expected_count) + self.assertEqual(mask.get_size(), expected_size) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_fill__bit_boundaries(self): + """Ensures masks of different sizes are filled correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for height in range(1, 4): + for width in range(1, 66): + mask = pygame.mask.Mask((width, height)) + expected_count = width * height + + mask.fill() + + self.assertEqual( + mask.count(), expected_count, f"size=({width}, {height})" + ) + + def test_clear(self): + """Ensure a mask can be cleared.""" + expected_count = 0 + expected_size = (13, 27) + mask = pygame.mask.Mask(expected_size, fill=True) + + mask.clear() + + self.assertEqual(mask.count(), expected_count) + self.assertEqual(mask.get_size(), expected_size) + + def test_clear__bit_boundaries(self): + """Ensures masks of different sizes are cleared correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + expected_count = 0 + + for height in range(1, 4): + for width in range(1, 66): + mask = pygame.mask.Mask((width, height), fill=True) + + mask.clear() + + self.assertEqual( + mask.count(), expected_count, f"size=({width}, {height})" + ) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_invert(self): + """Ensure a mask can be inverted.""" + side = 73 + expected_size = (side, side) + mask1 = pygame.mask.Mask(expected_size) + mask2 = pygame.mask.Mask(expected_size, fill=True) + expected_count1 = side * side + expected_count2 = 0 + + for i in range(side): + expected_count1 -= 1 + expected_count2 += 1 + pos = (i, i) + mask1.set_at(pos) + mask2.set_at(pos, 0) + + mask1.invert() + mask2.invert() + + self.assertEqual(mask1.count(), expected_count1) + self.assertEqual(mask2.count(), expected_count2) + self.assertEqual(mask1.get_size(), expected_size) + self.assertEqual(mask2.get_size(), expected_size) + + for i in range(side): + pos = (i, i) + msg = f"pos={pos}" + + self.assertEqual(mask1.get_at(pos), 0, msg) + self.assertEqual(mask2.get_at(pos), 1, msg) + + def test_invert__full(self): + """Ensure a full mask can be inverted.""" + expected_count = 0 + expected_size = (43, 97) + mask = pygame.mask.Mask(expected_size, fill=True) + + mask.invert() + + self.assertEqual(mask.count(), expected_count) + self.assertEqual(mask.get_size(), expected_size) + + def test_invert__empty(self): + """Ensure an empty mask can be inverted.""" + width, height = 43, 97 + expected_size = (width, height) + expected_count = width * height + mask = pygame.mask.Mask(expected_size) + + mask.invert() + + self.assertEqual(mask.count(), expected_count) + self.assertEqual(mask.get_size(), expected_size) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_invert__bit_boundaries(self): + """Ensures masks of different sizes are inverted correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for fill in (True, False): + for height in range(1, 4): + for width in range(1, 66): + mask = pygame.mask.Mask((width, height), fill=fill) + expected_count = 0 if fill else width * height + + mask.invert() + + self.assertEqual( + mask.count(), + expected_count, + f"fill={fill}, size=({width}, {height})", + ) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_scale(self): + """Ensure a mask can be scaled.""" + width, height = 43, 61 + original_size = (width, height) + + for fill in (True, False): + original_mask = pygame.mask.Mask(original_size, fill=fill) + original_count = width * height if fill else 0 + + # Test a range of sizes. Also tests scaling to 'same' + # size when new_w, new_h = width, height + for new_w in range(width - 10, width + 10): + for new_h in range(height - 10, height + 10): + expected_size = (new_w, new_h) + expected_count = new_w * new_h if fill else 0 + msg = f"size={expected_size}" + + mask = original_mask.scale(scale=expected_size) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.count(), expected_count, msg) + self.assertEqual(mask.get_size(), expected_size) + + # Ensure the original mask is unchanged. + self.assertEqual(original_mask.count(), original_count, msg) + self.assertEqual(original_mask.get_size(), original_size, msg) + + def test_scale__negative_size(self): + """Ensure scale handles negative sizes correctly.""" + mask = pygame.Mask((100, 100)) + + with self.assertRaises(ValueError): + mask.scale((-1, -1)) + + with self.assertRaises(ValueError): + mask.scale(Vector2(-1, 10)) + + with self.assertRaises(ValueError): + mask.scale((10, -1)) + + def test_draw(self): + """Ensure a mask can be drawn onto another mask. + + Testing the different combinations of full/empty masks: + (mask1-filled) 1 draw 1 (mask2-filled) + (mask1-empty) 0 draw 1 (mask2-filled) + (mask1-filled) 1 draw 0 (mask2-empty) + (mask1-empty) 0 draw 0 (mask2-empty) + """ + expected_size = (4, 4) + offset = (0, 0) + expected_default = pygame.mask.Mask(expected_size, fill=True) + expected_masks = {(False, False): pygame.mask.Mask(expected_size)} + + for fill2 in (True, False): + mask2 = pygame.mask.Mask(expected_size, fill=fill2) + mask2_count = mask2.count() + + for fill1 in (True, False): + key = (fill1, fill2) + msg = f"key={key}" + mask1 = pygame.mask.Mask(expected_size, fill=fill1) + expected_mask = expected_masks.get(key, expected_default) + + mask1.draw(mask2, offset) + + assertMaskEqual(self, mask1, expected_mask, msg) + + # Ensure mask2 unchanged. + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask2.get_size(), expected_size, msg) + + def test_draw__offset(self): + """Ensure an offset mask can be drawn onto another mask.""" + mask1 = pygame.mask.Mask((65, 3)) + mask2 = pygame.mask.Mask((66, 4), fill=True) + mask2_count = mask2.count() + mask2_size = mask2.get_size() + expected_mask = pygame.Mask(mask1.get_size()) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + for offset in self.ORIGIN_OFFSETS: + msg = f"offset={offset}" + rect2.topleft = offset + overlap_rect = rect1.clip(rect2) + expected_mask.clear() + + # Normally draw() could be used to set these bits, but the draw() + # method is being tested here, so a loop is used instead. + for x in range(overlap_rect.left, overlap_rect.right): + for y in range(overlap_rect.top, overlap_rect.bottom): + expected_mask.set_at((x, y)) + mask1.clear() # Ensure it's empty for testing each offset. + + mask1.draw(other=mask2, offset=offset) + + assertMaskEqual(self, mask1, expected_mask, msg) + + # Ensure mask2 unchanged. + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + + def test_draw__specific_offsets(self): + """Ensure an offset mask can be drawn onto another mask. + + Testing the specific case of: + -both masks are wider than 32 bits + -a positive offset is used + -the mask calling draw() is wider than the mask passed in + """ + mask1 = pygame.mask.Mask((65, 5)) + mask2 = pygame.mask.Mask((33, 3), fill=True) + expected_mask = pygame.Mask(mask1.get_size()) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + # This rect's corners are used to move rect2 around the inside of + # rect1. + corner_rect = rect1.inflate(-2, -2) + + for corner in ("topleft", "topright", "bottomright", "bottomleft"): + setattr(rect2, corner, getattr(corner_rect, corner)) + offset = rect2.topleft + msg = f"offset={offset}" + overlap_rect = rect1.clip(rect2) + expected_mask.clear() + + # Normally draw() could be used to set these bits, but the draw() + # method is being tested here, so a loop is used instead. + for x in range(overlap_rect.left, overlap_rect.right): + for y in range(overlap_rect.top, overlap_rect.bottom): + expected_mask.set_at((x, y)) + mask1.clear() # Ensure it's empty for testing each offset. + + mask1.draw(mask2, offset) + + assertMaskEqual(self, mask1, expected_mask, msg) + + def test_draw__offset_boundary(self): + """Ensures draw handles offsets and boundaries correctly.""" + mask1 = pygame.mask.Mask((13, 5)) + mask2 = pygame.mask.Mask((7, 3), fill=True) + mask1_count = mask1.count() + mask2_count = mask2.count() + mask1_size = mask1.get_size() + mask2_size = mask2.get_size() + + # Check the 4 boundaries. + offsets = ( + (mask1_size[0], 0), # off right + (0, mask1_size[1]), # off bottom + (-mask2_size[0], 0), # off left + (0, -mask2_size[1]), + ) # off top + + for offset in offsets: + msg = f"offset={offset}" + + mask1.draw(mask2, offset) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), mask1_size, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_draw__bit_boundaries(self): + """Ensures draw handles masks of different sizes correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for height in range(2, 4): + for width in range(2, 66): + mask_size = (width, height) + mask_count = width * height + mask1 = pygame.mask.Mask(mask_size) + mask2 = pygame.mask.Mask(mask_size, fill=True) + expected_mask = pygame.Mask(mask_size) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + # Testing masks offset from each other. + for offset in self.ORIGIN_OFFSETS: + msg = f"size={mask_size}, offset={offset}" + rect2.topleft = offset + overlap_rect = rect1.clip(rect2) + expected_mask.clear() + + # Normally draw() could be used to set these bits, but the + # draw() method is being tested here, so a loop is used + # instead. + for x in range(overlap_rect.left, overlap_rect.right): + for y in range(overlap_rect.top, overlap_rect.bottom): + expected_mask.set_at((x, y)) + mask1.clear() # Ensure it's empty for each test. + + mask1.draw(mask2, offset) + + assertMaskEqual(self, mask1, expected_mask, msg) + + # Ensure mask2 unchanged. + self.assertEqual(mask2.count(), mask_count, msg) + self.assertEqual(mask2.get_size(), mask_size, msg) + + def test_draw__invalid_mask_arg(self): + """Ensure draw handles invalid mask arguments correctly.""" + size = (7, 3) + offset = (0, 0) + mask = pygame.mask.Mask(size) + invalid_mask = pygame.Surface(size) + + with self.assertRaises(TypeError): + mask.draw(invalid_mask, offset) + + def test_draw__invalid_offset_arg(self): + """Ensure draw handles invalid offset arguments correctly.""" + size = (5, 7) + offset = "(0, 0)" + mask1 = pygame.mask.Mask(size) + mask2 = pygame.mask.Mask(size) + + with self.assertRaises(TypeError): + mask1.draw(mask2, offset) + + def test_erase(self): + """Ensure a mask can erase another mask. + + Testing the different combinations of full/empty masks: + (mask1-filled) 1 erase 1 (mask2-filled) + (mask1-empty) 0 erase 1 (mask2-filled) + (mask1-filled) 1 erase 0 (mask2-empty) + (mask1-empty) 0 erase 0 (mask2-empty) + """ + expected_size = (4, 4) + offset = (0, 0) + expected_default = pygame.mask.Mask(expected_size) + expected_masks = {(True, False): pygame.mask.Mask(expected_size, fill=True)} + + for fill2 in (True, False): + mask2 = pygame.mask.Mask(expected_size, fill=fill2) + mask2_count = mask2.count() + + for fill1 in (True, False): + key = (fill1, fill2) + msg = f"key={key}" + mask1 = pygame.mask.Mask(expected_size, fill=fill1) + expected_mask = expected_masks.get(key, expected_default) + + mask1.erase(mask2, offset) + + assertMaskEqual(self, mask1, expected_mask, msg) + + # Ensure mask2 unchanged. + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask2.get_size(), expected_size, msg) + + def test_erase__offset(self): + """Ensure an offset mask can erase another mask.""" + mask1 = pygame.mask.Mask((65, 3)) + mask2 = pygame.mask.Mask((66, 4), fill=True) + mask2_count = mask2.count() + mask2_size = mask2.get_size() + expected_mask = pygame.Mask(mask1.get_size()) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + for offset in self.ORIGIN_OFFSETS: + msg = f"offset={offset}" + rect2.topleft = offset + overlap_rect = rect1.clip(rect2) + expected_mask.fill() + + # Normally erase() could be used to clear these bits, but the + # erase() method is being tested here, so a loop is used instead. + for x in range(overlap_rect.left, overlap_rect.right): + for y in range(overlap_rect.top, overlap_rect.bottom): + expected_mask.set_at((x, y), 0) + mask1.fill() # Ensure it's filled for testing each offset. + + mask1.erase(other=mask2, offset=offset) + + assertMaskEqual(self, mask1, expected_mask, msg) + + # Ensure mask2 unchanged. + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + + def test_erase__specific_offsets(self): + """Ensure an offset mask can erase another mask. + + Testing the specific case of: + -both masks are wider than 32 bits + -a positive offset is used + -the mask calling erase() is wider than the mask passed in + """ + mask1 = pygame.mask.Mask((65, 5)) + mask2 = pygame.mask.Mask((33, 3), fill=True) + expected_mask = pygame.Mask(mask1.get_size()) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + # This rect's corners are used to move rect2 around the inside of + # rect1. + corner_rect = rect1.inflate(-2, -2) + + for corner in ("topleft", "topright", "bottomright", "bottomleft"): + setattr(rect2, corner, getattr(corner_rect, corner)) + offset = rect2.topleft + msg = f"offset={offset}" + overlap_rect = rect1.clip(rect2) + expected_mask.fill() + + # Normally erase() could be used to clear these bits, but the + # erase() method is being tested here, so a loop is used instead. + for x in range(overlap_rect.left, overlap_rect.right): + for y in range(overlap_rect.top, overlap_rect.bottom): + expected_mask.set_at((x, y), 0) + mask1.fill() # Ensure it's filled for testing each offset. + + mask1.erase(mask2, Vector2(offset)) + + assertMaskEqual(self, mask1, expected_mask, msg) + + def test_erase__offset_boundary(self): + """Ensures erase handles offsets and boundaries correctly.""" + mask1 = pygame.mask.Mask((7, 11), fill=True) + mask2 = pygame.mask.Mask((3, 13), fill=True) + mask1_count = mask1.count() + mask2_count = mask2.count() + mask1_size = mask1.get_size() + mask2_size = mask2.get_size() + + # Check the 4 boundaries. + offsets = ( + (mask1_size[0], 0), # off right + (0, mask1_size[1]), # off bottom + (-mask2_size[0], 0), # off left + (0, -mask2_size[1]), + ) # off top + + for offset in offsets: + msg = f"offset={offset}" + + mask1.erase(mask2, offset) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask1_count, msg) + self.assertEqual(mask2.count(), mask2_count, msg) + self.assertEqual(mask1.get_size(), mask1_size, msg) + self.assertEqual(mask2.get_size(), mask2_size, msg) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_erase__bit_boundaries(self): + """Ensures erase handles masks of different sizes correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for height in range(2, 4): + for width in range(2, 66): + mask_size = (width, height) + mask_count = width * height + mask1 = pygame.mask.Mask(mask_size) + mask2 = pygame.mask.Mask(mask_size, fill=True) + expected_mask = pygame.Mask(mask_size) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + # Testing masks offset from each other. + for offset in self.ORIGIN_OFFSETS: + msg = f"size={mask_size}, offset={offset}" + rect2.topleft = offset + overlap_rect = rect1.clip(rect2) + expected_mask.fill() + + # Normally erase() could be used to clear these bits, but + # the erase() method is being tested here, so a loop is + # used instead. + for x in range(overlap_rect.left, overlap_rect.right): + for y in range(overlap_rect.top, overlap_rect.bottom): + expected_mask.set_at((x, y), 0) + mask1.fill() # Ensure it's filled for each test. + + mask1.erase(mask2, offset) + + assertMaskEqual(self, mask1, expected_mask, msg) + + # Ensure mask2 unchanged. + self.assertEqual(mask2.count(), mask_count, msg) + self.assertEqual(mask2.get_size(), mask_size, msg) + + def test_erase__invalid_mask_arg(self): + """Ensure erase handles invalid mask arguments correctly.""" + size = (3, 7) + offset = (0, 0) + mask = pygame.mask.Mask(size) + invalid_mask = pygame.Surface(size) + + with self.assertRaises(TypeError): + mask.erase(invalid_mask, offset) + + def test_erase__invalid_offset_arg(self): + """Ensure erase handles invalid offset arguments correctly.""" + size = (7, 5) + offset = "(0, 0)" + mask1 = pygame.mask.Mask(size) + mask2 = pygame.mask.Mask(size) + + with self.assertRaises(TypeError): + mask1.erase(mask2, offset) + + def test_count(self): + """Ensure a mask's set bits are correctly counted.""" + side = 67 + expected_size = (side, side) + expected_count = 0 + mask = pygame.mask.Mask(expected_size) + + for i in range(side): + expected_count += 1 + mask.set_at((i, i)) + + count = mask.count() + + self.assertEqual(count, expected_count) + self.assertEqual(mask.get_size(), expected_size) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_count__bit_boundaries(self): + """Ensures the set bits of different sized masks are counted correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for fill in (True, False): + for height in range(1, 4): + for width in range(1, 66): + mask = pygame.mask.Mask((width, height), fill=fill) + expected_count = width * height if fill else 0 + + # Test toggling each bit. + for pos in ((x, y) for y in range(height) for x in range(width)): + if fill: + mask.set_at(pos, 0) + expected_count -= 1 + else: + mask.set_at(pos, 1) + expected_count += 1 + + count = mask.count() + + self.assertEqual( + count, + expected_count, + f"fill={fill}, size=({width}, {height}), pos={pos}", + ) + + def test_count__full_mask(self): + """Ensure a full mask's set bits are correctly counted.""" + width, height = 17, 97 + expected_size = (width, height) + expected_count = width * height + mask = pygame.mask.Mask(expected_size, fill=True) + + count = mask.count() + + self.assertEqual(count, expected_count) + self.assertEqual(mask.get_size(), expected_size) + + def test_count__empty_mask(self): + """Ensure an empty mask's set bits are correctly counted.""" + expected_count = 0 + expected_size = (13, 27) + mask = pygame.mask.Mask(expected_size) + + count = mask.count() + + self.assertEqual(count, expected_count) + self.assertEqual(mask.get_size(), expected_size) + + def test_centroid(self): + """Ensure a filled mask's centroid is correctly calculated.""" + mask = pygame.mask.Mask((5, 7), fill=True) + expected_centroid = mask.get_rect().center + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_centroid__empty_mask(self): + """Ensure an empty mask's centroid is correctly calculated.""" + expected_centroid = (0, 0) + expected_size = (101, 103) + mask = pygame.mask.Mask(expected_size) + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + self.assertEqual(mask.get_size(), expected_size) + + def test_centroid__single_row(self): + """Ensure a mask's centroid is correctly calculated + when setting points along a single row.""" + width, height = (5, 7) + mask = pygame.mask.Mask((width, height)) + + for y in range(height): + mask.clear() # Clear for each row. + + for x in range(width): + mask.set_at((x, y)) + expected_centroid = (x // 2, y) + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_centroid__two_rows(self): + """Ensure a mask's centroid is correctly calculated + when setting points along two rows.""" + width, height = (5, 7) + mask = pygame.mask.Mask((width, height)) + + # The first row is tested with each of the other rows. + for y in range(1, height): + mask.clear() # Clear for each set of rows. + + for x in range(width): + mask.set_at((x, 0)) + mask.set_at((x, y)) + expected_centroid = (x // 2, y // 2) + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_centroid__single_column(self): + """Ensure a mask's centroid is correctly calculated + when setting points along a single column.""" + width, height = (5, 7) + mask = pygame.mask.Mask((width, height)) + + for x in range(width): + mask.clear() # Clear for each column. + + for y in range(height): + mask.set_at((x, y)) + expected_centroid = (x, y // 2) + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_centroid__two_columns(self): + """Ensure a mask's centroid is correctly calculated + when setting points along two columns.""" + width, height = (5, 7) + mask = pygame.mask.Mask((width, height)) + + # The first column is tested with each of the other columns. + for x in range(1, width): + mask.clear() # Clear for each set of columns. + + for y in range(height): + mask.set_at((0, y)) + mask.set_at((x, y)) + expected_centroid = (x // 2, y // 2) + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_centroid__all_corners(self): + """Ensure a mask's centroid is correctly calculated + when its corners are set.""" + mask = pygame.mask.Mask((5, 7)) + expected_centroid = mask.get_rect().center + + for corner in corners(mask): + mask.set_at(corner) + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_centroid__two_corners(self): + """Ensure a mask's centroid is correctly calculated + when only two corners are set.""" + mask = pygame.mask.Mask((5, 7)) + mask_rect = mask.get_rect() + mask_corners = corners(mask) + + for i, corner1 in enumerate(mask_corners): + for corner2 in mask_corners[i + 1 :]: + mask.clear() # Clear for each pair of corners. + mask.set_at(corner1) + mask.set_at(corner2) + + if corner1[0] == corner2[0]: + expected_centroid = (corner1[0], abs(corner1[1] - corner2[1]) // 2) + elif corner1[1] == corner2[1]: + expected_centroid = (abs(corner1[0] - corner2[0]) // 2, corner1[1]) + else: + expected_centroid = mask_rect.center + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_angle(self): + """Ensure a mask's orientation angle is correctly calculated.""" + expected_angle = -45.0 + expected_size = (100, 100) + surface = pygame.Surface(expected_size) + mask = pygame.mask.from_surface(surface) + + angle = mask.angle() # Returns the orientation of the pixels + + self.assertIsInstance(angle, float) + self.assertEqual(angle, expected_angle) + + def test_angle__empty_mask(self): + """Ensure an empty mask's angle is correctly calculated.""" + expected_angle = 0.0 + expected_size = (107, 43) + mask = pygame.mask.Mask(expected_size) + + angle = mask.angle() + + self.assertIsInstance(angle, float) + self.assertAlmostEqual(angle, expected_angle) + self.assertEqual(mask.get_size(), expected_size) + + def test_drawing(self): + """Test fill, clear, invert, draw, erase""" + m = pygame.Mask((100, 100)) + self.assertEqual(m.count(), 0) + + m.fill() + self.assertEqual(m.count(), 10000) + + m2 = pygame.Mask((10, 10), fill=True) + m.erase(m2, (50, 50)) + self.assertEqual(m.count(), 9900) + + m.invert() + self.assertEqual(m.count(), 100) + + m.draw(m2, (0, 0)) + self.assertEqual(m.count(), 200) + + m.clear() + self.assertEqual(m.count(), 0) + + def test_outline(self): + """ """ + + m = pygame.Mask((20, 20)) + self.assertEqual(m.outline(), []) + + m.set_at((10, 10), 1) + self.assertEqual(m.outline(), [(10, 10)]) + + m.set_at((10, 12), 1) + self.assertEqual(m.outline(10), [(10, 10)]) + + m.set_at((11, 11), 1) + self.assertEqual( + m.outline(), [(10, 10), (11, 11), (10, 12), (11, 11), (10, 10)] + ) + self.assertEqual(m.outline(every=2), [(10, 10), (10, 12), (10, 10)]) + + # TODO: Test more corner case outlines. + + def test_convolve__size(self): + sizes = [(1, 1), (31, 31), (32, 32), (100, 100)] + for s1 in sizes: + m1 = pygame.Mask(s1) + for s2 in sizes: + m2 = pygame.Mask(s2) + o = m1.convolve(m2) + + self.assertIsInstance(o, pygame.mask.Mask) + + for i in (0, 1): + self.assertEqual( + o.get_size()[i], m1.get_size()[i] + m2.get_size()[i] - 1 + ) + + def test_convolve__point_identities(self): + """Convolving with a single point is the identity, while convolving a point with something flips it.""" + m = random_mask((100, 100)) + k = pygame.Mask((1, 1)) + k.set_at((0, 0)) + + convolve_mask = m.convolve(k) + + self.assertIsInstance(convolve_mask, pygame.mask.Mask) + assertMaskEqual(self, m, convolve_mask) + + convolve_mask = k.convolve(k.convolve(m)) + + self.assertIsInstance(convolve_mask, pygame.mask.Mask) + assertMaskEqual(self, m, convolve_mask) + + def test_convolve__with_output(self): + """checks that convolution modifies only the correct portion of the output""" + + m = random_mask((10, 10)) + k = pygame.Mask((2, 2)) + k.set_at((0, 0)) + + o = pygame.Mask((50, 50)) + test = pygame.Mask((50, 50)) + + m.convolve(k, o) + test.draw(m, (1, 1)) + + self.assertIsInstance(o, pygame.mask.Mask) + assertMaskEqual(self, o, test) + + o.clear() + test.clear() + + m.convolve(other=k, output=o, offset=Vector2(10, 10)) + test.draw(m, (11, 11)) + + self.assertIsInstance(o, pygame.mask.Mask) + assertMaskEqual(self, o, test) + + def test_convolve__out_of_range(self): + full = pygame.Mask((2, 2), fill=True) + # Tuple of points (out of range) and the expected count for each. + pts_data = (((0, 3), 0), ((0, 2), 3), ((-2, -2), 1), ((-3, -3), 0)) + + for pt, expected_count in pts_data: + convolve_mask = full.convolve(full, None, pt) + + self.assertIsInstance(convolve_mask, pygame.mask.Mask) + self.assertEqual(convolve_mask.count(), expected_count) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_convolve(self): + """Tests the definition of convolution""" + m1 = random_mask((100, 100)) + m2 = random_mask((100, 100)) + conv = m1.convolve(m2) + + self.assertIsInstance(conv, pygame.mask.Mask) + for i in range(conv.get_size()[0]): + for j in range(conv.get_size()[1]): + self.assertEqual( + conv.get_at((i, j)) == 0, m1.overlap(m2, (i - 99, j - 99)) is None + ) + + def _draw_component_pattern_box(self, mask, size, pos, inverse=False): + # Helper method to create/draw a 'box' pattern for testing. + # + # 111 + # 101 3x3 example pattern + # 111 + pattern = pygame.mask.Mask((size, size), fill=True) + pattern.set_at((size // 2, size // 2), 0) + + if inverse: + mask.erase(pattern, pos) + pattern.invert() + else: + mask.draw(pattern, pos) + + return pattern + + def _draw_component_pattern_x(self, mask, size, pos, inverse=False): + # Helper method to create/draw an 'X' pattern for testing. + # + # 101 + # 010 3x3 example pattern + # 101 + pattern = pygame.mask.Mask((size, size)) + + ymax = size - 1 + for y in range(size): + for x in range(size): + if x in [y, ymax - y]: + pattern.set_at((x, y)) + + if inverse: + mask.erase(pattern, pos) + pattern.invert() + else: + mask.draw(pattern, pos) + + return pattern + + def _draw_component_pattern_plus(self, mask, size, pos, inverse=False): + # Helper method to create/draw a '+' pattern for testing. + # + # 010 + # 111 3x3 example pattern + # 010 + pattern = pygame.mask.Mask((size, size)) + + xmid = ymid = size // 2 + for y in range(size): + for x in range(size): + if x == xmid or y == ymid: + pattern.set_at((x, y)) + + if inverse: + mask.erase(pattern, pos) + pattern.invert() + else: + mask.draw(pattern, pos) + + return pattern + + def test_connected_component(self): + """Ensure a mask's connected component is correctly calculated.""" + width, height = 41, 27 + expected_size = (width, height) + original_mask = pygame.mask.Mask(expected_size) + patterns = [] # Patterns and offsets. + + # Draw some connected patterns on the original mask. + offset = (0, 0) + pattern = self._draw_component_pattern_x(original_mask, 3, offset) + patterns.append((pattern, offset)) + + size = 4 + offset = (width - size, 0) + pattern = self._draw_component_pattern_plus(original_mask, size, offset) + patterns.append((pattern, offset)) + + # Make this one the largest connected component. + offset = (width // 2, height // 2) + pattern = self._draw_component_pattern_box(original_mask, 7, offset) + patterns.append((pattern, offset)) + + expected_pattern, expected_offset = patterns[-1] + expected_count = expected_pattern.count() + original_count = sum(p.count() for p, _ in patterns) + + mask = original_mask.connected_component() + + self.assertIsInstance(mask, pygame.mask.Mask) + self.assertEqual(mask.count(), expected_count) + self.assertEqual(mask.get_size(), expected_size) + self.assertEqual( + mask.overlap_area(expected_pattern, expected_offset), expected_count + ) + + # Ensure the original mask is unchanged. + self.assertEqual(original_mask.count(), original_count) + self.assertEqual(original_mask.get_size(), expected_size) + + for pattern, offset in patterns: + self.assertEqual( + original_mask.overlap_area(pattern, offset), pattern.count() + ) + + def test_connected_component__full_mask(self): + """Ensure a mask's connected component is correctly calculated + when the mask is full. + """ + expected_size = (23, 31) + original_mask = pygame.mask.Mask(expected_size, fill=True) + expected_count = original_mask.count() + + mask = original_mask.connected_component() + + self.assertIsInstance(mask, pygame.mask.Mask) + self.assertEqual(mask.count(), expected_count) + self.assertEqual(mask.get_size(), expected_size) + + # Ensure the original mask is unchanged. + self.assertEqual(original_mask.count(), expected_count) + self.assertEqual(original_mask.get_size(), expected_size) + + def test_connected_component__empty_mask(self): + """Ensure a mask's connected component is correctly calculated + when the mask is empty. + """ + expected_size = (37, 43) + original_mask = pygame.mask.Mask(expected_size) + original_count = original_mask.count() + expected_count = 0 + + mask = original_mask.connected_component() + + self.assertIsInstance(mask, pygame.mask.Mask) + self.assertEqual(mask.count(), expected_count) + self.assertEqual(mask.get_size(), expected_size) + + # Ensure the original mask is unchanged. + self.assertEqual(original_mask.count(), original_count) + self.assertEqual(original_mask.get_size(), expected_size) + + def test_connected_component__one_set_bit(self): + """Ensure a mask's connected component is correctly calculated + when the coordinate's bit is set with a connected component of 1 bit. + """ + width, height = 71, 67 + expected_size = (width, height) + original_mask = pygame.mask.Mask(expected_size, fill=True) + xset, yset = width // 2, height // 2 + set_pos = (xset, yset) + expected_offset = (xset - 1, yset - 1) + + # This isolates the bit at set_pos from all the other bits. + expected_pattern = self._draw_component_pattern_box( + original_mask, 3, expected_offset, inverse=True + ) + expected_count = 1 + original_count = original_mask.count() + + mask = original_mask.connected_component(set_pos) + + self.assertIsInstance(mask, pygame.mask.Mask) + self.assertEqual(mask.count(), expected_count) + self.assertEqual(mask.get_size(), expected_size) + self.assertEqual( + mask.overlap_area(expected_pattern, expected_offset), expected_count + ) + + # Ensure the original mask is unchanged. + self.assertEqual(original_mask.count(), original_count) + self.assertEqual(original_mask.get_size(), expected_size) + self.assertEqual( + original_mask.overlap_area(expected_pattern, expected_offset), + expected_count, + ) + + def test_connected_component__multi_set_bits(self): + """Ensure a mask's connected component is correctly calculated + when the coordinate's bit is set with a connected component of > 1 bit. + """ + expected_size = (113, 67) + original_mask = pygame.mask.Mask(expected_size) + p_width, p_height = 11, 13 + set_pos = xset, yset = 11, 21 + expected_offset = (xset - 1, yset - 1) + expected_pattern = pygame.mask.Mask((p_width, p_height), fill=True) + + # Make an unsymmetrical pattern. All the set bits need to be connected + # in the resulting pattern for this to work properly. + for y in range(3, p_height): + for x in range(1, p_width): + if x in [y, y - 3, p_width - 4]: + expected_pattern.set_at((x, y), 0) + + expected_count = expected_pattern.count() + original_mask.draw(expected_pattern, expected_offset) + + mask = original_mask.connected_component(set_pos) + + self.assertIsInstance(mask, pygame.mask.Mask) + self.assertEqual(mask.count(), expected_count) + self.assertEqual(mask.get_size(), expected_size) + self.assertEqual( + mask.overlap_area(expected_pattern, expected_offset), expected_count + ) + + # Ensure the original mask is unchanged. + self.assertEqual(original_mask.count(), expected_count) + self.assertEqual(original_mask.get_size(), expected_size) + self.assertEqual( + original_mask.overlap_area(expected_pattern, expected_offset), + expected_count, + ) + + def test_connected_component__unset_bit(self): + """Ensure a mask's connected component is correctly calculated + when the coordinate's bit is unset. + """ + width, height = 109, 101 + expected_size = (width, height) + original_mask = pygame.mask.Mask(expected_size, fill=True) + unset_pos = (width // 2, height // 2) + original_mask.set_at(unset_pos, 0) + original_count = original_mask.count() + expected_count = 0 + + mask = original_mask.connected_component(unset_pos) + + self.assertIsInstance(mask, pygame.mask.Mask) + self.assertEqual(mask.count(), expected_count) + self.assertEqual(mask.get_size(), expected_size) + + # Ensure the original mask is unchanged. + self.assertEqual(original_mask.count(), original_count) + self.assertEqual(original_mask.get_size(), expected_size) + self.assertEqual(original_mask.get_at(unset_pos), 0) + + def test_connected_component__out_of_bounds(self): + """Ensure connected_component() checks bounds.""" + width, height = 19, 11 + original_size = (width, height) + original_mask = pygame.mask.Mask(original_size, fill=True) + original_count = original_mask.count() + + for pos in ((0, -1), (-1, 0), (0, height + 1), (width + 1, 0)): + with self.assertRaises(IndexError): + mask = original_mask.connected_component(pos) + + # Ensure the original mask is unchanged. + self.assertEqual(original_mask.count(), original_count) + self.assertEqual(original_mask.get_size(), original_size) + + def test_connected_components(self): + """ """ + m = pygame.Mask((10, 10)) + + self.assertListEqual(m.connected_components(), []) + + comp = m.connected_component() + + self.assertEqual(m.count(), comp.count()) + + m.set_at((0, 0), 1) + m.set_at((1, 1), 1) + comp = m.connected_component() + comps = m.connected_components() + comps1 = m.connected_components(1) + comps2 = m.connected_components(2) + comps3 = m.connected_components(3) + + self.assertEqual(comp.count(), comps[0].count()) + self.assertEqual(comps1[0].count(), 2) + self.assertEqual(comps2[0].count(), 2) + self.assertListEqual(comps3, []) + + m.set_at((9, 9), 1) + comp = m.connected_component() + comp1 = m.connected_component((1, 1)) + comp2 = m.connected_component((2, 2)) + comps = m.connected_components() + comps1 = m.connected_components(1) + comps2 = m.connected_components(minimum=2) + comps3 = m.connected_components(3) + + self.assertEqual(comp.count(), 2) + self.assertEqual(comp1.count(), 2) + self.assertEqual(comp2.count(), 0) + self.assertEqual(len(comps), 2) + self.assertEqual(len(comps1), 2) + self.assertEqual(len(comps2), 1) + self.assertEqual(len(comps3), 0) + + for mask in comps: + self.assertIsInstance(mask, pygame.mask.Mask) + + def test_connected_components__negative_min_with_empty_mask(self): + """Ensures connected_components() properly handles negative min values + when the mask is empty. + + Negative and zero values for the min parameter (minimum number of bits + per connected component) equate to setting it to one. + """ + expected_comps = [] + mask_count = 0 + mask_size = (65, 13) + mask = pygame.mask.Mask(mask_size) + + connected_comps = mask.connected_components(-1) + + self.assertListEqual(connected_comps, expected_comps) + + # Ensure the original mask is unchanged. + self.assertEqual(mask.count(), mask_count) + self.assertEqual(mask.get_size(), mask_size) + + def test_connected_components__negative_min_with_full_mask(self): + """Ensures connected_components() properly handles negative min values + when the mask is full. + + Negative and zero values for the min parameter (minimum number of bits + per connected component) equate to setting it to one. + """ + mask_size = (64, 11) + mask = pygame.mask.Mask(mask_size, fill=True) + mask_count = mask.count() + expected_len = 1 + + connected_comps = mask.connected_components(-2) + + self.assertEqual(len(connected_comps), expected_len) + assertMaskEqual(self, connected_comps[0], mask) + + # Ensure the original mask is unchanged. + self.assertEqual(mask.count(), mask_count) + self.assertEqual(mask.get_size(), mask_size) + + def test_connected_components__negative_min_with_some_bits_set(self): + """Ensures connected_components() properly handles negative min values + when the mask has some bits set. + + Negative and zero values for the min parameter (minimum number of bits + per connected component) equate to setting it to one. + """ + mask_size = (64, 12) + mask = pygame.mask.Mask(mask_size) + expected_comps = {} + + # Set the corners and the center positions. A new expected component + # mask is created for each point. + for corner in corners(mask): + mask.set_at(corner) + + new_mask = pygame.mask.Mask(mask_size) + new_mask.set_at(corner) + expected_comps[corner] = new_mask + + center = (mask_size[0] // 2, mask_size[1] // 2) + mask.set_at(center) + + new_mask = pygame.mask.Mask(mask_size) + new_mask.set_at(center) + expected_comps[center] = new_mask + mask_count = mask.count() + + connected_comps = mask.connected_components(-3) + + self.assertEqual(len(connected_comps), len(expected_comps)) + + for comp in connected_comps: + # Since the masks in the connected component list can be in any + # order, loop the expected components to find its match. + found = False + + for pt in tuple(expected_comps.keys()): + if comp.get_at(pt): + found = True + assertMaskEqual(self, comp, expected_comps[pt]) + del expected_comps[pt] # Entry removed so it isn't reused. + break + + self.assertTrue(found, f"missing component for pt={pt}") + + # Ensure the original mask is unchanged. + self.assertEqual(mask.count(), mask_count) + self.assertEqual(mask.get_size(), mask_size) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_get_bounding_rects(self): + """Ensures get_bounding_rects works correctly.""" + # Create masks with different set point groups. Each group of + # connected set points will be contained in its own bounding rect. + # Diagonal points are considered connected. + mask_data = [] # [((size), ((rect1_pts), ...)), ...] + + # Mask 1: + # |0123456789 + # -+---------- + # 0|1100000000 + # 1|1000000000 + # 2|0000000000 + # 3|1001000000 + # 4|0000000000 + # 5|0000000000 + # 6|0000000000 + # 7|0000000000 + # 8|0000000000 + # 9|0000000000 + mask_data.append( + ( + (10, 10), # size + # Points to set for the 3 bounding rects. + (((0, 0), (1, 0), (0, 1)), ((0, 3),), ((3, 3),)), # rect1 # rect2 + ) + ) # rect3 + + # Mask 2: + # |0123 + # -+---- + # 0|1100 + # 1|1111 + mask_data.append( + ( + (4, 2), # size + # Points to set for the 1 bounding rect. + (((0, 0), (1, 0), (0, 1), (1, 1), (2, 1), (3, 1)),), + ) + ) + + # Mask 3: + # |01234 + # -+----- + # 0|00100 + # 1|01110 + # 2|00100 + mask_data.append( + ( + (5, 3), # size + # Points to set for the 1 bounding rect. + (((2, 0), (1, 1), (2, 1), (3, 1), (2, 2)),), + ) + ) + + # Mask 4: + # |01234 + # -+----- + # 0|00010 + # 1|00100 + # 2|01000 + mask_data.append( + ( + (5, 3), # size + # Points to set for the 1 bounding rect. + (((3, 0), (2, 1), (1, 2)),), + ) + ) + + # Mask 5: + # |01234 + # -+----- + # 0|00011 + # 1|11111 + mask_data.append( + ( + (5, 2), # size + # Points to set for the 1 bounding rect. + (((3, 0), (4, 0), (0, 1), (1, 1), (2, 1), (3, 1)),), + ) + ) + + # Mask 6: + # |01234 + # -+----- + # 0|10001 + # 1|00100 + # 2|10001 + mask_data.append( + ( + (5, 3), # size + # Points to set for the 5 bounding rects. + ( + ((0, 0),), # rect1 + ((4, 0),), # rect2 + ((2, 1),), # rect3 + ((0, 2),), # rect4 + ((4, 2),), + ), + ) + ) # rect5 + + for size, rect_point_tuples in mask_data: + rects = [] + mask = pygame.Mask(size) + + for rect_points in rect_point_tuples: + rects.append(create_bounding_rect(rect_points)) + for pt in rect_points: + mask.set_at(pt) + + expected_rects = sorted(rects, key=tuple) + + rects = mask.get_bounding_rects() + + self.assertListEqual( + sorted(mask.get_bounding_rects(), key=tuple), + expected_rects, + f"size={size}", + ) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_to_surface(self): + """Ensures empty and full masks can be drawn onto surfaces.""" + expected_ref_count = 3 + size = (33, 65) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + test_fills = ((pygame.Color("white"), True), (pygame.Color("black"), False)) + + for expected_color, fill in test_fills: + surface.fill(surface_color) + mask = pygame.mask.Mask(size, fill=fill) + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__create_surface(self): + """Ensures empty and full masks can be drawn onto a created surface.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + size = (33, 65) + test_fills = ((pygame.Color("white"), True), (pygame.Color("black"), False)) + + for expected_color, fill in test_fills: + mask = pygame.mask.Mask(size, fill=fill) + + for use_arg in (True, False): + if use_arg: + to_surface = mask.to_surface(None) + else: + to_surface = mask.to_surface() + + self.assertIsInstance(to_surface, pygame.Surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__surface_param(self): + """Ensures to_surface accepts a surface arg/kwarg.""" + expected_ref_count = 4 + expected_color = pygame.Color("white") + surface_color = pygame.Color("red") + size = (5, 3) + mask = pygame.mask.Mask(size, fill=True) + surface = pygame.Surface(size) + kwargs = {"surface": surface} + + for use_kwargs in (True, False): + surface.fill(surface_color) + + if use_kwargs: + to_surface = mask.to_surface(**kwargs) + else: + to_surface = mask.to_surface(kwargs["surface"]) + + self.assertIs(to_surface, surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__setsurface_param(self): + """Ensures to_surface accepts a setsurface arg/kwarg.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + expected_color = pygame.Color("red") + size = (5, 3) + mask = pygame.mask.Mask(size, fill=True) + setsurface = pygame.Surface(size, expected_flag, expected_depth) + setsurface.fill(expected_color) + kwargs = {"setsurface": setsurface} + + for use_kwargs in (True, False): + if use_kwargs: + to_surface = mask.to_surface(**kwargs) + else: + to_surface = mask.to_surface(None, kwargs["setsurface"]) + + self.assertIsInstance(to_surface, pygame.Surface) + + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__unsetsurface_param(self): + """Ensures to_surface accepts a unsetsurface arg/kwarg.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + expected_color = pygame.Color("red") + size = (5, 3) + mask = pygame.mask.Mask(size) + unsetsurface = pygame.Surface(size, expected_flag, expected_depth) + unsetsurface.fill(expected_color) + kwargs = {"unsetsurface": unsetsurface} + + for use_kwargs in (True, False): + if use_kwargs: + to_surface = mask.to_surface(**kwargs) + else: + to_surface = mask.to_surface(None, None, kwargs["unsetsurface"]) + + self.assertIsInstance(to_surface, pygame.Surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__setcolor_param(self): + """Ensures to_surface accepts a setcolor arg/kwarg.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + expected_color = pygame.Color("red") + size = (5, 3) + mask = pygame.mask.Mask(size, fill=True) + kwargs = {"setcolor": expected_color} + + for use_kwargs in (True, False): + if use_kwargs: + to_surface = mask.to_surface(**kwargs) + else: + to_surface = mask.to_surface(None, None, None, kwargs["setcolor"]) + + self.assertIsInstance(to_surface, pygame.Surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__setcolor_default(self): + """Ensures the default setcolor is correct.""" + expected_color = pygame.Color("white") + size = (3, 7) + mask = pygame.mask.Mask(size, fill=True) + + to_surface = mask.to_surface( + surface=None, setsurface=None, unsetsurface=None, unsetcolor=None + ) + + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__unsetcolor_param(self): + """Ensures to_surface accepts a unsetcolor arg/kwarg.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + expected_color = pygame.Color("red") + size = (5, 3) + mask = pygame.mask.Mask(size) + kwargs = {"unsetcolor": expected_color} + + for use_kwargs in (True, False): + if use_kwargs: + to_surface = mask.to_surface(**kwargs) + else: + to_surface = mask.to_surface( + None, None, None, None, kwargs["unsetcolor"] + ) + + self.assertIsInstance(to_surface, pygame.Surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__unsetcolor_default(self): + """Ensures the default unsetcolor is correct.""" + expected_color = pygame.Color("black") + size = (3, 7) + mask = pygame.mask.Mask(size) + + to_surface = mask.to_surface( + surface=None, setsurface=None, unsetsurface=None, setcolor=None + ) + + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__dest_param(self): + """Ensures to_surface accepts a dest arg/kwarg.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + default_surface_color = (0, 0, 0, 0) + default_unsetcolor = pygame.Color("black") + dest = (0, 0) + size = (5, 3) + mask = pygame.mask.Mask(size) + kwargs = {"dest": dest} + + for use_kwargs in (True, False): + if use_kwargs: + expected_color = default_unsetcolor + + to_surface = mask.to_surface(**kwargs) + else: + expected_color = default_surface_color + + to_surface = mask.to_surface( + None, None, None, None, None, kwargs["dest"] + ) + + self.assertIsInstance(to_surface, pygame.Surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__dest_default(self): + """Ensures the default dest is correct.""" + expected_color = pygame.Color("white") + surface_color = pygame.Color("red") + + mask_size = (3, 2) + mask = pygame.mask.Mask(mask_size, fill=True) + mask_rect = mask.get_rect() + + # Make the surface bigger than the mask. + surf_size = (mask_size[0] + 2, mask_size[1] + 1) + surface = pygame.Surface(surf_size, SRCALPHA, 32) + surface.fill(surface_color) + + to_surface = mask.to_surface( + surface, setsurface=None, unsetsurface=None, unsetcolor=None + ) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), surf_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + @unittest.expectedFailure + def test_to_surface__area_param(self): + """Ensures to_surface accepts an area arg/kwarg.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + default_surface_color = (0, 0, 0, 0) + default_unsetcolor = pygame.Color("black") + size = (5, 3) + mask = pygame.mask.Mask(size) + kwargs = {"area": mask.get_rect()} + + for use_kwargs in (True, False): + if use_kwargs: + expected_color = default_unsetcolor + + to_surface = mask.to_surface(**kwargs) + else: + expected_color = default_surface_color + + to_surface = mask.to_surface( + None, None, None, None, None, (0, 0), kwargs["area"] + ) + + self.assertIsInstance(to_surface, pygame.Surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__area_default(self): + """Ensures the default area is correct.""" + expected_color = pygame.Color("white") + surface_color = pygame.Color("red") + + mask_size = (3, 2) + mask = pygame.mask.Mask(mask_size, fill=True) + mask_rect = mask.get_rect() + + # Make the surface bigger than the mask. The default area is the full + # area of the mask. + surf_size = (mask_size[0] + 2, mask_size[1] + 1) + surface = pygame.Surface(surf_size, SRCALPHA, 32) + surface.fill(surface_color) + + to_surface = mask.to_surface( + surface, setsurface=None, unsetsurface=None, unsetcolor=None + ) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), surf_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + def test_to_surface__kwargs(self): + """Ensures to_surface accepts the correct kwargs.""" + expected_color = pygame.Color("white") + size = (5, 3) + mask = pygame.mask.Mask(size, fill=True) + surface = pygame.Surface(size) + surface_color = pygame.Color("red") + setsurface = surface.copy() + setsurface.fill(expected_color) + + test_data = ( + (None, None), # None entry allows loop to test all kwargs on first pass. + ("dest", (0, 0)), + ("unsetcolor", pygame.Color("yellow")), + ("setcolor", expected_color), + ("unsetsurface", surface.copy()), + ("setsurface", setsurface), + ("surface", surface), + ) + + kwargs = dict(test_data) + + for name, _ in test_data: + kwargs.pop(name) + surface.fill(surface_color) # Clear for each test. + + to_surface = mask.to_surface(**kwargs) + + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__kwargs_create_surface(self): + """Ensures to_surface accepts the correct kwargs + when creating a surface. + """ + expected_color = pygame.Color("black") + size = (5, 3) + mask = pygame.mask.Mask(size) + setsurface = pygame.Surface(size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + unsetsurface = setsurface.copy() + unsetsurface.fill(expected_color) + + test_data = ( + (None, None), # None entry allows loop to test all kwargs on first pass. + ("dest", (0, 0)), + ("unsetcolor", expected_color), + ("setcolor", pygame.Color("yellow")), + ("unsetsurface", unsetsurface), + ("setsurface", setsurface), + ("surface", None), + ) + kwargs = dict(test_data) + + for name, _ in test_data: + kwargs.pop(name) + + to_surface = mask.to_surface(**kwargs) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__kwargs_order_independent(self): + """Ensures to_surface kwargs are not order dependent.""" + expected_color = pygame.Color("blue") + size = (3, 2) + mask = pygame.mask.Mask(size, fill=True) + surface = pygame.Surface(size) + + to_surface = mask.to_surface( + dest=(0, 0), + setcolor=expected_color, + unsetcolor=None, + surface=surface, + unsetsurface=pygame.Surface(size), + setsurface=None, + ) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__args_invalid_types(self): + """Ensures to_surface detects invalid kwarg types.""" + size = (3, 2) + mask = pygame.mask.Mask(size, fill=True) + invalid_surf = pygame.Color("green") + invalid_color = pygame.Surface(size) + + with self.assertRaises(TypeError): + # Invalid dest. + mask.to_surface(None, None, None, None, None, (0,)) + + with self.assertRaises(TypeError): + # Invalid unsetcolor. + mask.to_surface(None, None, None, None, invalid_color) + + with self.assertRaises(TypeError): + # Invalid setcolor. + mask.to_surface(None, None, None, invalid_color, None) + + with self.assertRaises(TypeError): + # Invalid unsetsurface. + mask.to_surface(None, None, invalid_surf, None, None) + + with self.assertRaises(TypeError): + # Invalid setsurface. + mask.to_surface(None, invalid_surf, None, None, None) + + with self.assertRaises(TypeError): + # Invalid surface. + mask.to_surface(invalid_surf, None, None, None, None) + + def test_to_surface__kwargs_invalid_types(self): + """Ensures to_surface detects invalid kwarg types.""" + size = (3, 2) + mask = pygame.mask.Mask(size) + + valid_kwargs = { + "surface": pygame.Surface(size), + "setsurface": pygame.Surface(size), + "unsetsurface": pygame.Surface(size), + "setcolor": pygame.Color("green"), + "unsetcolor": pygame.Color("green"), + "dest": (0, 0), + } + + invalid_kwargs = { + "surface": (1, 2, 3, 4), + "setsurface": pygame.Color("green"), + "unsetsurface": ((1, 2), (2, 1)), + "setcolor": pygame.Mask((1, 2)), + "unsetcolor": pygame.Surface((2, 2)), + "dest": (0, 0, 0), + } + + kwarg_order = ( + "surface", + "setsurface", + "unsetsurface", + "setcolor", + "unsetcolor", + "dest", + ) + + for kwarg in kwarg_order: + kwargs = dict(valid_kwargs) + kwargs[kwarg] = invalid_kwargs[kwarg] + + with self.assertRaises(TypeError): + mask.to_surface(**kwargs) + + def test_to_surface__kwargs_invalid_name(self): + """Ensures to_surface detects invalid kwarg names.""" + mask = pygame.mask.Mask((3, 2)) + kwargs = {"setcolour": pygame.Color("red")} + + with self.assertRaises(TypeError): + mask.to_surface(**kwargs) + + def test_to_surface__args_and_kwargs(self): + """Ensures to_surface accepts a combination of args/kwargs""" + size = (5, 3) + + surface_color = pygame.Color("red") + setsurface_color = pygame.Color("yellow") + unsetsurface_color = pygame.Color("blue") + setcolor = pygame.Color("green") + unsetcolor = pygame.Color("cyan") + + surface = pygame.Surface(size, SRCALPHA, 32) + setsurface = surface.copy() + unsetsurface = surface.copy() + + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + mask = pygame.mask.Mask(size, fill=True) + expected_color = setsurface_color + + test_data = ( + (None, None), # None entry allows loop to test all kwargs on first pass. + ("surface", surface), + ("setsurface", setsurface), + ("unsetsurface", unsetsurface), + ("setcolor", setcolor), + ("unsetcolor", unsetcolor), + ("dest", (0, 0)), + ) + + args = [] + kwargs = dict(test_data) + + # Loop gradually moves the kwargs to args. + for name, value in test_data: + if name is not None: + args.append(value) + kwargs.pop(name) + + surface.fill(surface_color) + + to_surface = mask.to_surface(*args, **kwargs) + + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__valid_setcolor_formats(self): + """Ensures to_surface handles valid setcolor formats correctly.""" + size = (5, 3) + mask = pygame.mask.Mask(size, fill=True) + surface = pygame.Surface(size, SRCALPHA, 32) + expected_color = pygame.Color("green") + test_colors = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(expected_color), + expected_color, + "green", + "#00FF00FF", + "0x00FF00FF", + ) + + for setcolor in test_colors: + to_surface = mask.to_surface(setcolor=setcolor) + + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__valid_unsetcolor_formats(self): + """Ensures to_surface handles valid unsetcolor formats correctly.""" + size = (5, 3) + mask = pygame.mask.Mask(size) + surface = pygame.Surface(size, SRCALPHA, 32) + expected_color = pygame.Color("green") + test_colors = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(expected_color), + expected_color, + "green", + "#00FF00FF", + "0x00FF00FF", + ) + + for unsetcolor in test_colors: + to_surface = mask.to_surface(unsetcolor=unsetcolor) + + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__invalid_setcolor_formats(self): + """Ensures to_surface handles invalid setcolor formats correctly.""" + mask = pygame.mask.Mask((5, 3)) + + for setcolor in ("green color", "#00FF00FF0", "0x00FF00FF0", (1, 2)): + with self.assertRaises(ValueError): + mask.to_surface(setcolor=setcolor) + + for setcolor in (pygame.Surface((1, 2)), pygame.Mask((2, 1)), 1.1): + with self.assertRaises(TypeError): + mask.to_surface(setcolor=setcolor) + + def test_to_surface__invalid_unsetcolor_formats(self): + """Ensures to_surface handles invalid unsetcolor formats correctly.""" + mask = pygame.mask.Mask((5, 3)) + + for unsetcolor in ("green color", "#00FF00FF0", "0x00FF00FF0", (1, 2)): + with self.assertRaises(ValueError): + mask.to_surface(unsetcolor=unsetcolor) + + for unsetcolor in (pygame.Surface((1, 2)), pygame.Mask((2, 1)), 1.1): + with self.assertRaises(TypeError): + mask.to_surface(unsetcolor=unsetcolor) + + def test_to_surface__valid_dest_formats(self): + """Ensures to_surface handles valid dest formats correctly.""" + expected_color = pygame.Color("white") + mask = pygame.mask.Mask((3, 5), fill=True) + dests = ( + (0, 0), + [0, 0], + Vector2(0, 0), + (0, 0, 100, 100), + pygame.Rect((0, 0), (10, 10)), + ) + + for dest in dests: + to_surface = mask.to_surface(dest=dest) + + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__invalid_dest_formats(self): + """Ensures to_surface handles invalid dest formats correctly.""" + mask = pygame.mask.Mask((3, 5)) + invalid_dests = ( + (0,), # Incorrect size. + (0, 0, 0), # Incorrect size. + {0, 1}, # Incorrect type. + {0: 1}, # Incorrect type. + Rect, + ) # Incorrect type. + + for dest in invalid_dests: + with self.assertRaises(TypeError): + mask.to_surface(dest=dest) + + def test_to_surface__negative_sized_dest_rect(self): + """Ensures to_surface correctly handles negative sized dest rects.""" + expected_color = pygame.Color("white") + mask = pygame.mask.Mask((3, 5), fill=True) + dests = ( + pygame.Rect((0, 0), (10, -10)), + pygame.Rect((0, 0), (-10, 10)), + pygame.Rect((0, 0), (-10, -10)), + ) + + for dest in dests: + to_surface = mask.to_surface(dest=dest) + + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__zero_sized_dest_rect(self): + """Ensures to_surface correctly handles zero sized dest rects.""" + expected_color = pygame.Color("white") + mask = pygame.mask.Mask((3, 5), fill=True) + dests = ( + pygame.Rect((0, 0), (0, 10)), + pygame.Rect((0, 0), (10, 0)), + pygame.Rect((0, 0), (0, 0)), + ) + + for dest in dests: + to_surface = mask.to_surface(dest=dest) + + assertSurfaceFilled(self, to_surface, expected_color) + + @unittest.expectedFailure + def test_to_surface__valid_area_formats(self): + """Ensures to_surface handles valid area formats correctly.""" + size = (3, 5) + surface_color = pygame.Color("red") + expected_color = pygame.Color("white") + surface = pygame.Surface(size) + mask = pygame.mask.Mask(size, fill=True) + area_pos = (0, 0) + area_size = (2, 1) + areas = ( + (area_pos[0], area_pos[1], area_size[0], area_size[1]), + (area_pos, area_size), + (area_pos, list(area_size)), + (list(area_pos), area_size), + (list(area_pos), list(area_size)), + [area_pos[0], area_pos[1], area_size[0], area_size[1]], + [area_pos, area_size], + [area_pos, list(area_size)], + [list(area_pos), area_size], + [list(area_pos), list(area_size)], + pygame.Rect(area_pos, area_size), + ) + + for area in areas: + surface.fill(surface_color) + area_rect = pygame.Rect(area) + + to_surface = mask.to_surface(surface, area=area) + + assertSurfaceFilled(self, to_surface, expected_color, area_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, area_rect) + + @unittest.expectedFailure + def test_to_surface__invalid_area_formats(self): + """Ensures to_surface handles invalid area formats correctly.""" + mask = pygame.mask.Mask((3, 5)) + invalid_areas = ( + (0,), # Incorrect size. + (0, 0), # Incorrect size. + (0, 0, 1), # Incorrect size. + ((0, 0), (1,)), # Incorrect size. + ((0,), (1, 1)), # Incorrect size. + {0, 1, 2, 3}, # Incorrect type. + {0: 1, 2: 3}, # Incorrect type. + Rect, # Incorrect type. + ) + + for area in invalid_areas: + with self.assertRaisesRegex(TypeError, "invalid area argument"): + unused_to_surface = mask.to_surface(area=area) + + @unittest.expectedFailure + def test_to_surface__negative_sized_area_rect(self): + """Ensures to_surface correctly handles negative sized area rects.""" + size = (3, 5) + surface_color = pygame.Color("red") + expected_color = pygame.Color("white") + surface = pygame.Surface(size) + mask = pygame.mask.Mask(size) + mask.set_at((0, 0)) + + # These rects should cause position (0, 0) of the mask to be drawn. + areas = ( + pygame.Rect((0, 1), (1, -1)), + pygame.Rect((1, 0), (-1, 1)), + pygame.Rect((1, 1), (-1, -1)), + ) + + for area in areas: + surface.fill(surface_color) + + to_surface = mask.to_surface(surface, area=area) + + assertSurfaceFilled(self, to_surface, expected_color, area) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, area) + + @unittest.expectedFailure + def test_to_surface__zero_sized_area_rect(self): + """Ensures to_surface correctly handles zero sized area rects.""" + size = (3, 5) + expected_color = pygame.Color("red") + surface = pygame.Surface(size) + mask = pygame.mask.Mask(size, fill=True) + + # Zero sized rect areas should cause none of the mask to be drawn. + areas = ( + pygame.Rect((0, 0), (0, 1)), + pygame.Rect((0, 0), (1, 0)), + pygame.Rect((0, 0), (0, 0)), + ) + + for area in areas: + surface.fill(expected_color) + + to_surface = mask.to_surface(surface, area=area) + + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__default_surface_with_param_combinations(self): + """Ensures to_surface works with a default surface value + and combinations of other parameters. + + This tests many different parameter combinations with full and empty + masks. + """ + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + size = (5, 3) + dest = (0, 0) + + default_surface_color = (0, 0, 0, 0) + setsurface_color = pygame.Color("yellow") + unsetsurface_color = pygame.Color("blue") + setcolor = pygame.Color("green") + unsetcolor = pygame.Color("cyan") + + setsurface = pygame.Surface(size, expected_flag, expected_depth) + unsetsurface = setsurface.copy() + + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + kwargs = { + "setsurface": None, + "unsetsurface": None, + "setcolor": None, + "unsetcolor": None, + "dest": None, + } + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + + # Test different combinations of parameters. + for setsurface_param in (setsurface, None): + kwargs["setsurface"] = setsurface_param + + for unsetsurface_param in (unsetsurface, None): + kwargs["unsetsurface"] = unsetsurface_param + + for setcolor_param in (setcolor, None): + kwargs["setcolor"] = setcolor_param + + for unsetcolor_param in (unsetcolor, None): + kwargs["unsetcolor"] = unsetcolor_param + + for dest_param in (dest, None): + if dest_param is None: + kwargs.pop("dest", None) + else: + kwargs["dest"] = dest_param + + if fill: + if setsurface_param is not None: + expected_color = setsurface_color + elif setcolor_param is not None: + expected_color = setcolor + else: + expected_color = default_surface_color + else: + if unsetsurface_param is not None: + expected_color = unsetsurface_color + elif unsetcolor_param is not None: + expected_color = unsetcolor + else: + expected_color = default_surface_color + + to_surface = mask.to_surface(**kwargs) + + self.assertIsInstance(to_surface, pygame.Surface) + if not IS_PYPY: + self.assertEqual( + sys.getrefcount(to_surface), expected_ref_count + ) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual( + to_surface.get_bitsize(), expected_depth + ) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__surface_with_param_combinations(self): + """Ensures to_surface works with a surface value + and combinations of other parameters. + + This tests many different parameter combinations with full and empty + masks. + """ + expected_ref_count = 4 + expected_flag = SRCALPHA + expected_depth = 32 + size = (5, 3) + dest = (0, 0) + + surface_color = pygame.Color("red") + setsurface_color = pygame.Color("yellow") + unsetsurface_color = pygame.Color("blue") + setcolor = pygame.Color("green") + unsetcolor = pygame.Color("cyan") + + surface = pygame.Surface(size, expected_flag, expected_depth) + setsurface = surface.copy() + unsetsurface = surface.copy() + + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + kwargs = { + "surface": surface, + "setsurface": None, + "unsetsurface": None, + "setcolor": None, + "unsetcolor": None, + "dest": None, + } + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + + # Test different combinations of parameters. + for setsurface_param in (setsurface, None): + kwargs["setsurface"] = setsurface_param + + for unsetsurface_param in (unsetsurface, None): + kwargs["unsetsurface"] = unsetsurface_param + + for setcolor_param in (setcolor, None): + kwargs["setcolor"] = setcolor_param + + for unsetcolor_param in (unsetcolor, None): + kwargs["unsetcolor"] = unsetcolor_param + surface.fill(surface_color) # Clear for each test. + + for dest_param in (dest, None): + if dest_param is None: + kwargs.pop("dest", None) + else: + kwargs["dest"] = dest_param + + if fill: + if setsurface_param is not None: + expected_color = setsurface_color + elif setcolor_param is not None: + expected_color = setcolor + else: + expected_color = surface_color + else: + if unsetsurface_param is not None: + expected_color = unsetsurface_color + elif unsetcolor_param is not None: + expected_color = unsetcolor + else: + expected_color = surface_color + + to_surface = mask.to_surface(**kwargs) + + self.assertIs(to_surface, surface) + if not IS_PYPY: + self.assertEqual( + sys.getrefcount(to_surface), expected_ref_count + ) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual( + to_surface.get_bitsize(), expected_depth + ) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__set_and_unset_bits(self): + """Ensures that to_surface works correctly with with set/unset bits + when using the defaults for setcolor and unsetcolor. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + width, height = size = (10, 20) + mask = pygame.mask.Mask(size) + mask_rect = mask.get_rect() + + surface = pygame.Surface(size) + surface_color = pygame.Color("red") + + # Create a checkerboard pattern of set/unset bits. + for pos in ((x, y) for x in range(width) for y in range(x & 1, height, 2)): + mask.set_at(pos) + + # Test different dest values. + for dest in self.ORIGIN_OFFSETS: + mask_rect.topleft = dest + surface.fill(surface_color) + + to_surface = mask.to_surface(surface, dest=dest) + + to_surface.lock() # Lock for possible speed up. + for pos in ((x, y) for x in range(width) for y in range(height)): + mask_pos = (pos[0] - dest[0], pos[1] - dest[1]) + if not mask_rect.collidepoint(pos): + expected_color = surface_color + elif mask.get_at(mask_pos): + expected_color = default_setcolor + else: + expected_color = default_unsetcolor + + self.assertEqual(to_surface.get_at(pos), expected_color, (dest, pos)) + to_surface.unlock() + + def test_to_surface__set_and_unset_bits_with_setsurface_unsetsurface(self): + """Ensures that to_surface works correctly with with set/unset bits + when using setsurface and unsetsurface. + """ + width, height = size = (10, 20) + mask = pygame.mask.Mask(size) + mask_rect = mask.get_rect() + + surface = pygame.Surface(size) + surface_color = pygame.Color("red") + + setsurface = surface.copy() + setsurface_color = pygame.Color("green") + setsurface.fill(setsurface_color) + + unsetsurface = surface.copy() + unsetsurface_color = pygame.Color("blue") + unsetsurface.fill(unsetsurface_color) + + # Create a checkerboard pattern of set/unset bits. + for pos in ((x, y) for x in range(width) for y in range(x & 1, height, 2)): + mask.set_at(pos) + + # Test different dest values. + for dest in self.ORIGIN_OFFSETS: + mask_rect.topleft = dest + + # Tests the color parameters set to None and also as their + # default values. Should have no effect as they are not being + # used, but this exercises different to_surface() code. + for disable_color_params in (True, False): + surface.fill(surface_color) # Clear for each test. + + if disable_color_params: + to_surface = mask.to_surface( + surface, + dest=dest, + setsurface=setsurface, + unsetsurface=unsetsurface, + setcolor=None, + unsetcolor=None, + ) + else: + to_surface = mask.to_surface( + surface, + dest=dest, + setsurface=setsurface, + unsetsurface=unsetsurface, + ) + + to_surface.lock() # Lock for possible speed up. + + for pos in ((x, y) for x in range(width) for y in range(height)): + mask_pos = (pos[0] - dest[0], pos[1] - dest[1]) + + if not mask_rect.collidepoint(pos): + expected_color = surface_color + elif mask.get_at(mask_pos): + expected_color = setsurface_color + else: + expected_color = unsetsurface_color + + self.assertEqual(to_surface.get_at(pos), expected_color) + to_surface.unlock() + + def test_to_surface__surface_narrower_than_mask(self): + """Ensures that surfaces narrower than the mask work correctly. + + For this test the surface's width is less than the mask's width. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 20) + narrow_size = (6, 20) + + surface = pygame.Surface(narrow_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), narrow_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__setsurface_narrower_than_mask(self): + """Ensures that setsurfaces narrower than the mask work correctly. + + For this test the setsurface's width is less than the mask's width. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 20) + narrow_size = (6, 20) + + setsurface = pygame.Surface(narrow_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + setsurface_rect = setsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, setsurface_color, setsurface_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_setcolor, setsurface_rect + ) + else: + assertSurfaceFilled(self, to_surface, default_unsetcolor) + + def test_to_surface__unsetsurface_narrower_than_mask(self): + """Ensures that unsetsurfaces narrower than the mask work correctly. + + For this test the unsetsurface's width is less than the mask's width. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 20) + narrow_size = (6, 20) + + unsetsurface = pygame.Surface(narrow_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + unsetsurface_rect = unsetsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, default_setcolor) + else: + assertSurfaceFilled( + self, to_surface, unsetsurface_color, unsetsurface_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_unsetcolor, unsetsurface_rect + ) + + def test_to_surface__setsurface_narrower_than_mask_and_colors_none(self): + """Ensures that setsurfaces narrower than the mask work correctly + when setcolor and unsetcolor are set to None. + + For this test the setsurface's width is less than the mask's width. + """ + default_surface_color = (0, 0, 0, 0) + mask_size = (10, 20) + narrow_size = (6, 20) + + setsurface = pygame.Surface(narrow_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + setsurface_rect = setsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface( + setsurface=setsurface, setcolor=None, unsetcolor=None + ) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, setsurface_color, setsurface_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_surface_color, setsurface_rect + ) + else: + assertSurfaceFilled(self, to_surface, default_surface_color) + + def test_to_surface__unsetsurface_narrower_than_mask_and_colors_none(self): + """Ensures that unsetsurfaces narrower than the mask work correctly + when setcolor and unsetcolor are set to None. + + For this test the unsetsurface's width is less than the mask's width. + """ + default_surface_color = (0, 0, 0, 0) + mask_size = (10, 20) + narrow_size = (6, 20) + + unsetsurface = pygame.Surface(narrow_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + unsetsurface_rect = unsetsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface( + unsetsurface=unsetsurface, setcolor=None, unsetcolor=None + ) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, default_surface_color) + else: + assertSurfaceFilled( + self, to_surface, unsetsurface_color, unsetsurface_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_surface_color, unsetsurface_rect + ) + + def test_to_surface__surface_wider_than_mask(self): + """Ensures that surfaces wider than the mask work correctly. + + For this test the surface's width is greater than the mask's width. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (6, 15) + wide_size = (11, 15) + + surface = pygame.Surface(wide_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + mask_rect = mask.get_rect() + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), wide_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + def test_to_surface__setsurface_wider_than_mask(self): + """Ensures that setsurfaces wider than the mask work correctly. + + For this test the setsurface's width is greater than the mask's width. + """ + default_unsetcolor = pygame.Color("black") + mask_size = (6, 15) + wide_size = (11, 15) + + setsurface = pygame.Surface(wide_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + expected_color = setsurface_color if fill else default_unsetcolor + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__unsetsurface_wider_than_mask(self): + """Ensures that unsetsurfaces wider than the mask work correctly. + + For this test the unsetsurface's width is greater than the mask's + width. + """ + default_setcolor = pygame.Color("white") + mask_size = (6, 15) + wide_size = (11, 15) + + unsetsurface = pygame.Surface(wide_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + expected_color = default_setcolor if fill else unsetsurface_color + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__surface_shorter_than_mask(self): + """Ensures that surfaces shorter than the mask work correctly. + + For this test the surface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 11) + short_size = (10, 6) + + surface = pygame.Surface(short_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), short_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__setsurface_shorter_than_mask(self): + """Ensures that setsurfaces shorter than the mask work correctly. + + For this test the setsurface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 11) + short_size = (10, 6) + + setsurface = pygame.Surface(short_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + setsurface_rect = setsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, setsurface_color, setsurface_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_setcolor, setsurface_rect + ) + else: + assertSurfaceFilled(self, to_surface, default_unsetcolor) + + def test_to_surface__unsetsurface_shorter_than_mask(self): + """Ensures that unsetsurfaces shorter than the mask work correctly. + + For this test the unsetsurface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 11) + short_size = (10, 6) + + unsetsurface = pygame.Surface(short_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + unsetsurface_rect = unsetsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, default_setcolor) + else: + assertSurfaceFilled( + self, to_surface, unsetsurface_color, unsetsurface_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_unsetcolor, unsetsurface_rect + ) + + def test_to_surface__setsurface_shorter_than_mask_and_colors_none(self): + """Ensures that setsurfaces shorter than the mask work correctly + when setcolor and unsetcolor are set to None. + + For this test the setsurface's height is less than the mask's height. + """ + default_surface_color = (0, 0, 0, 0) + mask_size = (10, 11) + short_size = (10, 6) + + setsurface = pygame.Surface(short_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + setsurface_rect = setsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface( + setsurface=setsurface, setcolor=None, unsetcolor=None + ) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, setsurface_color, setsurface_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_surface_color, setsurface_rect + ) + else: + assertSurfaceFilled(self, to_surface, default_surface_color) + + def test_to_surface__unsetsurface_shorter_than_mask_and_colors_none(self): + """Ensures that unsetsurfaces shorter than the mask work correctly + when setcolor and unsetcolor are set to None. + + For this test the unsetsurface's height is less than the mask's height. + """ + default_surface_color = (0, 0, 0, 0) + mask_size = (10, 11) + short_size = (10, 6) + + unsetsurface = pygame.Surface(short_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + unsetsurface_rect = unsetsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface( + unsetsurface=unsetsurface, setcolor=None, unsetcolor=None + ) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, default_surface_color) + else: + assertSurfaceFilled( + self, to_surface, unsetsurface_color, unsetsurface_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_surface_color, unsetsurface_rect + ) + + def test_to_surface__surface_taller_than_mask(self): + """Ensures that surfaces taller than the mask work correctly. + + For this test the surface's height is greater than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 6) + tall_size = (10, 11) + + surface = pygame.Surface(tall_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + mask_rect = mask.get_rect() + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), tall_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + def test_to_surface__setsurface_taller_than_mask(self): + """Ensures that setsurfaces taller than the mask work correctly. + + For this test the setsurface's height is greater than the mask's + height. + """ + default_unsetcolor = pygame.Color("black") + mask_size = (10, 6) + tall_size = (10, 11) + + setsurface = pygame.Surface(tall_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + expected_color = setsurface_color if fill else default_unsetcolor + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__unsetsurface_taller_than_mask(self): + """Ensures that unsetsurfaces taller than the mask work correctly. + + For this test the unsetsurface's height is greater than the mask's + height. + """ + default_setcolor = pygame.Color("white") + mask_size = (10, 6) + tall_size = (10, 11) + + unsetsurface = pygame.Surface(tall_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + expected_color = default_setcolor if fill else unsetsurface_color + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__surface_wider_and_taller_than_mask(self): + """Ensures that surfaces wider and taller than the mask work correctly. + + For this test the surface's width is greater than the mask's width and + the surface's height is greater than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (6, 8) + wide_tall_size = (11, 15) + + surface = pygame.Surface(wide_tall_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + mask_rect = mask.get_rect() + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), wide_tall_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + def test_to_surface__setsurface_wider_and_taller_than_mask(self): + """Ensures that setsurfaces wider and taller than the mask work + correctly. + + For this test the setsurface's width is greater than the mask's width + and the setsurface's height is greater than the mask's height. + """ + default_unsetcolor = pygame.Color("black") + mask_size = (6, 8) + wide_tall_size = (11, 15) + + setsurface = pygame.Surface(wide_tall_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + expected_color = setsurface_color if fill else default_unsetcolor + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__unsetsurface_wider_and_taller_than_mask(self): + """Ensures that unsetsurfaces wider and taller than the mask work + correctly. + + For this test the unsetsurface's width is greater than the mask's width + and the unsetsurface's height is greater than the mask's height. + """ + default_setcolor = pygame.Color("white") + mask_size = (6, 8) + wide_tall_size = (11, 15) + + unsetsurface = pygame.Surface(wide_tall_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + expected_color = default_setcolor if fill else unsetsurface_color + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__surface_wider_and_shorter_than_mask(self): + """Ensures that surfaces wider and shorter than the mask work + correctly. + + For this test the surface's width is greater than the mask's width and + the surface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (7, 11) + wide_short_size = (13, 6) + + surface = pygame.Surface(wide_short_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + mask_rect = mask.get_rect() + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), wide_short_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + def test_to_surface__setsurface_wider_and_shorter_than_mask(self): + """Ensures that setsurfaces wider and shorter than the mask work + correctly. + + For this test the setsurface's width is greater than the mask's width + and the setsurface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (7, 11) + wide_short_size = (10, 6) + + setsurface = pygame.Surface(wide_short_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + setsurface_rect = setsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, setsurface_color, setsurface_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_setcolor, setsurface_rect + ) + else: + assertSurfaceFilled(self, to_surface, default_unsetcolor) + + def test_to_surface__unsetsurface_wider_and_shorter_than_mask(self): + """Ensures that unsetsurfaces wider and shorter than the mask work + correctly. + + For this test the unsetsurface's width is greater than the mask's width + and the unsetsurface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (7, 11) + wide_short_size = (10, 6) + + unsetsurface = pygame.Surface(wide_short_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + unsetsurface_rect = unsetsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, default_setcolor) + else: + assertSurfaceFilled( + self, to_surface, unsetsurface_color, unsetsurface_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_unsetcolor, unsetsurface_rect + ) + + def test_to_surface__surface_narrower_and_taller_than_mask(self): + """Ensures that surfaces narrower and taller than the mask work + correctly. + + For this test the surface's width is less than the mask's width and + the surface's height is greater than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 8) + narrow_tall_size = (6, 15) + + surface = pygame.Surface(narrow_tall_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + mask_rect = mask.get_rect() + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), narrow_tall_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + def test_to_surface__setsurface_narrower_and_taller_than_mask(self): + """Ensures that setsurfaces narrower and taller than the mask work + correctly. + + For this test the setsurface's width is less than the mask's width + and the setsurface's height is greater than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 8) + narrow_tall_size = (6, 15) + + setsurface = pygame.Surface(narrow_tall_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + setsurface_rect = setsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, setsurface_color, setsurface_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_setcolor, setsurface_rect + ) + else: + assertSurfaceFilled(self, to_surface, default_unsetcolor) + + def test_to_surface__unsetsurface_narrower_and_taller_than_mask(self): + """Ensures that unsetsurfaces narrower and taller than the mask work + correctly. + + For this test the unsetsurface's width is less than the mask's width + and the unsetsurface's height is greater than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 8) + narrow_tall_size = (6, 15) + + unsetsurface = pygame.Surface(narrow_tall_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + unsetsurface_rect = unsetsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, default_setcolor) + else: + assertSurfaceFilled( + self, to_surface, unsetsurface_color, unsetsurface_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_unsetcolor, unsetsurface_rect + ) + + def test_to_surface__surface_narrower_and_shorter_than_mask(self): + """Ensures that surfaces narrower and shorter than the mask work + correctly. + + For this test the surface's width is less than the mask's width and + the surface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 18) + narrow_short_size = (6, 15) + + surface = pygame.Surface(narrow_short_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + mask_rect = mask.get_rect() + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), narrow_short_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + def test_to_surface__setsurface_narrower_and_shorter_than_mask(self): + """Ensures that setsurfaces narrower and shorter than the mask work + correctly. + + For this test the setsurface's width is less than the mask's width + and the setsurface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 18) + narrow_short_size = (6, 15) + + setsurface = pygame.Surface(narrow_short_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + setsurface_rect = setsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, setsurface_color, setsurface_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_setcolor, setsurface_rect + ) + else: + assertSurfaceFilled(self, to_surface, default_unsetcolor) + + def test_to_surface__unsetsurface_narrower_and_shorter_than_mask(self): + """Ensures that unsetsurfaces narrower and shorter than the mask work + correctly. + + For this test the unsetsurface's width is less than the mask's width + and the unsetsurface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 18) + narrow_short_size = (6, 15) + + unsetsurface = pygame.Surface(narrow_short_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + unsetsurface_rect = unsetsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, default_setcolor) + else: + assertSurfaceFilled( + self, to_surface, unsetsurface_color, unsetsurface_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_unsetcolor, unsetsurface_rect + ) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_to_surface__all_surfaces_different_sizes_than_mask(self): + """Ensures that all the surface parameters can be of different sizes.""" + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + surface_color = pygame.Color("red") + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + + mask_size = (10, 15) + surface_size = (11, 14) + setsurface_size = (9, 8) + unsetsurface_size = (12, 16) + + surface = pygame.Surface(surface_size) + setsurface = pygame.Surface(setsurface_size) + unsetsurface = pygame.Surface(unsetsurface_size) + + surface.fill(surface_color) + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + surface_rect = surface.get_rect() + setsurface_rect = setsurface.get_rect() + unsetsurface_rect = unsetsurface.get_rect() + + # Create a mask that is filled except for a rect in the center. + mask = pygame.mask.Mask(mask_size, fill=True) + mask_rect = mask.get_rect() + unfilled_rect = pygame.Rect((0, 0), (4, 5)) + unfilled_rect.center = mask_rect.center + + for pos in ( + (x, y) + for x in range(unfilled_rect.x, unfilled_rect.w) + for y in range(unfilled_rect.y, unfilled_rect.h) + ): + mask.set_at(pos, 0) + + to_surface = mask.to_surface(surface, setsurface, unsetsurface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), surface_size) + + # Check each surface pixel for the correct color. + to_surface.lock() # Lock for possible speed up. + + for pos in ( + (x, y) for x in range(surface_rect.w) for y in range(surface_rect.h) + ): + if not mask_rect.collidepoint(pos): + expected_color = surface_color + elif mask.get_at(pos): + # Checking set bit colors. + if setsurface_rect.collidepoint(pos): + expected_color = setsurface_color + else: + expected_color = default_setcolor + else: + # Checking unset bit colors. + if unsetsurface_rect.collidepoint(pos): + expected_color = unsetsurface_color + else: + expected_color = default_unsetcolor + + self.assertEqual(to_surface.get_at(pos), expected_color) + + to_surface.unlock() + + def test_to_surface__dest_locations(self): + """Ensures dest values can be different locations on/off the surface.""" + SIDE = 7 + surface = pygame.Surface((SIDE, SIDE)) + surface_rect = surface.get_rect() + dest_rect = surface_rect.copy() + + surface_color = pygame.Color("red") + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + + directions = ( + ((s, 0) for s in range(-SIDE, SIDE + 1)), # left to right + ((0, s) for s in range(-SIDE, SIDE + 1)), # top to bottom + ((s, s) for s in range(-SIDE, SIDE + 1)), # topleft to bottomright diag + ((-s, s) for s in range(-SIDE, SIDE + 1)), # topright to bottomleft diag + ) + + for fill in (True, False): + mask = pygame.mask.Mask((SIDE, SIDE), fill=fill) + expected_color = default_setcolor if fill else default_unsetcolor + + for direction in directions: + for pos in direction: + dest_rect.topleft = pos + overlap_rect = dest_rect.clip(surface_rect) + surface.fill(surface_color) + + to_surface = mask.to_surface(surface, dest=dest_rect) + + assertSurfaceFilled(self, to_surface, expected_color, overlap_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, overlap_rect + ) + + @unittest.expectedFailure + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_to_surface__area_locations(self): + """Ensures area rects can be different locations on/off the mask.""" + SIDE = 7 + surface = pygame.Surface((SIDE, SIDE)) + + surface_color = pygame.Color("red") + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + + directions = ( + ((s, 0) for s in range(-SIDE, SIDE + 1)), # left to right + ((0, s) for s in range(-SIDE, SIDE + 1)), # top to bottom + ((s, s) for s in range(-SIDE, SIDE + 1)), # topleft to bottomright diag + ((-s, s) for s in range(-SIDE, SIDE + 1)), # topright to bottomleft diag + ) + + for fill in (True, False): + mask = pygame.mask.Mask((SIDE, SIDE), fill=fill) + mask_rect = mask.get_rect() + area_rect = mask_rect.copy() + expected_color = default_setcolor if fill else default_unsetcolor + + for direction in directions: + for pos in direction: + area_rect.topleft = pos + overlap_rect = area_rect.clip(mask_rect) + overlap_rect.topleft = (0, 0) + surface.fill(surface_color) + + to_surface = mask.to_surface(surface, area=area_rect) + + assertSurfaceFilled(self, to_surface, expected_color, overlap_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, overlap_rect + ) + + @unittest.expectedFailure + def test_to_surface__dest_and_area_locations(self): + """Ensures dest/area values can be different locations on/off the + surface/mask. + """ + SIDE = 5 + surface = pygame.Surface((SIDE, SIDE)) + surface_rect = surface.get_rect() + dest_rect = surface_rect.copy() + + surface_color = pygame.Color("red") + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + + dest_directions = ( + ((s, 0) for s in range(-SIDE, SIDE + 1)), # left to right + ((0, s) for s in range(-SIDE, SIDE + 1)), # top to bottom + ((s, s) for s in range(-SIDE, SIDE + 1)), # topleft to bottomright diag + ((-s, s) for s in range(-SIDE, SIDE + 1)), # topright to bottomleft diag + ) + + # Using only the topleft to bottomright diagonal to test the area (to + # reduce the number of loop iterations). + area_positions = list(dest_directions[2]) + + for fill in (True, False): + mask = pygame.mask.Mask((SIDE, SIDE), fill=fill) + mask_rect = mask.get_rect() + area_rect = mask_rect.copy() + expected_color = default_setcolor if fill else default_unsetcolor + + for dest_direction in dest_directions: + for dest_pos in dest_direction: + dest_rect.topleft = dest_pos + + for area_pos in area_positions: + area_rect.topleft = area_pos + area_overlap_rect = area_rect.clip(mask_rect) + area_overlap_rect.topleft = dest_rect.topleft + dest_overlap_rect = dest_rect.clip(area_overlap_rect) + + surface.fill(surface_color) + + to_surface = mask.to_surface( + surface, dest=dest_rect, area=area_rect + ) + + assertSurfaceFilled( + self, to_surface, expected_color, dest_overlap_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, dest_overlap_rect + ) + + @unittest.expectedFailure + def test_to_surface__area_sizes(self): + """Ensures area rects can be different sizes.""" + SIDE = 7 + SIZES = ( + (0, 0), + (0, 1), + (1, 0), + (1, 1), + (SIDE - 1, SIDE - 1), + (SIDE - 1, SIDE), + (SIDE, SIDE - 1), + (SIDE, SIDE), + (SIDE + 1, SIDE), + (SIDE, SIDE + 1), + (SIDE + 1, SIDE + 1), + ) + + surface = pygame.Surface((SIDE, SIDE)) + surface_color = pygame.Color("red") + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + + for fill in (True, False): + mask = pygame.mask.Mask((SIDE, SIDE), fill=fill) + mask_rect = mask.get_rect() + expected_color = default_setcolor if fill else default_unsetcolor + + for size in SIZES: + area_rect = pygame.Rect((0, 0), size) + + for pos in self.ORIGIN_OFFSETS: + area_rect.topleft = pos + overlap_rect = area_rect.clip(mask_rect) + overlap_rect.topleft = (0, 0) + surface.fill(surface_color) + + to_surface = mask.to_surface(surface, area=area_rect) + + assertSurfaceFilled(self, to_surface, expected_color, overlap_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, overlap_rect + ) + + def test_to_surface__surface_color_alphas(self): + """Ensures the setsurface/unsetsurface color alpha values are respected.""" + size = (13, 17) + setsurface_color = pygame.Color("green") + setsurface_color.a = 53 + unsetsurface_color = pygame.Color("blue") + unsetsurface_color.a = 109 + + setsurface = pygame.Surface(size, flags=SRCALPHA, depth=32) + unsetsurface = pygame.Surface(size, flags=SRCALPHA, depth=32) + + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + expected_color = setsurface_color if fill else unsetsurface_color + + to_surface = mask.to_surface( + setsurface=setsurface, unsetsurface=unsetsurface + ) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__color_alphas(self): + """Ensures the setcolor/unsetcolor alpha values are respected.""" + size = (13, 17) + setcolor = pygame.Color("green") + setcolor.a = 35 + unsetcolor = pygame.Color("blue") + unsetcolor.a = 213 + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + expected_color = setcolor if fill else unsetcolor + + to_surface = mask.to_surface(setcolor=setcolor, unsetcolor=unsetcolor) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__depths(self): + """Ensures to_surface works correctly with supported surface depths.""" + size = (13, 17) + surface_color = pygame.Color("red") + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + + for depth in (8, 16, 24, 32): + surface = pygame.Surface(size, depth=depth) + setsurface = pygame.Surface(size, depth=depth) + unsetsurface = pygame.Surface(size, depth=depth) + + surface.fill(surface_color) + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + + # For non-32 bit depths, the actual color can be different from + # what was filled. + expected_color = ( + setsurface.get_at((0, 0)) if fill else unsetsurface.get_at((0, 0)) + ) + + to_surface = mask.to_surface(surface, setsurface, unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__different_depths(self): + """Ensures an exception is raised when surfaces have different depths.""" + size = (13, 17) + surface_color = pygame.Color("red") + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + mask = pygame.mask.Mask(size) + + # Test different combinations of depths. + test_depths = ( + (8, 8, 16), # surface/setsurface/unsetsurface + (8, 8, 24), + (8, 8, 32), + (16, 16, 24), + (16, 16, 32), + (24, 16, 8), + (32, 16, 16), + (32, 32, 16), + (32, 24, 32), + ) + + for depths in test_depths: + surface = pygame.Surface(size, depth=depths[0]) + setsurface = pygame.Surface(size, depth=depths[1]) + unsetsurface = pygame.Surface(size, depth=depths[2]) + + surface.fill(surface_color) + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + with self.assertRaises(ValueError): + mask.to_surface(surface, setsurface, unsetsurface) + + def test_to_surface__different_depths_with_created_surfaces(self): + """Ensures an exception is raised when surfaces have different depths + than the created surface. + """ + size = (13, 17) + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + mask = pygame.mask.Mask(size) + + # Test different combinations of depths. The created surface always has + # a depth of 32. + test_depths = ( + (8, 8), # setsurface/unsetsurface + (16, 16), + (24, 24), + (24, 16), + (32, 8), + (32, 16), + (32, 24), + (16, 32), + ) + + for set_depth, unset_depth in test_depths: + setsurface = pygame.Surface(size, depth=set_depth) + unsetsurface = pygame.Surface(size, depth=unset_depth) + + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + with self.assertRaises(ValueError): + mask.to_surface(setsurface=setsurface, unsetsurface=unsetsurface) + + def test_to_surface__same_srcalphas(self): + """Ensures to_surface works correctly when the SRCALPHA flag is set or not.""" + size = (13, 17) + surface_color = pygame.Color("red") + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + + for depth in (16, 32): + for flags in (0, SRCALPHA): + surface = pygame.Surface(size, flags=flags, depth=depth) + setsurface = pygame.Surface(size, flags=flags, depth=depth) + unsetsurface = pygame.Surface(size, flags=flags, depth=depth) + + surface.fill(surface_color) + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + expected_color = setsurface_color if fill else unsetsurface_color + + to_surface = mask.to_surface(surface, setsurface, unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + if flags: + self.assertTrue(to_surface.get_flags() & flags) + + def test_to_surface__same_srcalphas_with_created_surfaces(self): + """Ensures to_surface works correctly when it creates a surface + and the SRCALPHA flag is set on both setsurface and unsetsurface. + """ + size = (13, 17) + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + # The created surface always has a depth of 32 and the SRCALPHA flag set. + expected_flags = SRCALPHA + + setsurface = pygame.Surface(size, flags=expected_flags, depth=32) + unsetsurface = pygame.Surface(size, flags=expected_flags, depth=32) + + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + expected_color = setsurface_color if fill else unsetsurface_color + + to_surface = mask.to_surface( + setsurface=setsurface, unsetsurface=unsetsurface + ) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + self.assertTrue(to_surface.get_flags() & expected_flags) + + def test_to_surface__different_srcalphas(self): + """Ensures an exception is raised when surfaces have different SRCALPHA + flag settings. + """ + size = (13, 17) + surface_color = pygame.Color("red") + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + mask = pygame.mask.Mask(size) + + # Test different combinations of SRCALPHA flags. + test_flags = ( + (SRCALPHA, 0, 0), # surface/setsurface/unsetsurface + (SRCALPHA, SRCALPHA, 0), + (0, SRCALPHA, SRCALPHA), + (0, 0, SRCALPHA), + ) + + for depth in (16, 32): + for flags in test_flags: + surface = pygame.Surface(size, flags=flags[0], depth=depth) + setsurface = pygame.Surface(size, flags=flags[1], depth=depth) + unsetsurface = pygame.Surface(size, flags=flags[2], depth=depth) + + surface.fill(surface_color) + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + with self.assertRaises(ValueError): + mask.to_surface(surface, setsurface, unsetsurface) + + def test_to_surface__different_srcalphas_with_created_surfaces(self): + """Ensures an exception is raised when surfaces have different SRCALPHA + flag settings than the created surface. + """ + size = (13, 17) + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + mask = pygame.mask.Mask(size) + + for depth in (16, 32): + # Test different combinations of SRCALPHA flags. The created + # surface always has the SRCALPHA flag set. + for flags in ((0, 0), (SRCALPHA, 0), (0, SRCALPHA)): + setsurface = pygame.Surface(size, flags=flags[0], depth=depth) + unsetsurface = pygame.Surface(size, flags=flags[1], depth=depth) + + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + with self.assertRaises(ValueError): + mask.to_surface(setsurface=setsurface, unsetsurface=unsetsurface) + + def test_to_surface__dest_on_surface(self): + """Ensures dest values on the surface work correctly + when using the defaults for setcolor and unsetcolor. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + width, height = size = (5, 9) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + mask_rect = mask.get_rect() + expected_color = default_setcolor if fill else default_unsetcolor + + # Test the dest parameter at different locations on the surface. + for dest in ((x, y) for y in range(height) for x in range(width)): + surface.fill(surface_color) # Clear for each test. + mask_rect.topleft = dest + + to_surface = mask.to_surface(surface, dest=dest) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, mask_rect + ) + + def test_to_surface__dest_on_surface_with_setsurface_unsetsurface(self): + """Ensures dest values on the surface work correctly + when using setsurface and unsetsurface. + """ + width, height = size = (5, 9) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + + setsurface = surface.copy() + setsurface_color = pygame.Color("green") + setsurface.fill(setsurface_color) + + unsetsurface = surface.copy() + unsetsurface_color = pygame.Color("blue") + unsetsurface.fill(unsetsurface_color) + + # Using different kwargs to exercise different to_surface() code. + # Should not have any impact on the resulting drawn surfaces. + kwargs = { + "surface": surface, + "setsurface": setsurface, + "unsetsurface": unsetsurface, + "dest": None, + } + + color_kwargs = dict(kwargs) + color_kwargs.update((("setcolor", None), ("unsetcolor", None))) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + mask_rect = mask.get_rect() + expected_color = setsurface_color if fill else unsetsurface_color + + # Test the dest parameter at different locations on the surface. + for dest in ((x, y) for y in range(height) for x in range(width)): + mask_rect.topleft = dest + + for use_color_params in (True, False): + surface.fill(surface_color) # Clear for each test. + + test_kwargs = color_kwargs if use_color_params else kwargs + test_kwargs["dest"] = dest + to_surface = mask.to_surface(**test_kwargs) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, mask_rect + ) + + def test_to_surface__dest_off_surface(self): + """Ensures dest values off the surface work correctly + when using the defaults for setcolor and unsetcolor. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + width, height = size = (5, 7) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + + # Test different dests off the surface. + dests = [(-width, -height), (-width, 0), (0, -height)] + dests.extend(off_corners(surface.get_rect())) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + mask_rect = mask.get_rect() + expected_color = default_setcolor if fill else default_unsetcolor + + for dest in dests: + surface.fill(surface_color) # Clear for each test. + mask_rect.topleft = dest + + to_surface = mask.to_surface(surface, dest=dest) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, mask_rect + ) + + def test_to_surface__dest_off_surface_with_setsurface_unsetsurface(self): + """Ensures dest values off the surface work correctly + when using setsurface and unsetsurface. + """ + width, height = size = (5, 7) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + + setsurface = surface.copy() + setsurface_color = pygame.Color("green") + setsurface.fill(setsurface_color) + + unsetsurface = surface.copy() + unsetsurface_color = pygame.Color("blue") + unsetsurface.fill(unsetsurface_color) + + # Test different dests off the surface. + dests = [(-width, -height), (-width, 0), (0, -height)] + dests.extend(off_corners(surface.get_rect())) + + # Using different kwargs to exercise different to_surface() code. + # Should not have any impact on the resulting drawn surfaces. + kwargs = { + "surface": surface, + "setsurface": setsurface, + "unsetsurface": unsetsurface, + "dest": None, + } + + color_kwargs = dict(kwargs) + color_kwargs.update((("setcolor", None), ("unsetcolor", None))) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + mask_rect = mask.get_rect() + expected_color = setsurface_color if fill else unsetsurface_color + + for dest in dests: + mask_rect.topleft = dest + + for use_color_params in (True, False): + surface.fill(surface_color) # Clear for each test. + test_kwargs = color_kwargs if use_color_params else kwargs + test_kwargs["dest"] = dest + to_surface = mask.to_surface(**test_kwargs) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, mask_rect + ) + + @unittest.expectedFailure + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_to_surface__area_on_mask(self): + """Ensures area values on the mask work correctly + when using the defaults for setcolor and unsetcolor. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + width, height = size = (5, 9) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + mask_rect = mask.get_rect() + area_rect = mask_rect.copy() + expected_color = default_setcolor if fill else default_unsetcolor + + # Testing the area parameter at different locations on the mask. + for pos in ((x, y) for y in range(height) for x in range(width)): + surface.fill(surface_color) # Clear for each test. + area_rect.topleft = pos + overlap_rect = mask_rect.clip(area_rect) + overlap_rect.topleft = (0, 0) + + to_surface = mask.to_surface(surface, area=area_rect) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color, overlap_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, overlap_rect + ) + + @unittest.expectedFailure + def test_to_surface__area_on_mask_with_setsurface_unsetsurface(self): + """Ensures area values on the mask work correctly + when using setsurface and unsetsurface. + """ + width, height = size = (5, 9) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + + setsurface = surface.copy() + setsurface_color = pygame.Color("green") + setsurface.fill(setsurface_color) + + unsetsurface = surface.copy() + unsetsurface_color = pygame.Color("blue") + unsetsurface.fill(unsetsurface_color) + + # Using the values in kwargs vs color_kwargs tests different to_surface + # code. Should not have any impact on the resulting drawn surfaces. + kwargs = { + "surface": surface, + "setsurface": setsurface, + "unsetsurface": unsetsurface, + "area": pygame.Rect((0, 0), size), + } + + color_kwargs = dict(kwargs) + color_kwargs.update((("setcolor", None), ("unsetcolor", None))) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + mask_rect = mask.get_rect() + area_rect = mask_rect.copy() + expected_color = setsurface_color if fill else unsetsurface_color + + # Testing the area parameter at different locations on the mask. + for pos in ((x, y) for y in range(height) for x in range(width)): + area_rect.topleft = pos + overlap_rect = mask_rect.clip(area_rect) + overlap_rect.topleft = (0, 0) + + for use_color_params in (True, False): + surface.fill(surface_color) # Clear for each test. + test_kwargs = color_kwargs if use_color_params else kwargs + test_kwargs["area"].topleft = pos + overlap_rect = mask_rect.clip(test_kwargs["area"]) + overlap_rect.topleft = (0, 0) + + to_surface = mask.to_surface(**test_kwargs) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color, overlap_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, overlap_rect + ) + + @unittest.expectedFailure + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_to_surface__area_off_mask(self): + """Ensures area values off the mask work correctly + when using the defaults for setcolor and unsetcolor. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + width, height = size = (5, 7) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + + # Testing positions off the mask. + positions = [(-width, -height), (-width, 0), (0, -height)] + positions.extend(off_corners(pygame.Rect((0, 0), (width, height)))) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + mask_rect = mask.get_rect() + area_rect = mask_rect.copy() + expected_color = default_setcolor if fill else default_unsetcolor + + for pos in positions: + surface.fill(surface_color) # Clear for each test. + area_rect.topleft = pos + overlap_rect = mask_rect.clip(area_rect) + overlap_rect.topleft = (0, 0) + + to_surface = mask.to_surface(surface, area=area_rect) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color, overlap_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, overlap_rect + ) + + @unittest.expectedFailure + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_to_surface__area_off_mask_with_setsurface_unsetsurface(self): + """Ensures area values off the mask work correctly + when using setsurface and unsetsurface. + """ + width, height = size = (5, 7) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + + setsurface = surface.copy() + setsurface_color = pygame.Color("green") + setsurface.fill(setsurface_color) + + unsetsurface = surface.copy() + unsetsurface_color = pygame.Color("blue") + unsetsurface.fill(unsetsurface_color) + + # Testing positions off the mask. + positions = [(-width, -height), (-width, 0), (0, -height)] + positions.extend(off_corners(pygame.Rect((0, 0), (width, height)))) + + # Using the values in kwargs vs color_kwargs tests different to_surface + # code. Should not have any impact on the resulting drawn surfaces. + kwargs = { + "surface": surface, + "setsurface": setsurface, + "unsetsurface": unsetsurface, + "area": pygame.Rect((0, 0), size), + } + + color_kwargs = dict(kwargs) + color_kwargs.update((("setcolor", None), ("unsetcolor", None))) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + mask_rect = mask.get_rect() + expected_color = setsurface_color if fill else unsetsurface_color + + for pos in positions: + for use_color_params in (True, False): + surface.fill(surface_color) # Clear for each test. + test_kwargs = color_kwargs if use_color_params else kwargs + test_kwargs["area"].topleft = pos + overlap_rect = mask_rect.clip(test_kwargs["area"]) + overlap_rect.topleft = (0, 0) + + to_surface = mask.to_surface(**test_kwargs) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color, overlap_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, overlap_rect + ) + + def test_to_surface__surface_with_zero_size(self): + """Ensures zero sized surfaces are handled correctly.""" + expected_ref_count = 3 + size = (0, 0) + surface = pygame.Surface(size) + mask = pygame.mask.Mask((3, 4), fill=True) + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertEqual(to_surface.get_size(), size) + + def test_to_surface__setsurface_with_zero_size(self): + """Ensures zero sized setsurfaces are handled correctly.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + expected_color = pygame.Color("white") # Default setcolor. + mask_size = (2, 4) + mask = pygame.mask.Mask(mask_size, fill=True) + setsurface = pygame.Surface((0, 0), expected_flag, expected_depth) + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__unsetsurface_with_zero_size(self): + """Ensures zero sized unsetsurfaces are handled correctly.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + expected_color = pygame.Color("black") # Default unsetcolor. + mask_size = (4, 2) + mask = pygame.mask.Mask(mask_size) + unsetsurface = pygame.Surface((0, 0), expected_flag, expected_depth) + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_zero_mask(self): + """Ensures masks can be created with zero sizes.""" + for size in ((100, 0), (0, 100), (0, 0)): + for fill in (True, False): + msg = f"size={size}, fill={fill}" + + mask = pygame.mask.Mask(size, fill=fill) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.get_size(), size, msg) + + def test_zero_mask_copy(self): + """Ensures copy correctly handles zero sized masks.""" + for expected_size in ((11, 0), (0, 11), (0, 0)): + mask = pygame.mask.Mask(expected_size) + + mask_copy = mask.copy() + + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + + def test_zero_mask_get_size(self): + """Ensures get_size correctly handles zero sized masks.""" + for expected_size in ((41, 0), (0, 40), (0, 0)): + mask = pygame.mask.Mask(expected_size) + + size = mask.get_size() + + self.assertEqual(size, expected_size) + + def test_zero_mask_get_rect(self): + """Ensures get_rect correctly handles zero sized masks.""" + for expected_size in ((4, 0), (0, 4), (0, 0)): + expected_rect = pygame.Rect((0, 0), expected_size) + mask = pygame.mask.Mask(expected_size) + + rect = mask.get_rect() + + self.assertEqual(rect, expected_rect) + + def test_zero_mask_get_at(self): + """Ensures get_at correctly handles zero sized masks.""" + for size in ((51, 0), (0, 50), (0, 0)): + mask = pygame.mask.Mask(size) + + with self.assertRaises(IndexError): + value = mask.get_at((0, 0)) + + def test_zero_mask_set_at(self): + """Ensures set_at correctly handles zero sized masks.""" + for size in ((31, 0), (0, 30), (0, 0)): + mask = pygame.mask.Mask(size) + + with self.assertRaises(IndexError): + mask.set_at((0, 0)) + + def test_zero_mask_overlap(self): + """Ensures overlap correctly handles zero sized masks. + + Tests combinations of sized and zero sized masks. + """ + offset = (0, 0) + + for size1, size2 in zero_size_pairs(51, 42): + msg = f"size1={size1}, size2={size2}" + mask1 = pygame.mask.Mask(size1, fill=True) + mask2 = pygame.mask.Mask(size2, fill=True) + + overlap_pos = mask1.overlap(mask2, offset) + + self.assertIsNone(overlap_pos, msg) + + def test_zero_mask_overlap_area(self): + """Ensures overlap_area correctly handles zero sized masks. + + Tests combinations of sized and zero sized masks. + """ + offset = (0, 0) + expected_count = 0 + + for size1, size2 in zero_size_pairs(41, 52): + msg = f"size1={size1}, size2={size2}" + mask1 = pygame.mask.Mask(size1, fill=True) + mask2 = pygame.mask.Mask(size2, fill=True) + + overlap_count = mask1.overlap_area(mask2, offset) + + self.assertEqual(overlap_count, expected_count, msg) + + def test_zero_mask_overlap_mask(self): + """Ensures overlap_mask correctly handles zero sized masks. + + Tests combinations of sized and zero sized masks. + """ + offset = (0, 0) + expected_count = 0 + + for size1, size2 in zero_size_pairs(43, 53): + msg = f"size1={size1}, size2={size2}" + mask1 = pygame.mask.Mask(size1, fill=True) + mask2 = pygame.mask.Mask(size2, fill=True) + + overlap_mask = mask1.overlap_mask(mask2, offset) + + self.assertIsInstance(overlap_mask, pygame.mask.Mask, msg) + self.assertEqual(overlap_mask.count(), expected_count, msg) + self.assertEqual(overlap_mask.get_size(), size1, msg) + + def test_zero_mask_fill(self): + """Ensures fill correctly handles zero sized masks.""" + expected_count = 0 + + for size in ((100, 0), (0, 100), (0, 0)): + mask = pygame.mask.Mask(size) + + mask.fill() + + self.assertEqual(mask.count(), expected_count, f"size={size}") + + def test_zero_mask_clear(self): + sizes = ((100, 0), (0, 100), (0, 0)) + + for size in sizes: + mask = pygame.mask.Mask(size) + mask.clear() + self.assertEqual(mask.count(), 0) + + def test_zero_mask_flip(self): + sizes = ((100, 0), (0, 100), (0, 0)) + + for size in sizes: + mask = pygame.mask.Mask(size) + mask.invert() + self.assertEqual(mask.count(), 0) + + def test_zero_mask_scale(self): + sizes = ((100, 0), (0, 100), (0, 0)) + + for size in sizes: + mask = pygame.mask.Mask(size) + mask2 = mask.scale((2, 3)) + + self.assertIsInstance(mask2, pygame.mask.Mask) + self.assertEqual(mask2.get_size(), (2, 3)) + + def test_zero_mask_draw(self): + """Ensures draw correctly handles zero sized masks. + + Tests combinations of sized and zero sized masks. + """ + offset = (0, 0) + + for size1, size2 in zero_size_pairs(31, 37): + msg = f"size1={size1}, size2={size2}" + mask1 = pygame.mask.Mask(size1, fill=True) + mask2 = pygame.mask.Mask(size2, fill=True) + expected_count = mask1.count() + + mask1.draw(mask2, offset) + + self.assertEqual(mask1.count(), expected_count, msg) + self.assertEqual(mask1.get_size(), size1, msg) + + def test_zero_mask_erase(self): + """Ensures erase correctly handles zero sized masks. + + Tests combinations of sized and zero sized masks. + """ + offset = (0, 0) + + for size1, size2 in zero_size_pairs(29, 23): + msg = f"size1={size1}, size2={size2}" + mask1 = pygame.mask.Mask(size1, fill=True) + mask2 = pygame.mask.Mask(size2, fill=True) + expected_count = mask1.count() + + mask1.erase(mask2, offset) + + self.assertEqual(mask1.count(), expected_count, msg) + self.assertEqual(mask1.get_size(), size1, msg) + + def test_zero_mask_count(self): + sizes = ((100, 0), (0, 100), (0, 0)) + + for size in sizes: + mask = pygame.mask.Mask(size, fill=True) + self.assertEqual(mask.count(), 0) + + def test_zero_mask_centroid(self): + sizes = ((100, 0), (0, 100), (0, 0)) + + for size in sizes: + mask = pygame.mask.Mask(size) + self.assertEqual(mask.centroid(), (0, 0)) + + def test_zero_mask_angle(self): + sizes = ((100, 0), (0, 100), (0, 0)) + + for size in sizes: + mask = pygame.mask.Mask(size) + self.assertEqual(mask.angle(), 0.0) + + def test_zero_mask_outline(self): + """Ensures outline correctly handles zero sized masks.""" + expected_points = [] + + for size in ((61, 0), (0, 60), (0, 0)): + mask = pygame.mask.Mask(size) + + points = mask.outline() + + self.assertListEqual(points, expected_points, f"size={size}") + + def test_zero_mask_outline__with_arg(self): + """Ensures outline correctly handles zero sized masks + when using the skip pixels argument.""" + expected_points = [] + + for size in ((66, 0), (0, 65), (0, 0)): + mask = pygame.mask.Mask(size) + + points = mask.outline(10) + + self.assertListEqual(points, expected_points, f"size={size}") + + def test_zero_mask_convolve(self): + """Ensures convolve correctly handles zero sized masks. + + Tests the different combinations of sized and zero sized masks. + """ + for size1 in ((17, 13), (71, 0), (0, 70), (0, 0)): + mask1 = pygame.mask.Mask(size1, fill=True) + + for size2 in ((11, 7), (81, 0), (0, 60), (0, 0)): + msg = f"sizes={size1}, {size2}" + mask2 = pygame.mask.Mask(size2, fill=True) + expected_size = ( + max(0, size1[0] + size2[0] - 1), + max(0, size1[1] + size2[1] - 1), + ) + + mask = mask1.convolve(mask2) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertIsNot(mask, mask2, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + + def test_zero_mask_convolve__with_output_mask(self): + """Ensures convolve correctly handles zero sized masks + when using an output mask argument. + + Tests the different combinations of sized and zero sized masks. + """ + for size1 in ((11, 17), (91, 0), (0, 90), (0, 0)): + mask1 = pygame.mask.Mask(size1, fill=True) + + for size2 in ((13, 11), (83, 0), (0, 62), (0, 0)): + mask2 = pygame.mask.Mask(size2, fill=True) + + for output_size in ((7, 5), (71, 0), (0, 70), (0, 0)): + msg = f"sizes={size1}, {size2}, {output_size}" + output_mask = pygame.mask.Mask(output_size) + + mask = mask1.convolve(mask2, output_mask) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertIs(mask, output_mask, msg) + self.assertEqual(mask.get_size(), output_size, msg) + + def test_zero_mask_connected_component(self): + """Ensures connected_component correctly handles zero sized masks.""" + expected_count = 0 + + for size in ((81, 0), (0, 80), (0, 0)): + msg = f"size={size}" + mask = pygame.mask.Mask(size) + + cc_mask = mask.connected_component() + + self.assertIsInstance(cc_mask, pygame.mask.Mask, msg) + self.assertEqual(cc_mask.get_size(), size) + self.assertEqual(cc_mask.count(), expected_count, msg) + + def test_zero_mask_connected_component__indexed(self): + """Ensures connected_component correctly handles zero sized masks + when using an index argument.""" + for size in ((91, 0), (0, 90), (0, 0)): + mask = pygame.mask.Mask(size) + + with self.assertRaises(IndexError): + cc_mask = mask.connected_component((0, 0)) + + def test_zero_mask_connected_components(self): + """Ensures connected_components correctly handles zero sized masks.""" + expected_cc_masks = [] + + for size in ((11, 0), (0, 10), (0, 0)): + mask = pygame.mask.Mask(size) + + cc_masks = mask.connected_components() + + self.assertListEqual(cc_masks, expected_cc_masks, f"size={size}") + + def test_zero_mask_get_bounding_rects(self): + """Ensures get_bounding_rects correctly handles zero sized masks.""" + expected_bounding_rects = [] + + for size in ((21, 0), (0, 20), (0, 0)): + mask = pygame.mask.Mask(size) + + bounding_rects = mask.get_bounding_rects() + + self.assertListEqual( + bounding_rects, expected_bounding_rects, f"size={size}" + ) + + def test_zero_mask_to_surface(self): + """Ensures to_surface correctly handles zero sized masks and surfaces.""" + mask_color = pygame.Color("blue") + surf_color = pygame.Color("red") + + for surf_size in ((7, 3), (7, 0), (0, 7), (0, 0)): + surface = pygame.Surface(surf_size, SRCALPHA, 32) + surface.fill(surf_color) + + for mask_size in ((5, 0), (0, 5), (0, 0)): + mask = pygame.mask.Mask(mask_size, fill=True) + + to_surface = mask.to_surface(surface, setcolor=mask_color) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), surf_size) + + if 0 not in surf_size: + assertSurfaceFilled(self, to_surface, surf_color) + + def test_zero_mask_to_surface__create_surface(self): + """Ensures to_surface correctly handles zero sized masks and surfaces + when it has to create a default surface. + """ + mask_color = pygame.Color("blue") + + for mask_size in ((3, 0), (0, 3), (0, 0)): + mask = pygame.mask.Mask(mask_size, fill=True) + + to_surface = mask.to_surface(setcolor=mask_color) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + +class SubMask(pygame.mask.Mask): + """Subclass of the Mask class to help test subclassing.""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.test_attribute = True + + +class SubMaskCopy(SubMask): + """Subclass of the Mask class to help test copying subclasses.""" + + def copy(self): + mask_copy = super().copy() + mask_copy.test_attribute = self.test_attribute + return mask_copy + + +class SubMaskDunderCopy(SubMask): + """Subclass of the Mask class to help test copying subclasses.""" + + def __copy__(self): + mask_copy = super().__copy__() + mask_copy.test_attribute = self.test_attribute + return mask_copy + + +class SubMaskCopyAndDunderCopy(SubMaskDunderCopy): + """Subclass of the Mask class to help test copying subclasses.""" + + def copy(self): + return super().copy() + + +class MaskSubclassTest(unittest.TestCase): + """Test subclassed Masks.""" + + def test_subclass_mask(self): + """Ensures the Mask class can be subclassed.""" + mask = SubMask((5, 3), fill=True) + + self.assertIsInstance(mask, pygame.mask.Mask) + self.assertIsInstance(mask, SubMask) + self.assertTrue(mask.test_attribute) + + def test_subclass_copy(self): + """Ensures copy works for subclassed Masks.""" + mask = SubMask((65, 2), fill=True) + + # Test both the copy() and __copy__() methods. + for mask_copy in (mask.copy(), copy.copy(mask)): + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsInstance(mask_copy, SubMask) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + # No subclass attributes because copy()/__copy__() not overridden. + self.assertFalse(hasattr(mask_copy, "test_attribute")) + + def test_subclass_copy__override_copy(self): + """Ensures copy works for subclassed Masks overriding copy.""" + mask = SubMaskCopy((65, 2), fill=True) + + # Test both the copy() and __copy__() methods. + for i, mask_copy in enumerate((mask.copy(), copy.copy(mask))): + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsInstance(mask_copy, SubMaskCopy) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + + if 1 == i: + # No subclass attributes because __copy__() not overridden. + self.assertFalse(hasattr(mask_copy, "test_attribute")) + else: + self.assertTrue(mask_copy.test_attribute) + + def test_subclass_copy__override_dunder_copy(self): + """Ensures copy works for subclassed Masks overriding __copy__.""" + mask = SubMaskDunderCopy((65, 2), fill=True) + + # Test both the copy() and __copy__() methods. + for mask_copy in (mask.copy(), copy.copy(mask)): + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsInstance(mask_copy, SubMaskDunderCopy) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + # Calls to copy() eventually call __copy__() internally so the + # attributes will be copied. + self.assertTrue(mask_copy.test_attribute) + + def test_subclass_copy__override_both_copy_methods(self): + """Ensures copy works for subclassed Masks overriding copy/__copy__.""" + mask = SubMaskCopyAndDunderCopy((65, 2), fill=True) + + # Test both the copy() and __copy__() methods. + for mask_copy in (mask.copy(), copy.copy(mask)): + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsInstance(mask_copy, SubMaskCopyAndDunderCopy) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + self.assertTrue(mask_copy.test_attribute) + + def test_subclass_get_size(self): + """Ensures get_size works for subclassed Masks.""" + expected_size = (2, 3) + mask = SubMask(expected_size) + + size = mask.get_size() + + self.assertEqual(size, expected_size) + + def test_subclass_mask_get_rect(self): + """Ensures get_rect works for subclassed Masks.""" + expected_rect = pygame.Rect((0, 0), (65, 33)) + mask = SubMask(expected_rect.size, fill=True) + + rect = mask.get_rect() + + self.assertEqual(rect, expected_rect) + + def test_subclass_get_at(self): + """Ensures get_at works for subclassed Masks.""" + expected_bit = 1 + mask = SubMask((3, 2), fill=True) + + bit = mask.get_at((0, 0)) + + self.assertEqual(bit, expected_bit) + + def test_subclass_set_at(self): + """Ensures set_at works for subclassed Masks.""" + expected_bit = 1 + expected_count = 1 + pos = (0, 0) + mask = SubMask(fill=False, size=(4, 2)) + + mask.set_at(pos) + + self.assertEqual(mask.get_at(pos), expected_bit) + self.assertEqual(mask.count(), expected_count) + + def test_subclass_overlap(self): + """Ensures overlap works for subclassed Masks.""" + expected_pos = (0, 0) + mask_size = (2, 3) + masks = (pygame.mask.Mask(fill=True, size=mask_size), SubMask(mask_size, True)) + arg_masks = ( + pygame.mask.Mask(fill=True, size=mask_size), + SubMask(mask_size, True), + ) + + # Test different combinations of subclassed and non-subclassed Masks. + for mask in masks: + for arg_mask in arg_masks: + overlap_pos = mask.overlap(arg_mask, (0, 0)) + + self.assertEqual(overlap_pos, expected_pos) + + def test_subclass_overlap_area(self): + """Ensures overlap_area works for subclassed Masks.""" + mask_size = (3, 2) + expected_count = mask_size[0] * mask_size[1] + masks = (pygame.mask.Mask(fill=True, size=mask_size), SubMask(mask_size, True)) + arg_masks = ( + pygame.mask.Mask(fill=True, size=mask_size), + SubMask(mask_size, True), + ) + + # Test different combinations of subclassed and non-subclassed Masks. + for mask in masks: + for arg_mask in arg_masks: + overlap_count = mask.overlap_area(arg_mask, (0, 0)) + + self.assertEqual(overlap_count, expected_count) + + def test_subclass_overlap_mask(self): + """Ensures overlap_mask works for subclassed Masks.""" + expected_size = (4, 5) + expected_count = expected_size[0] * expected_size[1] + masks = ( + pygame.mask.Mask(fill=True, size=expected_size), + SubMask(expected_size, True), + ) + arg_masks = ( + pygame.mask.Mask(fill=True, size=expected_size), + SubMask(expected_size, True), + ) + + # Test different combinations of subclassed and non-subclassed Masks. + for mask in masks: + for arg_mask in arg_masks: + overlap_mask = mask.overlap_mask(arg_mask, (0, 0)) + + self.assertIsInstance(overlap_mask, pygame.mask.Mask) + self.assertNotIsInstance(overlap_mask, SubMask) + self.assertEqual(overlap_mask.count(), expected_count) + self.assertEqual(overlap_mask.get_size(), expected_size) + + def test_subclass_fill(self): + """Ensures fill works for subclassed Masks.""" + mask_size = (2, 4) + expected_count = mask_size[0] * mask_size[1] + mask = SubMask(fill=False, size=mask_size) + + mask.fill() + + self.assertEqual(mask.count(), expected_count) + + def test_subclass_clear(self): + """Ensures clear works for subclassed Masks.""" + mask_size = (4, 3) + expected_count = 0 + mask = SubMask(mask_size, True) + + mask.clear() + + self.assertEqual(mask.count(), expected_count) + + def test_subclass_invert(self): + """Ensures invert works for subclassed Masks.""" + mask_size = (1, 4) + expected_count = mask_size[0] * mask_size[1] + mask = SubMask(fill=False, size=mask_size) + + mask.invert() + + self.assertEqual(mask.count(), expected_count) + + def test_subclass_scale(self): + """Ensures scale works for subclassed Masks.""" + expected_size = (5, 2) + mask = SubMask((1, 4)) + + scaled_mask = mask.scale(expected_size) + + self.assertIsInstance(scaled_mask, pygame.mask.Mask) + self.assertNotIsInstance(scaled_mask, SubMask) + self.assertEqual(scaled_mask.get_size(), expected_size) + + def test_subclass_draw(self): + """Ensures draw works for subclassed Masks.""" + mask_size = (5, 4) + expected_count = mask_size[0] * mask_size[1] + arg_masks = ( + pygame.mask.Mask(fill=True, size=mask_size), + SubMask(mask_size, True), + ) + + # Test different combinations of subclassed and non-subclassed Masks. + for mask in (pygame.mask.Mask(mask_size), SubMask(mask_size)): + for arg_mask in arg_masks: + mask.clear() # Clear for each test. + + mask.draw(arg_mask, (0, 0)) + + self.assertEqual(mask.count(), expected_count) + + def test_subclass_erase(self): + """Ensures erase works for subclassed Masks.""" + mask_size = (3, 4) + expected_count = 0 + masks = (pygame.mask.Mask(mask_size, True), SubMask(mask_size, True)) + arg_masks = (pygame.mask.Mask(mask_size, True), SubMask(mask_size, True)) + + # Test different combinations of subclassed and non-subclassed Masks. + for mask in masks: + for arg_mask in arg_masks: + mask.fill() # Fill for each test. + + mask.erase(arg_mask, (0, 0)) + + self.assertEqual(mask.count(), expected_count) + + def test_subclass_count(self): + """Ensures count works for subclassed Masks.""" + mask_size = (5, 2) + expected_count = mask_size[0] * mask_size[1] - 1 + mask = SubMask(fill=True, size=mask_size) + mask.set_at((1, 1), 0) + + count = mask.count() + + self.assertEqual(count, expected_count) + + def test_subclass_centroid(self): + """Ensures centroid works for subclassed Masks.""" + expected_centroid = (0, 0) + mask_size = (3, 2) + mask = SubMask((3, 2)) + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_subclass_angle(self): + """Ensures angle works for subclassed Masks.""" + expected_angle = 0.0 + mask = SubMask(size=(5, 4)) + + angle = mask.angle() + + self.assertAlmostEqual(angle, expected_angle) + + def test_subclass_outline(self): + """Ensures outline works for subclassed Masks.""" + expected_outline = [] + mask = SubMask((3, 4)) + + outline = mask.outline() + + self.assertListEqual(outline, expected_outline) + + def test_subclass_convolve(self): + """Ensures convolve works for subclassed Masks.""" + width, height = 7, 5 + mask_size = (width, height) + expected_count = 0 + expected_size = (max(0, width * 2 - 1), max(0, height * 2 - 1)) + + arg_masks = (pygame.mask.Mask(mask_size), SubMask(mask_size)) + output_masks = (pygame.mask.Mask(mask_size), SubMask(mask_size)) + + # Test different combinations of subclassed and non-subclassed Masks. + for mask in (pygame.mask.Mask(mask_size), SubMask(mask_size)): + for arg_mask in arg_masks: + convolve_mask = mask.convolve(arg_mask) + + self.assertIsInstance(convolve_mask, pygame.mask.Mask) + self.assertNotIsInstance(convolve_mask, SubMask) + self.assertEqual(convolve_mask.count(), expected_count) + self.assertEqual(convolve_mask.get_size(), expected_size) + + # Test subclassed masks for the output_mask as well. + for output_mask in output_masks: + convolve_mask = mask.convolve(arg_mask, output_mask) + + self.assertIsInstance(convolve_mask, pygame.mask.Mask) + self.assertEqual(convolve_mask.count(), expected_count) + self.assertEqual(convolve_mask.get_size(), mask_size) + + if isinstance(output_mask, SubMask): + self.assertIsInstance(convolve_mask, SubMask) + else: + self.assertNotIsInstance(convolve_mask, SubMask) + + def test_subclass_connected_component(self): + """Ensures connected_component works for subclassed Masks.""" + expected_count = 0 + expected_size = (3, 4) + mask = SubMask(expected_size) + + cc_mask = mask.connected_component() + + self.assertIsInstance(cc_mask, pygame.mask.Mask) + self.assertNotIsInstance(cc_mask, SubMask) + self.assertEqual(cc_mask.count(), expected_count) + self.assertEqual(cc_mask.get_size(), expected_size) + + def test_subclass_connected_components(self): + """Ensures connected_components works for subclassed Masks.""" + expected_ccs = [] + mask = SubMask((5, 4)) + + ccs = mask.connected_components() + + self.assertListEqual(ccs, expected_ccs) + + def test_subclass_get_bounding_rects(self): + """Ensures get_bounding_rects works for subclassed Masks.""" + expected_bounding_rects = [] + mask = SubMask((3, 2)) + + bounding_rects = mask.get_bounding_rects() + + self.assertListEqual(bounding_rects, expected_bounding_rects) + + def test_subclass_to_surface(self): + """Ensures to_surface works for subclassed Masks.""" + expected_color = pygame.Color("blue") + size = (5, 3) + mask = SubMask(size, fill=True) + surface = pygame.Surface(size, SRCALPHA, 32) + surface.fill(pygame.Color("red")) + + to_surface = mask.to_surface(surface, setcolor=expected_color) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + +@unittest.skipIf(IS_PYPY, "pypy has lots of mask failures") # TODO +class MaskModuleTest(unittest.TestCase): + def test_from_surface(self): + """Ensures from_surface creates a mask with the correct bits set. + + This test checks the masks created by the from_surface function using + 16 and 32 bit surfaces. Each alpha value (0-255) is tested against + several different threshold values. + Note: On 16 bit surface the requested alpha value can differ from what + is actually set. This test uses the value read from the surface. + """ + threshold_count = 256 + surface_color = [55, 155, 255, 0] + expected_size = (11, 9) + all_set_count = expected_size[0] * expected_size[1] + none_set_count = 0 + + for depth in (16, 32): + surface = pygame.Surface(expected_size, SRCALPHA, depth) + + for alpha in range(threshold_count): + surface_color[3] = alpha + surface.fill(surface_color) + + if depth < 32: + # On surfaces with depths < 32 the requested alpha can be + # different than what gets set. Use the value read from the + # surface. + alpha = surface.get_at((0, 0))[3] + + # Test the mask created at threshold values low, high and + # around alpha. + threshold_test_values = {-1, 0, alpha - 1, alpha, alpha + 1, 255, 256} + + for threshold in threshold_test_values: + msg = f"depth={depth}, alpha={alpha}, threshold={threshold}" + + if alpha > threshold: + expected_count = all_set_count + else: + expected_count = none_set_count + + mask = pygame.mask.from_surface( + surface=surface, threshold=threshold + ) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + self.assertEqual(mask.count(), expected_count, msg) + + def test_from_surface__different_alphas_32bit(self): + """Ensures from_surface creates a mask with the correct bits set + when pixels have different alpha values (32 bits surfaces). + + This test checks the masks created by the from_surface function using + a 32 bit surface. The surface is created with each pixel having a + different alpha value (0-255). This surface is tested over a range + of threshold values (0-255). + """ + offset = (0, 0) + threshold_count = 256 + surface_color = [10, 20, 30, 0] + expected_size = (threshold_count, 1) + expected_mask = pygame.Mask(expected_size, fill=True) + surface = pygame.Surface(expected_size, SRCALPHA, 32) + + # Give each pixel a different alpha. + surface.lock() # Lock for possible speed up. + for a in range(threshold_count): + surface_color[3] = a + surface.set_at((a, 0), surface_color) + surface.unlock() + + # Test the mask created for each different alpha threshold. + for threshold in range(threshold_count): + msg = f"threshold={threshold}" + expected_mask.set_at((threshold, 0), 0) + expected_count = expected_mask.count() + + mask = pygame.mask.from_surface(surface, threshold) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + self.assertEqual(mask.count(), expected_count, msg) + self.assertEqual( + mask.overlap_area(expected_mask, offset), expected_count, msg + ) + + def test_from_surface__different_alphas_16bit(self): + """Ensures from_surface creates a mask with the correct bits set + when pixels have different alpha values (16 bit surfaces). + + This test checks the masks created by the from_surface function using + a 16 bit surface. Each pixel of the surface is set with a different + alpha value (0-255), but since this is a 16 bit surface the requested + alpha value can differ from what is actually set. The resulting surface + will have groups of alpha values which complicates the test as the + alpha groups will all be set/unset at a given threshold. The setup + calculates these groups and an expected mask for each. This test data + is then used to test each alpha grouping over a range of threshold + values. + """ + threshold_count = 256 + surface_color = [110, 120, 130, 0] + expected_size = (threshold_count, 1) + surface = pygame.Surface(expected_size, SRCALPHA, 16) + + # Give each pixel a different alpha. + surface.lock() # Lock for possible speed up. + for a in range(threshold_count): + surface_color[3] = a + surface.set_at((a, 0), surface_color) + surface.unlock() + + alpha_thresholds = OrderedDict() + special_thresholds = set() + + # Create the threshold ranges and identify any thresholds that need + # special handling. + for threshold in range(threshold_count): + # On surfaces with depths < 32 the requested alpha can be different + # than what gets set. Use the value read from the surface. + alpha = surface.get_at((threshold, 0))[3] + + if alpha not in alpha_thresholds: + alpha_thresholds[alpha] = [threshold] + else: + alpha_thresholds[alpha].append(threshold) + + if threshold < alpha: + special_thresholds.add(threshold) + + # Use each threshold group to create an expected mask. + test_data = [] # [(from_threshold, to_threshold, expected_mask), ...] + offset = (0, 0) + erase_mask = pygame.Mask(expected_size) + exp_mask = pygame.Mask(expected_size, fill=True) + + for thresholds in alpha_thresholds.values(): + for threshold in thresholds: + if threshold in special_thresholds: + # Any special thresholds just reuse previous exp_mask. + test_data.append((threshold, threshold + 1, exp_mask)) + else: + to_threshold = thresholds[-1] + 1 + + # Make the expected mask by erasing the unset bits. + for thres in range(to_threshold): + erase_mask.set_at((thres, 0), 1) + + exp_mask = pygame.Mask(expected_size, fill=True) + exp_mask.erase(erase_mask, offset) + test_data.append((threshold, to_threshold, exp_mask)) + break + + # All the setup is done. Now test the masks created over the threshold + # ranges. + for from_threshold, to_threshold, expected_mask in test_data: + expected_count = expected_mask.count() + + for threshold in range(from_threshold, to_threshold): + msg = f"threshold={threshold}" + + mask = pygame.mask.from_surface(surface, threshold) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + self.assertEqual(mask.count(), expected_count, msg) + self.assertEqual( + mask.overlap_area(expected_mask, offset), expected_count, msg + ) + + def test_from_surface__with_colorkey_mask_cleared(self): + """Ensures from_surface creates a mask with the correct bits set + when the surface uses a colorkey. + + The surface is filled with the colorkey color so the resulting masks + are expected to have no bits set. + """ + colorkeys = ((0, 0, 0), (1, 2, 3), (50, 100, 200), (255, 255, 255)) + expected_size = (7, 11) + expected_count = 0 + + for depth in (8, 16, 24, 32): + msg = f"depth={depth}" + surface = pygame.Surface(expected_size, 0, depth) + + for colorkey in colorkeys: + surface.set_colorkey(colorkey) + # With some depths (i.e. 8 and 16) the actual colorkey can be + # different than what was requested via the set. + surface.fill(surface.get_colorkey()) + + mask = pygame.mask.from_surface(surface) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + self.assertEqual(mask.count(), expected_count, msg) + + def test_from_surface__with_colorkey_mask_filled(self): + """Ensures from_surface creates a mask with the correct bits set + when the surface uses a colorkey. + + The surface is filled with a color that is not the colorkey color so + the resulting masks are expected to have all bits set. + """ + colorkeys = ((0, 0, 0), (1, 2, 3), (10, 100, 200), (255, 255, 255)) + surface_color = (50, 100, 200) + expected_size = (11, 7) + expected_count = expected_size[0] * expected_size[1] + + for depth in (8, 16, 24, 32): + msg = f"depth={depth}" + surface = pygame.Surface(expected_size, 0, depth) + surface.fill(surface_color) + + for colorkey in colorkeys: + surface.set_colorkey(colorkey) + + mask = pygame.mask.from_surface(surface) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + self.assertEqual(mask.count(), expected_count, msg) + + def test_from_surface__with_colorkey_mask_pattern(self): + """Ensures from_surface creates a mask with the correct bits set + when the surface uses a colorkey. + + The surface is filled with alternating pixels of colorkey and + non-colorkey colors, so the resulting masks are expected to have + alternating bits set. + """ + + def alternate(func, set_value, unset_value, width, height): + # Helper function to set alternating values. + setbit = False + for pos in ((x, y) for x in range(width) for y in range(height)): + func(pos, set_value if setbit else unset_value) + setbit = not setbit + + surface_color = (5, 10, 20) + colorkey = (50, 60, 70) + expected_size = (11, 2) + expected_mask = pygame.mask.Mask(expected_size) + alternate(expected_mask.set_at, 1, 0, *expected_size) + expected_count = expected_mask.count() + offset = (0, 0) + + for depth in (8, 16, 24, 32): + msg = f"depth={depth}" + surface = pygame.Surface(expected_size, 0, depth) + # Fill the surface with alternating colors. + alternate(surface.set_at, surface_color, colorkey, *expected_size) + surface.set_colorkey(colorkey) + + mask = pygame.mask.from_surface(surface) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + self.assertEqual(mask.count(), expected_count, msg) + self.assertEqual( + mask.overlap_area(expected_mask, offset), expected_count, msg + ) + + def test_from_threshold(self): + """Does mask.from_threshold() work correctly?""" + + a = [16, 24, 32] + + for i in a: + surf = pygame.surface.Surface((70, 70), 0, i) + surf.fill((100, 50, 200), (20, 20, 20, 20)) + mask = pygame.mask.from_threshold( + surf, (100, 50, 200, 255), (10, 10, 10, 255) + ) + + rects = mask.get_bounding_rects() + + self.assertEqual(mask.count(), 400) + self.assertEqual(mask.get_bounding_rects(), [pygame.Rect((20, 20, 20, 20))]) + + for i in a: + surf = pygame.surface.Surface((70, 70), 0, i) + surf2 = pygame.surface.Surface((70, 70), 0, i) + surf.fill((100, 100, 100)) + surf2.fill((150, 150, 150)) + surf2.fill((100, 100, 100), (40, 40, 10, 10)) + mask = pygame.mask.from_threshold( + surface=surf, + color=(0, 0, 0, 0), + threshold=(10, 10, 10, 255), + othersurface=surf2, + ) + + self.assertIsInstance(mask, pygame.mask.Mask) + self.assertEqual(mask.count(), 100) + self.assertEqual(mask.get_bounding_rects(), [pygame.Rect((40, 40, 10, 10))]) + + def test_zero_size_from_surface(self): + """Ensures from_surface can create masks from zero sized surfaces.""" + for size in ((100, 0), (0, 100), (0, 0)): + mask = pygame.mask.from_surface(pygame.Surface(size)) + + self.assertIsInstance(mask, pygame.mask.MaskType, f"size={size}") + self.assertEqual(mask.get_size(), size) + + def test_zero_size_from_threshold(self): + a = [16, 24, 32] + sizes = ((100, 0), (0, 100), (0, 0)) + + for size in sizes: + for i in a: + surf = pygame.surface.Surface(size, 0, i) + surf.fill((100, 50, 200), (20, 20, 20, 20)) + mask = pygame.mask.from_threshold( + surf, (100, 50, 200, 255), (10, 10, 10, 255) + ) + + self.assertEqual(mask.count(), 0) + + rects = mask.get_bounding_rects() + self.assertEqual(rects, []) + + for i in a: + surf = pygame.surface.Surface(size, 0, i) + surf2 = pygame.surface.Surface(size, 0, i) + surf.fill((100, 100, 100)) + surf2.fill((150, 150, 150)) + surf2.fill((100, 100, 100), (40, 40, 10, 10)) + mask = pygame.mask.from_threshold( + surf, (0, 0, 0, 0), (10, 10, 10, 255), surf2 + ) + + self.assertIsInstance(mask, pygame.mask.Mask) + self.assertEqual(mask.count(), 0) + + rects = mask.get_bounding_rects() + self.assertEqual(rects, []) + + def test_buffer_interface(self): + size = (1000, 100) + pixels_set = ((0, 1), (100, 10), (173, 90)) + pixels_unset = ((0, 0), (101, 10), (173, 91)) + + mask = pygame.Mask(size) + for point in pixels_set: + mask.set_at(point, 1) + + view = memoryview(mask) + intwidth = 8 * view.strides[1] + + for point in pixels_set: + x, y = point + col = x // intwidth + self.assertEqual( + (view[col, y] >> (x % intwidth)) & 1, + 1, + f"the pixel at {point} is not set to 1", + ) + + for point in pixels_unset: + x, y = point + col = x // intwidth + self.assertEqual( + (view[col, y] >> (x % intwidth)) & 1, + 0, + f"the pixel at {point} is not set to 0", + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/math_test.py b/.venv/Lib/site-packages/pygame/tests/math_test.py new file mode 100644 index 00000000..cdb20b1d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/math_test.py @@ -0,0 +1,2930 @@ +import math +import platform +import unittest +from collections.abc import Collection, Sequence + +import pygame.math +from pygame.math import Vector2, Vector3 + +IS_PYPY = "PyPy" == platform.python_implementation() + + +class MathModuleTest(unittest.TestCase): + """Math module tests.""" + + def test_lerp(self): + result = pygame.math.lerp(10, 100, 0.5) # 55.0 + self.assertAlmostEqual(result, 55.0) + + result = pygame.math.lerp(10, 100, 0.0) # 10 + self.assertAlmostEqual(result, 10.0) + + result = pygame.math.lerp(10, 100, 1.0) # 100 + self.assertAlmostEqual(result, 100.0) + + # Not enough args + self.assertRaises(TypeError, pygame.math.lerp, 1) + + # Wrong arg type + self.assertRaises(TypeError, pygame.math.lerp, "str", "str", "str") + + # Percent outside range [0, 1] + self.assertRaises(ValueError, pygame.math.lerp, 10, 100, 1.1) + self.assertRaises(ValueError, pygame.math.lerp, 10, 100, -0.5) + + def test_clamp(self): + """Test clamp function.""" + + # Int tests + # Test going above max + result = pygame.math.clamp(10, 1, 5) + self.assertEqual(result, 5) + # Test going below min + result = pygame.math.clamp(-10, 1, 5) + self.assertEqual(result, 1) + # Test equal to max + result = pygame.math.clamp(5, 1, 5) + self.assertEqual(result, 5) + # Test equal to min + result = pygame.math.clamp(1, 1, 5) + self.assertEqual(result, 1) + # Test between min and max + result = pygame.math.clamp(3, 1, 5) + self.assertEqual(result, 3) + + # Float tests + # Test going above max + result = pygame.math.clamp(10.0, 1.12, 5.0) + self.assertAlmostEqual(result, 5.0) + # Test going below min + result = pygame.math.clamp(-10.0, 1.12, 5.0) + self.assertAlmostEqual(result, 1.12) + # Test equal to max + result = pygame.math.clamp(5.0, 1.12, 5.0) + self.assertAlmostEqual(result, 5.0) + # Test equal to min + result = pygame.math.clamp(1.12, 1.12, 5.0) + self.assertAlmostEqual(result, 1.12) + # Test between min and max + result = pygame.math.clamp(2.5, 1.12, 5.0) + self.assertAlmostEqual(result, 2.5) + + # Error tests + # Not enough args + self.assertRaises(TypeError, pygame.math.clamp, 10) + # Non numeric args + self.assertRaises(TypeError, pygame.math.clamp, "hello", "py", "thon") + + +class Vector2TypeTest(unittest.TestCase): + def setUp(self): + self.zeroVec = Vector2() + self.e1 = Vector2(1, 0) + self.e2 = Vector2(0, 1) + self.t1 = (1.2, 3.4) + self.l1 = list(self.t1) + self.v1 = Vector2(self.t1) + self.t2 = (5.6, 7.8) + self.l2 = list(self.t2) + self.v2 = Vector2(self.t2) + self.s1 = 5.6 + self.s2 = 7.8 + + def testConstructionDefault(self): + v = Vector2() + self.assertEqual(v.x, 0.0) + self.assertEqual(v.y, 0.0) + + def testConstructionScalar(self): + v = Vector2(1) + self.assertEqual(v.x, 1.0) + self.assertEqual(v.y, 1.0) + + def testConstructionScalarKeywords(self): + v = Vector2(x=1) + self.assertEqual(v.x, 1.0) + self.assertEqual(v.y, 1.0) + + def testConstructionKeywords(self): + v = Vector2(x=1, y=2) + self.assertEqual(v.x, 1.0) + self.assertEqual(v.y, 2.0) + + def testConstructionXY(self): + v = Vector2(1.2, 3.4) + self.assertEqual(v.x, 1.2) + self.assertEqual(v.y, 3.4) + + def testConstructionTuple(self): + v = Vector2((1.2, 3.4)) + self.assertEqual(v.x, 1.2) + self.assertEqual(v.y, 3.4) + + def testConstructionList(self): + v = Vector2([1.2, 3.4]) + self.assertEqual(v.x, 1.2) + self.assertEqual(v.y, 3.4) + + def testConstructionVector2(self): + v = Vector2(Vector2(1.2, 3.4)) + self.assertEqual(v.x, 1.2) + self.assertEqual(v.y, 3.4) + + def testAttributeAccess(self): + tmp = self.v1.x + self.assertEqual(tmp, self.v1.x) + self.assertEqual(tmp, self.v1[0]) + tmp = self.v1.y + self.assertEqual(tmp, self.v1.y) + self.assertEqual(tmp, self.v1[1]) + self.v1.x = 3.141 + self.assertEqual(self.v1.x, 3.141) + self.v1.y = 3.141 + self.assertEqual(self.v1.y, 3.141) + + def assign_nonfloat(): + v = Vector2() + v.x = "spam" + + self.assertRaises(TypeError, assign_nonfloat) + + def test___round___basic(self): + self.assertEqual(round(pygame.Vector2(0.0, 0.0)), pygame.Vector2(0.0, 0.0)) + self.assertEqual(type(round(pygame.Vector2(0.0, 0.0))), pygame.Vector2) + self.assertEqual( + round(pygame.Vector2(1.0, 1.0)), round(pygame.Vector2(1.0, 1.0)) + ) + self.assertEqual( + round(pygame.Vector2(10.0, 10.0)), round(pygame.Vector2(10.0, 10.0)) + ) + self.assertEqual( + round(pygame.Vector2(1000000000.0, 1000000000.0)), + pygame.Vector2(1000000000.0, 1000000000.0), + ) + self.assertEqual(round(pygame.Vector2(1e20, 1e20)), pygame.Vector2(1e20, 1e20)) + + self.assertEqual(round(pygame.Vector2(-1.0, -1.0)), pygame.Vector2(-1.0, -1.0)) + self.assertEqual( + round(pygame.Vector2(-10.0, -10.0)), pygame.Vector2(-10.0, -10.0) + ) + self.assertEqual( + round(pygame.Vector2(-1000000000.0, -1000000000.0)), + pygame.Vector2(-1000000000.0, -1000000000.0), + ) + self.assertEqual( + round(pygame.Vector2(-1e20, -1e20)), pygame.Vector2(-1e20, -1e20) + ) + + self.assertEqual(round(pygame.Vector2(0.1, 0.1)), pygame.Vector2(0.0, 0.0)) + self.assertEqual(round(pygame.Vector2(1.1, 1.1)), pygame.Vector2(1.0, 1.0)) + self.assertEqual(round(pygame.Vector2(10.1, 10.1)), pygame.Vector2(10.0, 10.0)) + self.assertEqual( + round(pygame.Vector2(1000000000.1, 1000000000.1)), + pygame.Vector2(1000000000.0, 1000000000.0), + ) + + self.assertEqual(round(pygame.Vector2(-1.1, -1.1)), pygame.Vector2(-1.0, -1.0)) + self.assertEqual( + round(pygame.Vector2(-10.1, -10.1)), pygame.Vector2(-10.0, -10.0) + ) + self.assertEqual( + round(pygame.Vector2(-1000000000.1, -1000000000.1)), + pygame.Vector2(-1000000000.0, -1000000000.0), + ) + + self.assertEqual(round(pygame.Vector2(0.9, 0.9)), pygame.Vector2(1.0, 1.0)) + self.assertEqual(round(pygame.Vector2(9.9, 9.9)), pygame.Vector2(10.0, 10.0)) + self.assertEqual( + round(pygame.Vector2(999999999.9, 999999999.9)), + pygame.Vector2(1000000000.0, 1000000000.0), + ) + + self.assertEqual(round(pygame.Vector2(-0.9, -0.9)), pygame.Vector2(-1.0, -1.0)) + self.assertEqual( + round(pygame.Vector2(-9.9, -9.9)), pygame.Vector2(-10.0, -10.0) + ) + self.assertEqual( + round(pygame.Vector2(-999999999.9, -999999999.9)), + pygame.Vector2(-1000000000.0, -1000000000.0), + ) + + self.assertEqual( + round(pygame.Vector2(-8.0, -8.0), -1), pygame.Vector2(-10.0, -10.0) + ) + self.assertEqual(type(round(pygame.Vector2(-8.0, -8.0), -1)), pygame.Vector2) + + self.assertEqual(type(round(pygame.Vector2(-8.0, -8.0), 0)), pygame.Vector2) + self.assertEqual(type(round(pygame.Vector2(-8.0, -8.0), 1)), pygame.Vector2) + + # Check even / odd rounding behaviour + self.assertEqual(round(pygame.Vector2(5.5, 5.5)), pygame.Vector2(6, 6)) + self.assertEqual(round(pygame.Vector2(5.4, 5.4)), pygame.Vector2(5.0, 5.0)) + self.assertEqual(round(pygame.Vector2(5.6, 5.6)), pygame.Vector2(6.0, 6.0)) + self.assertEqual(round(pygame.Vector2(-5.5, -5.5)), pygame.Vector2(-6, -6)) + self.assertEqual(round(pygame.Vector2(-5.4, -5.4)), pygame.Vector2(-5, -5)) + self.assertEqual(round(pygame.Vector2(-5.6, -5.6)), pygame.Vector2(-6, -6)) + + self.assertRaises(TypeError, round, pygame.Vector2(1.0, 1.0), 1.5) + self.assertRaises(TypeError, round, pygame.Vector2(1.0, 1.0), "a") + + def testCopy(self): + v_copy0 = Vector2(2004.0, 2022.0) + v_copy1 = v_copy0.copy() + self.assertEqual(v_copy0.x, v_copy1.x) + self.assertEqual(v_copy0.y, v_copy1.y) + + def test_move_towards_basic(self): + expected = Vector2(8.08, 2006.87) + origin = Vector2(7.22, 2004.0) + target = Vector2(12.30, 2021.0) + change_ip = Vector2(7.22, 2004.0) + + change = origin.move_towards(target, 3) + change_ip.move_towards_ip(target, 3) + + self.assertEqual(round(change.x, 2), expected.x) + self.assertEqual(round(change.y, 2), expected.y) + self.assertEqual(round(change_ip.x, 2), expected.x) + self.assertEqual(round(change_ip.y, 2), expected.y) + + def test_move_towards_max_distance(self): + expected = Vector2(12.30, 2021) + origin = Vector2(7.22, 2004.0) + target = Vector2(12.30, 2021.0) + change_ip = Vector2(7.22, 2004.0) + + change = origin.move_towards(target, 25) + change_ip.move_towards_ip(target, 25) + + self.assertEqual(round(change.x, 2), expected.x) + self.assertEqual(round(change.y, 2), expected.y) + self.assertEqual(round(change_ip.x, 2), expected.x) + self.assertEqual(round(change_ip.y, 2), expected.y) + + def test_move_nowhere(self): + expected = Vector2(7.22, 2004.0) + origin = Vector2(7.22, 2004.0) + target = Vector2(12.30, 2021.0) + change_ip = Vector2(7.22, 2004.0) + + change = origin.move_towards(target, 0) + change_ip.move_towards_ip(target, 0) + + self.assertEqual(round(change.x, 2), expected.x) + self.assertEqual(round(change.y, 2), expected.y) + self.assertEqual(round(change_ip.x, 2), expected.x) + self.assertEqual(round(change_ip.y, 2), expected.y) + + def test_move_away(self): + expected = Vector2(6.36, 2001.13) + origin = Vector2(7.22, 2004.0) + target = Vector2(12.30, 2021.0) + change_ip = Vector2(7.22, 2004.0) + + change = origin.move_towards(target, -3) + change_ip.move_towards_ip(target, -3) + + self.assertEqual(round(change.x, 2), expected.x) + self.assertEqual(round(change.y, 2), expected.y) + self.assertEqual(round(change_ip.x, 2), expected.x) + self.assertEqual(round(change_ip.y, 2), expected.y) + + def test_move_towards_self(self): + vec = Vector2(6.36, 2001.13) + vec2 = vec.copy() + for dist in (-3.54, -1, 0, 0.234, 12): + self.assertEqual(vec.move_towards(vec2, dist), vec) + vec2.move_towards_ip(vec, dist) + self.assertEqual(vec, vec2) + + def test_move_towards_errors(self): + def overpopulate(): + origin = Vector2(7.22, 2004.0) + target = Vector2(12.30, 2021.0) + origin.move_towards(target, 3, 2) + + def overpopulate_ip(): + origin = Vector2(7.22, 2004.0) + target = Vector2(12.30, 2021.0) + origin.move_towards_ip(target, 3, 2) + + def invalid_types1(): + origin = Vector2(7.22, 2004.0) + target = Vector2(12.30, 2021.0) + origin.move_towards(target, "novial") + + def invalid_types_ip1(): + origin = Vector2(7.22, 2004.0) + target = Vector2(12.30, 2021.0) + origin.move_towards_ip(target, "is") + + def invalid_types2(): + origin = Vector2(7.22, 2004.0) + target = Vector2(12.30, 2021.0) + origin.move_towards("kinda", 3) + + def invalid_types_ip2(): + origin = Vector2(7.22, 2004.0) + target = Vector2(12.30, 2021.0) + origin.move_towards_ip("cool", 3) + + self.assertRaises(TypeError, overpopulate) + self.assertRaises(TypeError, overpopulate_ip) + self.assertRaises(TypeError, invalid_types1) + self.assertRaises(TypeError, invalid_types_ip1) + self.assertRaises(TypeError, invalid_types2) + self.assertRaises(TypeError, invalid_types_ip2) + + def testSequence(self): + v = Vector2(1.2, 3.4) + Vector2()[:] + self.assertEqual(len(v), 2) + self.assertEqual(v[0], 1.2) + self.assertEqual(v[1], 3.4) + self.assertRaises(IndexError, lambda: v[2]) + self.assertEqual(v[-1], 3.4) + self.assertEqual(v[-2], 1.2) + self.assertRaises(IndexError, lambda: v[-3]) + self.assertEqual(v[:], [1.2, 3.4]) + self.assertEqual(v[1:], [3.4]) + self.assertEqual(v[:1], [1.2]) + self.assertEqual(list(v), [1.2, 3.4]) + self.assertEqual(tuple(v), (1.2, 3.4)) + v[0] = 5.6 + v[1] = 7.8 + self.assertEqual(v.x, 5.6) + self.assertEqual(v.y, 7.8) + v[:] = [9.1, 11.12] + self.assertEqual(v.x, 9.1) + self.assertEqual(v.y, 11.12) + + def overpopulate(): + v = Vector2() + v[:] = [1, 2, 3] + + self.assertRaises(ValueError, overpopulate) + + def underpopulate(): + v = Vector2() + v[:] = [1] + + self.assertRaises(ValueError, underpopulate) + + def assign_nonfloat(): + v = Vector2() + v[0] = "spam" + + self.assertRaises(TypeError, assign_nonfloat) + + def testExtendedSlicing(self): + # deletion + def delSlice(vec, start=None, stop=None, step=None): + if start is not None and stop is not None and step is not None: + del vec[start:stop:step] + elif start is not None and stop is None and step is not None: + del vec[start::step] + elif start is None and stop is None and step is not None: + del vec[::step] + + v = Vector2(self.v1) + self.assertRaises(TypeError, delSlice, v, None, None, 2) + self.assertRaises(TypeError, delSlice, v, 1, None, 2) + self.assertRaises(TypeError, delSlice, v, 1, 2, 1) + + # assignment + v = Vector2(self.v1) + v[::2] = [-1] + self.assertEqual(v, [-1, self.v1.y]) + v = Vector2(self.v1) + v[::-2] = [10] + self.assertEqual(v, [self.v1.x, 10]) + v = Vector2(self.v1) + v[::-1] = v + self.assertEqual(v, [self.v1.y, self.v1.x]) + a = Vector2(self.v1) + b = Vector2(self.v1) + c = Vector2(self.v1) + a[1:2] = [2.2] + b[slice(1, 2)] = [2.2] + c[1:2:] = (2.2,) + self.assertEqual(a, b) + self.assertEqual(a, c) + self.assertEqual(type(a), type(self.v1)) + self.assertEqual(type(b), type(self.v1)) + self.assertEqual(type(c), type(self.v1)) + + def test_contains(self): + c = Vector2(0, 1) + + # call __contains__ explicitly to test that it is defined + self.assertTrue(c.__contains__(0)) + self.assertTrue(0 in c) + self.assertTrue(1 in c) + self.assertTrue(2 not in c) + self.assertFalse(c.__contains__(2)) + + self.assertRaises(TypeError, lambda: "string" in c) + self.assertRaises(TypeError, lambda: 3 + 4j in c) + + def testAdd(self): + v3 = self.v1 + self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x + self.v2.x) + self.assertEqual(v3.y, self.v1.y + self.v2.y) + v3 = self.v1 + self.t2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x + self.t2[0]) + self.assertEqual(v3.y, self.v1.y + self.t2[1]) + v3 = self.v1 + self.l2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x + self.l2[0]) + self.assertEqual(v3.y, self.v1.y + self.l2[1]) + v3 = self.t1 + self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.t1[0] + self.v2.x) + self.assertEqual(v3.y, self.t1[1] + self.v2.y) + v3 = self.l1 + self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.l1[0] + self.v2.x) + self.assertEqual(v3.y, self.l1[1] + self.v2.y) + + def testSub(self): + v3 = self.v1 - self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x - self.v2.x) + self.assertEqual(v3.y, self.v1.y - self.v2.y) + v3 = self.v1 - self.t2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x - self.t2[0]) + self.assertEqual(v3.y, self.v1.y - self.t2[1]) + v3 = self.v1 - self.l2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x - self.l2[0]) + self.assertEqual(v3.y, self.v1.y - self.l2[1]) + v3 = self.t1 - self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.t1[0] - self.v2.x) + self.assertEqual(v3.y, self.t1[1] - self.v2.y) + v3 = self.l1 - self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.l1[0] - self.v2.x) + self.assertEqual(v3.y, self.l1[1] - self.v2.y) + + def testScalarMultiplication(self): + v = self.s1 * self.v1 + self.assertTrue(isinstance(v, type(self.v1))) + self.assertEqual(v.x, self.s1 * self.v1.x) + self.assertEqual(v.y, self.s1 * self.v1.y) + v = self.v1 * self.s2 + self.assertEqual(v.x, self.v1.x * self.s2) + self.assertEqual(v.y, self.v1.y * self.s2) + + def testScalarDivision(self): + v = self.v1 / self.s1 + self.assertTrue(isinstance(v, type(self.v1))) + self.assertAlmostEqual(v.x, self.v1.x / self.s1) + self.assertAlmostEqual(v.y, self.v1.y / self.s1) + v = self.v1 // self.s2 + self.assertTrue(isinstance(v, type(self.v1))) + self.assertEqual(v.x, self.v1.x // self.s2) + self.assertEqual(v.y, self.v1.y // self.s2) + + def testBool(self): + self.assertEqual(bool(self.zeroVec), False) + self.assertEqual(bool(self.v1), True) + self.assertTrue(not self.zeroVec) + self.assertTrue(self.v1) + + def testUnary(self): + v = +self.v1 + self.assertTrue(isinstance(v, type(self.v1))) + self.assertEqual(v.x, self.v1.x) + self.assertEqual(v.y, self.v1.y) + self.assertNotEqual(id(v), id(self.v1)) + v = -self.v1 + self.assertTrue(isinstance(v, type(self.v1))) + self.assertEqual(v.x, -self.v1.x) + self.assertEqual(v.y, -self.v1.y) + self.assertNotEqual(id(v), id(self.v1)) + + def testCompare(self): + int_vec = Vector2(3, -2) + flt_vec = Vector2(3.0, -2.0) + zero_vec = Vector2(0, 0) + self.assertEqual(int_vec == flt_vec, True) + self.assertEqual(int_vec != flt_vec, False) + self.assertEqual(int_vec != zero_vec, True) + self.assertEqual(flt_vec == zero_vec, False) + self.assertEqual(int_vec == (3, -2), True) + self.assertEqual(int_vec != (3, -2), False) + self.assertEqual(int_vec != [0, 0], True) + self.assertEqual(int_vec == [0, 0], False) + self.assertEqual(int_vec != 5, True) + self.assertEqual(int_vec == 5, False) + self.assertEqual(int_vec != [3, -2, 0], True) + self.assertEqual(int_vec == [3, -2, 0], False) + + def testStr(self): + v = Vector2(1.2, 3.4) + self.assertEqual(str(v), "[1.2, 3.4]") + + def testRepr(self): + v = Vector2(1.2, 3.4) + self.assertEqual(v.__repr__(), "") + self.assertEqual(v, Vector2(v.__repr__())) + + def testIter(self): + it = self.v1.__iter__() + next_ = it.__next__ + self.assertEqual(next_(), self.v1[0]) + self.assertEqual(next_(), self.v1[1]) + self.assertRaises(StopIteration, lambda: next_()) + it1 = self.v1.__iter__() + it2 = self.v1.__iter__() + self.assertNotEqual(id(it1), id(it2)) + self.assertEqual(id(it1), id(it1.__iter__())) + self.assertEqual(list(it1), list(it2)) + self.assertEqual(list(self.v1.__iter__()), self.l1) + idx = 0 + for val in self.v1: + self.assertEqual(val, self.v1[idx]) + idx += 1 + + def test_rotate(self): + v1 = Vector2(1, 0) + v2 = v1.rotate(90) + v3 = v1.rotate(90 + 360) + self.assertEqual(v1.x, 1) + self.assertEqual(v1.y, 0) + self.assertEqual(v2.x, 0) + self.assertEqual(v2.y, 1) + self.assertEqual(v3.x, v2.x) + self.assertEqual(v3.y, v2.y) + v1 = Vector2(-1, -1) + v2 = v1.rotate(-90) + self.assertEqual(v2.x, -1) + self.assertEqual(v2.y, 1) + v2 = v1.rotate(360) + self.assertEqual(v1.x, v2.x) + self.assertEqual(v1.y, v2.y) + v2 = v1.rotate(0) + self.assertEqual(v1.x, v2.x) + self.assertEqual(v1.y, v2.y) + # issue 214 + self.assertEqual(Vector2(0, 1).rotate(359.99999999), Vector2(0, 1)) + + def test_rotate_rad(self): + tests = ( + ((1, 0), math.pi), + ((1, 0), math.pi / 2), + ((1, 0), -math.pi / 2), + ((1, 0), math.pi / 4), + ) + for initialVec, radians in tests: + self.assertEqual( + Vector2(initialVec).rotate_rad(radians), + (math.cos(radians), math.sin(radians)), + ) + + def test_rotate_ip(self): + v = Vector2(1, 0) + self.assertEqual(v.rotate_ip(90), None) + self.assertEqual(v.x, 0) + self.assertEqual(v.y, 1) + v = Vector2(-1, -1) + v.rotate_ip(-90) + self.assertEqual(v.x, -1) + self.assertEqual(v.y, 1) + + def test_rotate_rad_ip(self): + tests = ( + ((1, 0), math.pi), + ((1, 0), math.pi / 2), + ((1, 0), -math.pi / 2), + ((1, 0), math.pi / 4), + ) + for initialVec, radians in tests: + vec = Vector2(initialVec) + vec.rotate_rad_ip(radians) + self.assertEqual(vec, (math.cos(radians), math.sin(radians))) + + def test_normalize(self): + v = self.v1.normalize() + # length is 1 + self.assertAlmostEqual(v.x * v.x + v.y * v.y, 1.0) + # v1 is unchanged + self.assertEqual(self.v1.x, self.l1[0]) + self.assertEqual(self.v1.y, self.l1[1]) + # v2 is parallel to v1 + self.assertAlmostEqual(self.v1.x * v.y - self.v1.y * v.x, 0.0) + self.assertRaises(ValueError, lambda: self.zeroVec.normalize()) + + def test_normalize_ip(self): + v = +self.v1 + # v has length != 1 before normalizing + self.assertNotEqual(v.x * v.x + v.y * v.y, 1.0) + # inplace operations should return None + self.assertEqual(v.normalize_ip(), None) + # length is 1 + self.assertAlmostEqual(v.x * v.x + v.y * v.y, 1.0) + # v2 is parallel to v1 + self.assertAlmostEqual(self.v1.x * v.y - self.v1.y * v.x, 0.0) + self.assertRaises(ValueError, lambda: self.zeroVec.normalize_ip()) + + def test_is_normalized(self): + self.assertEqual(self.v1.is_normalized(), False) + v = self.v1.normalize() + self.assertEqual(v.is_normalized(), True) + self.assertEqual(self.e2.is_normalized(), True) + self.assertEqual(self.zeroVec.is_normalized(), False) + + def test_cross(self): + self.assertEqual( + self.v1.cross(self.v2), self.v1.x * self.v2.y - self.v1.y * self.v2.x + ) + self.assertEqual( + self.v1.cross(self.l2), self.v1.x * self.l2[1] - self.v1.y * self.l2[0] + ) + self.assertEqual( + self.v1.cross(self.t2), self.v1.x * self.t2[1] - self.v1.y * self.t2[0] + ) + self.assertEqual(self.v1.cross(self.v2), -self.v2.cross(self.v1)) + self.assertEqual(self.v1.cross(self.v1), 0) + + def test_dot(self): + self.assertAlmostEqual( + self.v1.dot(self.v2), self.v1.x * self.v2.x + self.v1.y * self.v2.y + ) + self.assertAlmostEqual( + self.v1.dot(self.l2), self.v1.x * self.l2[0] + self.v1.y * self.l2[1] + ) + self.assertAlmostEqual( + self.v1.dot(self.t2), self.v1.x * self.t2[0] + self.v1.y * self.t2[1] + ) + self.assertEqual(self.v1.dot(self.v2), self.v2.dot(self.v1)) + self.assertEqual(self.v1.dot(self.v2), self.v1 * self.v2) + + def test_angle_to(self): + self.assertEqual( + self.v1.rotate(self.v1.angle_to(self.v2)).normalize(), self.v2.normalize() + ) + self.assertEqual(Vector2(1, 1).angle_to((-1, 1)), 90) + self.assertEqual(Vector2(1, 0).angle_to((0, -1)), -90) + self.assertEqual(Vector2(1, 0).angle_to((-1, 1)), 135) + self.assertEqual(abs(Vector2(1, 0).angle_to((-1, 0))), 180) + + def test_scale_to_length(self): + v = Vector2(1, 1) + v.scale_to_length(2.5) + self.assertEqual(v, Vector2(2.5, 2.5) / math.sqrt(2)) + self.assertRaises(ValueError, lambda: self.zeroVec.scale_to_length(1)) + self.assertEqual(v.scale_to_length(0), None) + self.assertEqual(v, self.zeroVec) + + def test_length(self): + self.assertEqual(Vector2(3, 4).length(), 5) + self.assertEqual(Vector2(-3, 4).length(), 5) + self.assertEqual(self.zeroVec.length(), 0) + + def test_length_squared(self): + self.assertEqual(Vector2(3, 4).length_squared(), 25) + self.assertEqual(Vector2(-3, 4).length_squared(), 25) + self.assertEqual(self.zeroVec.length_squared(), 0) + + def test_reflect(self): + v = Vector2(1, -1) + n = Vector2(0, 1) + self.assertEqual(v.reflect(n), Vector2(1, 1)) + self.assertEqual(v.reflect(3 * n), v.reflect(n)) + self.assertEqual(v.reflect(-v), -v) + self.assertRaises(ValueError, lambda: v.reflect(self.zeroVec)) + + def test_reflect_ip(self): + v1 = Vector2(1, -1) + v2 = Vector2(v1) + n = Vector2(0, 1) + self.assertEqual(v2.reflect_ip(n), None) + self.assertEqual(v2, Vector2(1, 1)) + v2 = Vector2(v1) + v2.reflect_ip(3 * n) + self.assertEqual(v2, v1.reflect(n)) + v2 = Vector2(v1) + v2.reflect_ip(-v1) + self.assertEqual(v2, -v1) + self.assertRaises(ValueError, lambda: v2.reflect_ip(Vector2())) + + def test_distance_to(self): + diff = self.v1 - self.v2 + self.assertEqual(self.e1.distance_to(self.e2), math.sqrt(2)) + self.assertEqual(self.e1.distance_to((0, 1)), math.sqrt(2)) + self.assertEqual(self.e1.distance_to([0, 1]), math.sqrt(2)) + self.assertAlmostEqual( + self.v1.distance_to(self.v2), math.sqrt(diff.x * diff.x + diff.y * diff.y) + ) + self.assertAlmostEqual( + self.v1.distance_to(self.t2), math.sqrt(diff.x * diff.x + diff.y * diff.y) + ) + self.assertAlmostEqual( + self.v1.distance_to(self.l2), math.sqrt(diff.x * diff.x + diff.y * diff.y) + ) + self.assertEqual(self.v1.distance_to(self.v1), 0) + self.assertEqual(self.v1.distance_to(self.t1), 0) + self.assertEqual(self.v1.distance_to(self.l1), 0) + self.assertEqual(self.v1.distance_to(self.t2), self.v2.distance_to(self.t1)) + self.assertEqual(self.v1.distance_to(self.l2), self.v2.distance_to(self.l1)) + self.assertEqual(self.v1.distance_to(self.v2), self.v2.distance_to(self.v1)) + + def test_distance_squared_to(self): + diff = self.v1 - self.v2 + self.assertEqual(self.e1.distance_squared_to(self.e2), 2) + self.assertEqual(self.e1.distance_squared_to((0, 1)), 2) + self.assertEqual(self.e1.distance_squared_to([0, 1]), 2) + self.assertAlmostEqual( + self.v1.distance_squared_to(self.v2), diff.x * diff.x + diff.y * diff.y + ) + self.assertAlmostEqual( + self.v1.distance_squared_to(self.t2), diff.x * diff.x + diff.y * diff.y + ) + self.assertAlmostEqual( + self.v1.distance_squared_to(self.l2), diff.x * diff.x + diff.y * diff.y + ) + self.assertEqual(self.v1.distance_squared_to(self.v1), 0) + self.assertEqual(self.v1.distance_squared_to(self.t1), 0) + self.assertEqual(self.v1.distance_squared_to(self.l1), 0) + self.assertEqual( + self.v1.distance_squared_to(self.v2), self.v2.distance_squared_to(self.v1) + ) + self.assertEqual( + self.v1.distance_squared_to(self.t2), self.v2.distance_squared_to(self.t1) + ) + self.assertEqual( + self.v1.distance_squared_to(self.l2), self.v2.distance_squared_to(self.l1) + ) + + def test_update(self): + v = Vector2(3, 4) + v.update(0) + self.assertEqual(v, Vector2((0, 0))) + v.update(5, 1) + self.assertEqual(v, Vector2(5, 1)) + v.update((4, 1)) + self.assertNotEqual(v, Vector2((5, 1))) + + def test_swizzle(self): + self.assertEqual(self.v1.yx, (self.v1.y, self.v1.x)) + self.assertEqual( + self.v1.xxyyxy, + (self.v1.x, self.v1.x, self.v1.y, self.v1.y, self.v1.x, self.v1.y), + ) + self.v1.xy = self.t2 + self.assertEqual(self.v1, self.t2) + self.v1.yx = self.t2 + self.assertEqual(self.v1, (self.t2[1], self.t2[0])) + self.assertEqual(type(self.v1), Vector2) + + def invalidSwizzleX(): + Vector2().xx = (1, 2) + + def invalidSwizzleY(): + Vector2().yy = (1, 2) + + self.assertRaises(AttributeError, invalidSwizzleX) + self.assertRaises(AttributeError, invalidSwizzleY) + + def invalidAssignment(): + Vector2().xy = 3 + + self.assertRaises(TypeError, invalidAssignment) + + def unicodeAttribute(): + getattr(Vector2(), "ä") + + self.assertRaises(AttributeError, unicodeAttribute) + + def test_swizzle_return_types(self): + self.assertEqual(type(self.v1.x), float) + self.assertEqual(type(self.v1.xy), Vector2) + self.assertEqual(type(self.v1.xyx), Vector3) + # but we don't have vector4 or above... so tuple. + self.assertEqual(type(self.v1.xyxy), tuple) + self.assertEqual(type(self.v1.xyxyx), tuple) + + def test_elementwise(self): + v1 = self.v1 + v2 = self.v2 + s1 = self.s1 + s2 = self.s2 + # behaviour for "elementwise op scalar" + self.assertEqual(v1.elementwise() + s1, (v1.x + s1, v1.y + s1)) + self.assertEqual(v1.elementwise() - s1, (v1.x - s1, v1.y - s1)) + self.assertEqual(v1.elementwise() * s2, (v1.x * s2, v1.y * s2)) + self.assertEqual(v1.elementwise() / s2, (v1.x / s2, v1.y / s2)) + self.assertEqual(v1.elementwise() // s1, (v1.x // s1, v1.y // s1)) + self.assertEqual(v1.elementwise() ** s1, (v1.x**s1, v1.y**s1)) + self.assertEqual(v1.elementwise() % s1, (v1.x % s1, v1.y % s1)) + self.assertEqual(v1.elementwise() > s1, v1.x > s1 and v1.y > s1) + self.assertEqual(v1.elementwise() < s1, v1.x < s1 and v1.y < s1) + self.assertEqual(v1.elementwise() == s1, v1.x == s1 and v1.y == s1) + self.assertEqual(v1.elementwise() != s1, s1 not in [v1.x, v1.y]) + self.assertEqual(v1.elementwise() >= s1, v1.x >= s1 and v1.y >= s1) + self.assertEqual(v1.elementwise() <= s1, v1.x <= s1 and v1.y <= s1) + self.assertEqual(v1.elementwise() != s1, s1 not in [v1.x, v1.y]) + # behaviour for "scalar op elementwise" + self.assertEqual(s1 + v1.elementwise(), (s1 + v1.x, s1 + v1.y)) + self.assertEqual(s1 - v1.elementwise(), (s1 - v1.x, s1 - v1.y)) + self.assertEqual(s1 * v1.elementwise(), (s1 * v1.x, s1 * v1.y)) + self.assertEqual(s1 / v1.elementwise(), (s1 / v1.x, s1 / v1.y)) + self.assertEqual(s1 // v1.elementwise(), (s1 // v1.x, s1 // v1.y)) + self.assertEqual(s1 ** v1.elementwise(), (s1**v1.x, s1**v1.y)) + self.assertEqual(s1 % v1.elementwise(), (s1 % v1.x, s1 % v1.y)) + self.assertEqual(s1 < v1.elementwise(), s1 < v1.x and s1 < v1.y) + self.assertEqual(s1 > v1.elementwise(), s1 > v1.x and s1 > v1.y) + self.assertEqual(s1 == v1.elementwise(), s1 == v1.x and s1 == v1.y) + self.assertEqual(s1 != v1.elementwise(), s1 not in [v1.x, v1.y]) + self.assertEqual(s1 <= v1.elementwise(), s1 <= v1.x and s1 <= v1.y) + self.assertEqual(s1 >= v1.elementwise(), s1 >= v1.x and s1 >= v1.y) + self.assertEqual(s1 != v1.elementwise(), s1 not in [v1.x, v1.y]) + + # behaviour for "elementwise op vector" + self.assertEqual(type(v1.elementwise() * v2), type(v1)) + self.assertEqual(v1.elementwise() + v2, v1 + v2) + self.assertEqual(v1.elementwise() - v2, v1 - v2) + self.assertEqual(v1.elementwise() * v2, (v1.x * v2.x, v1.y * v2.y)) + self.assertEqual(v1.elementwise() / v2, (v1.x / v2.x, v1.y / v2.y)) + self.assertEqual(v1.elementwise() // v2, (v1.x // v2.x, v1.y // v2.y)) + self.assertEqual(v1.elementwise() ** v2, (v1.x**v2.x, v1.y**v2.y)) + self.assertEqual(v1.elementwise() % v2, (v1.x % v2.x, v1.y % v2.y)) + self.assertEqual(v1.elementwise() > v2, v1.x > v2.x and v1.y > v2.y) + self.assertEqual(v1.elementwise() < v2, v1.x < v2.x and v1.y < v2.y) + self.assertEqual(v1.elementwise() >= v2, v1.x >= v2.x and v1.y >= v2.y) + self.assertEqual(v1.elementwise() <= v2, v1.x <= v2.x and v1.y <= v2.y) + self.assertEqual(v1.elementwise() == v2, v1.x == v2.x and v1.y == v2.y) + self.assertEqual(v1.elementwise() != v2, v1.x != v2.x and v1.y != v2.y) + # behaviour for "vector op elementwise" + self.assertEqual(v2 + v1.elementwise(), v2 + v1) + self.assertEqual(v2 - v1.elementwise(), v2 - v1) + self.assertEqual(v2 * v1.elementwise(), (v2.x * v1.x, v2.y * v1.y)) + self.assertEqual(v2 / v1.elementwise(), (v2.x / v1.x, v2.y / v1.y)) + self.assertEqual(v2 // v1.elementwise(), (v2.x // v1.x, v2.y // v1.y)) + self.assertEqual(v2 ** v1.elementwise(), (v2.x**v1.x, v2.y**v1.y)) + self.assertEqual(v2 % v1.elementwise(), (v2.x % v1.x, v2.y % v1.y)) + self.assertEqual(v2 < v1.elementwise(), v2.x < v1.x and v2.y < v1.y) + self.assertEqual(v2 > v1.elementwise(), v2.x > v1.x and v2.y > v1.y) + self.assertEqual(v2 <= v1.elementwise(), v2.x <= v1.x and v2.y <= v1.y) + self.assertEqual(v2 >= v1.elementwise(), v2.x >= v1.x and v2.y >= v1.y) + self.assertEqual(v2 == v1.elementwise(), v2.x == v1.x and v2.y == v1.y) + self.assertEqual(v2 != v1.elementwise(), v2.x != v1.x and v2.y != v1.y) + + # behaviour for "elementwise op elementwise" + self.assertEqual(v2.elementwise() + v1.elementwise(), v2 + v1) + self.assertEqual(v2.elementwise() - v1.elementwise(), v2 - v1) + self.assertEqual( + v2.elementwise() * v1.elementwise(), (v2.x * v1.x, v2.y * v1.y) + ) + self.assertEqual( + v2.elementwise() / v1.elementwise(), (v2.x / v1.x, v2.y / v1.y) + ) + self.assertEqual( + v2.elementwise() // v1.elementwise(), (v2.x // v1.x, v2.y // v1.y) + ) + self.assertEqual( + v2.elementwise() ** v1.elementwise(), (v2.x**v1.x, v2.y**v1.y) + ) + self.assertEqual( + v2.elementwise() % v1.elementwise(), (v2.x % v1.x, v2.y % v1.y) + ) + self.assertEqual( + v2.elementwise() < v1.elementwise(), v2.x < v1.x and v2.y < v1.y + ) + self.assertEqual( + v2.elementwise() > v1.elementwise(), v2.x > v1.x and v2.y > v1.y + ) + self.assertEqual( + v2.elementwise() <= v1.elementwise(), v2.x <= v1.x and v2.y <= v1.y + ) + self.assertEqual( + v2.elementwise() >= v1.elementwise(), v2.x >= v1.x and v2.y >= v1.y + ) + self.assertEqual( + v2.elementwise() == v1.elementwise(), v2.x == v1.x and v2.y == v1.y + ) + self.assertEqual( + v2.elementwise() != v1.elementwise(), v2.x != v1.x and v2.y != v1.y + ) + + # other behaviour + self.assertEqual(abs(v1.elementwise()), (abs(v1.x), abs(v1.y))) + self.assertEqual(-v1.elementwise(), -v1) + self.assertEqual(+v1.elementwise(), +v1) + self.assertEqual(bool(v1.elementwise()), bool(v1)) + self.assertEqual(bool(Vector2().elementwise()), bool(Vector2())) + self.assertEqual(self.zeroVec.elementwise() ** 0, (1, 1)) + self.assertRaises(ValueError, lambda: pow(Vector2(-1, 0).elementwise(), 1.2)) + self.assertRaises(ZeroDivisionError, lambda: self.zeroVec.elementwise() ** -1) + self.assertRaises(ZeroDivisionError, lambda: self.zeroVec.elementwise() ** -1) + self.assertRaises(ZeroDivisionError, lambda: Vector2(1, 1).elementwise() / 0) + self.assertRaises(ZeroDivisionError, lambda: Vector2(1, 1).elementwise() // 0) + self.assertRaises(ZeroDivisionError, lambda: Vector2(1, 1).elementwise() % 0) + self.assertRaises( + ZeroDivisionError, lambda: Vector2(1, 1).elementwise() / self.zeroVec + ) + self.assertRaises( + ZeroDivisionError, lambda: Vector2(1, 1).elementwise() // self.zeroVec + ) + self.assertRaises( + ZeroDivisionError, lambda: Vector2(1, 1).elementwise() % self.zeroVec + ) + self.assertRaises(ZeroDivisionError, lambda: 2 / self.zeroVec.elementwise()) + self.assertRaises(ZeroDivisionError, lambda: 2 // self.zeroVec.elementwise()) + self.assertRaises(ZeroDivisionError, lambda: 2 % self.zeroVec.elementwise()) + + def test_slerp(self): + self.assertRaises(ValueError, lambda: self.zeroVec.slerp(self.v1, 0.5)) + self.assertRaises(ValueError, lambda: self.v1.slerp(self.zeroVec, 0.5)) + self.assertRaises(ValueError, lambda: self.zeroVec.slerp(self.zeroVec, 0.5)) + v1 = Vector2(1, 0) + v2 = Vector2(0, 1) + steps = 10 + angle_step = v1.angle_to(v2) / steps + for i, u in ((i, v1.slerp(v2, i / float(steps))) for i in range(steps + 1)): + self.assertAlmostEqual(u.length(), 1) + self.assertAlmostEqual(v1.angle_to(u), i * angle_step) + self.assertEqual(u, v2) + + v1 = Vector2(100, 0) + v2 = Vector2(0, 10) + radial_factor = v2.length() / v1.length() + for i, u in ((i, v1.slerp(v2, -i / float(steps))) for i in range(steps + 1)): + self.assertAlmostEqual( + u.length(), + (v2.length() - v1.length()) * (float(i) / steps) + v1.length(), + ) + self.assertEqual(u, v2) + self.assertEqual(v1.slerp(v1, 0.5), v1) + self.assertEqual(v2.slerp(v2, 0.5), v2) + self.assertRaises(ValueError, lambda: v1.slerp(-v1, 0.5)) + + def test_lerp(self): + v1 = Vector2(0, 0) + v2 = Vector2(10, 10) + self.assertEqual(v1.lerp(v2, 0.5), (5, 5)) + self.assertRaises(ValueError, lambda: v1.lerp(v2, 2.5)) + + v1 = Vector2(-10, -5) + v2 = Vector2(10, 10) + self.assertEqual(v1.lerp(v2, 0.5), (0, 2.5)) + + def test_polar(self): + v = Vector2() + v.from_polar(self.v1.as_polar()) + self.assertEqual(self.v1, v) + self.assertEqual(self.v1, Vector2.from_polar(self.v1.as_polar())) + self.assertEqual(self.e1.as_polar(), (1, 0)) + self.assertEqual(self.e2.as_polar(), (1, 90)) + self.assertEqual((2 * self.e2).as_polar(), (2, 90)) + self.assertRaises(TypeError, lambda: v.from_polar((None, None))) + self.assertRaises(TypeError, lambda: v.from_polar("ab")) + self.assertRaises(TypeError, lambda: v.from_polar((None, 1))) + self.assertRaises(TypeError, lambda: v.from_polar((1, 2, 3))) + self.assertRaises(TypeError, lambda: v.from_polar((1,))) + self.assertRaises(TypeError, lambda: v.from_polar(1, 2)) + self.assertRaises(TypeError, lambda: Vector2.from_polar((None, None))) + self.assertRaises(TypeError, lambda: Vector2.from_polar("ab")) + self.assertRaises(TypeError, lambda: Vector2.from_polar((None, 1))) + self.assertRaises(TypeError, lambda: Vector2.from_polar((1, 2, 3))) + self.assertRaises(TypeError, lambda: Vector2.from_polar((1,))) + self.assertRaises(TypeError, lambda: Vector2.from_polar(1, 2)) + v.from_polar((0.5, 90)) + self.assertEqual(v, 0.5 * self.e2) + self.assertEqual(Vector2.from_polar((0.5, 90)), 0.5 * self.e2) + self.assertEqual(Vector2.from_polar((0.5, 90)), v) + v.from_polar((1, 0)) + self.assertEqual(v, self.e1) + self.assertEqual(Vector2.from_polar((1, 0)), self.e1) + self.assertEqual(Vector2.from_polar((1, 0)), v) + + def test_subclass_operation(self): + class Vector(pygame.math.Vector2): + pass + + vec = Vector() + + vec_a = Vector(2, 0) + vec_b = Vector(0, 1) + + vec_a + vec_b + vec_a *= 2 + + def test_project_v2_onto_x_axis(self): + """Project onto x-axis, e.g. get the component pointing in the x-axis direction.""" + # arrange + v = Vector2(2, 2) + x_axis = Vector2(10, 0) + + # act + actual = v.project(x_axis) + + # assert + self.assertEqual(v.x, actual.x) + self.assertEqual(0, actual.y) + + def test_project_v2_onto_y_axis(self): + """Project onto y-axis, e.g. get the component pointing in the y-axis direction.""" + # arrange + v = Vector2(2, 2) + y_axis = Vector2(0, 100) + + # act + actual = v.project(y_axis) + + # assert + self.assertEqual(0, actual.x) + self.assertEqual(v.y, actual.y) + + def test_project_v2_onto_other(self): + """Project onto other vector.""" + # arrange + v = Vector2(2, 3) + other = Vector2(3, 5) + + # act + actual = v.project(other) + + # assert + expected = v.dot(other) / other.dot(other) * other + self.assertEqual(expected.x, actual.x) + self.assertEqual(expected.y, actual.y) + + def test_project_v2_onto_other_as_tuple(self): + """Project onto other tuple as vector.""" + # arrange + v = Vector2(2, 3) + other = Vector2(3, 5) + + # act + actual = v.project(tuple(other)) + + # assert + expected = v.dot(other) / other.dot(other) * other + self.assertEqual(expected.x, actual.x) + self.assertEqual(expected.y, actual.y) + + def test_project_v2_onto_other_as_list(self): + """Project onto other list as vector.""" + # arrange + v = Vector2(2, 3) + other = Vector2(3, 5) + + # act + actual = v.project(list(other)) + + # assert + expected = v.dot(other) / other.dot(other) * other + self.assertEqual(expected.x, actual.x) + self.assertEqual(expected.y, actual.y) + + def test_project_v2_raises_if_other_has_zero_length(self): + """Check if exception is raise when projected on vector has zero length.""" + # arrange + v = Vector2(2, 3) + other = Vector2(0, 0) + + # act / assert + self.assertRaises(ValueError, v.project, other) + + def test_project_v2_raises_if_other_is_not_iterable(self): + """Check if exception is raise when projected on vector is not iterable.""" + # arrange + v = Vector2(2, 3) + other = 10 + + # act / assert + self.assertRaises(TypeError, v.project, other) + + def test_collection_abc(self): + v = Vector2(3, 4) + self.assertTrue(isinstance(v, Collection)) + self.assertFalse(isinstance(v, Sequence)) + + def test_clamp_mag_v2_max(self): + v1 = Vector2(7, 2) + v2 = v1.clamp_magnitude(5) + v3 = v1.clamp_magnitude(0, 5) + self.assertEqual(v2, v3) + + v1.clamp_magnitude_ip(5) + self.assertEqual(v1, v2) + + v1.clamp_magnitude_ip(0, 5) + self.assertEqual(v1, v2) + + expected_v2 = Vector2(4.807619738204116, 1.3736056394868903) + self.assertEqual(expected_v2, v2) + + def test_clamp_mag_v2_min(self): + v1 = Vector2(1, 2) + v2 = v1.clamp_magnitude(3, 5) + v1.clamp_magnitude_ip(3, 5) + expected_v2 = Vector2(1.3416407864998738, 2.6832815729997477) + self.assertEqual(expected_v2, v2) + self.assertEqual(expected_v2, v1) + + def test_clamp_mag_v2_no_change(self): + v1 = Vector2(1, 2) + for args in ( + (1, 6), + (1.12, 3.55), + (0.93, 2.83), + (7.6,), + ): + with self.subTest(args=args): + v2 = v1.clamp_magnitude(*args) + v1.clamp_magnitude_ip(*args) + self.assertEqual(v1, v2) + self.assertEqual(v1, Vector2(1, 2)) + + def test_clamp_mag_v2_edge_cases(self): + v1 = Vector2(1, 2) + v2 = v1.clamp_magnitude(6, 6) + v1.clamp_magnitude_ip(6, 6) + self.assertEqual(v1, v2) + self.assertAlmostEqual(v1.length(), 6) + + v2 = v1.clamp_magnitude(0) + v1.clamp_magnitude_ip(0, 0) + self.assertEqual(v1, v2) + self.assertEqual(v1, Vector2()) + + def test_clamp_mag_v2_errors(self): + v1 = Vector2(1, 2) + for invalid_args in ( + ("foo", "bar"), + (1, 2, 3), + (342.234, "test"), + ): + with self.subTest(invalid_args=invalid_args): + self.assertRaises(TypeError, v1.clamp_magnitude, *invalid_args) + self.assertRaises(TypeError, v1.clamp_magnitude_ip, *invalid_args) + + for invalid_args in ( + (-1,), + (4, 3), # min > max + (-4, 10), + (-4, -2), + ): + with self.subTest(invalid_args=invalid_args): + self.assertRaises(ValueError, v1.clamp_magnitude, *invalid_args) + self.assertRaises(ValueError, v1.clamp_magnitude_ip, *invalid_args) + + # 0 vector + v2 = Vector2() + self.assertRaises(ValueError, v2.clamp_magnitude, 3) + self.assertRaises(ValueError, v2.clamp_magnitude_ip, 4) + + def test_subclassing_v2(self): + """Check if Vector2 is subclassable""" + v = Vector2(4, 2) + + class TestVector(Vector2): + def supermariobrosiscool(self): + return 722 + + other = TestVector(4, 1) + + self.assertEqual(other.supermariobrosiscool(), 722) + self.assertNotEqual(type(v), TestVector) + self.assertNotEqual(type(v), type(other.copy())) + self.assertEqual(TestVector, type(other.reflect(v))) + self.assertEqual(TestVector, type(other.lerp(v, 1))) + self.assertEqual(TestVector, type(other.slerp(v, 1))) + self.assertEqual(TestVector, type(other.rotate(5))) + self.assertEqual(TestVector, type(other.rotate_rad(5))) + self.assertEqual(TestVector, type(other.project(v))) + self.assertEqual(TestVector, type(other.move_towards(v, 5))) + self.assertEqual(TestVector, type(other.clamp_magnitude(5))) + self.assertEqual(TestVector, type(other.clamp_magnitude(1, 5))) + self.assertEqual(TestVector, type(other.elementwise() + other)) + + other1 = TestVector(4, 2) + + self.assertEqual(type(other + other1), TestVector) + self.assertEqual(type(other - other1), TestVector) + self.assertEqual(type(other * 3), TestVector) + self.assertEqual(type(other / 3), TestVector) + self.assertEqual(type(other.elementwise() ** 3), TestVector) + + +class Vector3TypeTest(unittest.TestCase): + def setUp(self): + self.zeroVec = Vector3() + self.e1 = Vector3(1, 0, 0) + self.e2 = Vector3(0, 1, 0) + self.e3 = Vector3(0, 0, 1) + self.t1 = (1.2, 3.4, 9.6) + self.l1 = list(self.t1) + self.v1 = Vector3(self.t1) + self.t2 = (5.6, 7.8, 2.1) + self.l2 = list(self.t2) + self.v2 = Vector3(self.t2) + self.s1 = 5.6 + self.s2 = 7.8 + + def testConstructionDefault(self): + v = Vector3() + self.assertEqual(v.x, 0.0) + self.assertEqual(v.y, 0.0) + self.assertEqual(v.z, 0.0) + + def testConstructionXYZ(self): + v = Vector3(1.2, 3.4, 9.6) + self.assertEqual(v.x, 1.2) + self.assertEqual(v.y, 3.4) + self.assertEqual(v.z, 9.6) + + def testConstructionTuple(self): + v = Vector3((1.2, 3.4, 9.6)) + self.assertEqual(v.x, 1.2) + self.assertEqual(v.y, 3.4) + self.assertEqual(v.z, 9.6) + + def testConstructionList(self): + v = Vector3([1.2, 3.4, -9.6]) + self.assertEqual(v.x, 1.2) + self.assertEqual(v.y, 3.4) + self.assertEqual(v.z, -9.6) + + def testConstructionVector3(self): + v = Vector3(Vector3(1.2, 3.4, -9.6)) + self.assertEqual(v.x, 1.2) + self.assertEqual(v.y, 3.4) + self.assertEqual(v.z, -9.6) + + def testConstructionScalar(self): + v = Vector3(1) + self.assertEqual(v.x, 1.0) + self.assertEqual(v.y, 1.0) + self.assertEqual(v.z, 1.0) + + def testConstructionScalarKeywords(self): + v = Vector3(x=1) + self.assertEqual(v.x, 1.0) + self.assertEqual(v.y, 1.0) + self.assertEqual(v.z, 1.0) + + def testConstructionKeywords(self): + v = Vector3(x=1, y=2, z=3) + self.assertEqual(v.x, 1.0) + self.assertEqual(v.y, 2.0) + self.assertEqual(v.z, 3.0) + + def testConstructionMissing(self): + self.assertRaises(ValueError, Vector3, 1, 2) + self.assertRaises(ValueError, Vector3, x=1, y=2) + + def testAttributeAccess(self): + tmp = self.v1.x + self.assertEqual(tmp, self.v1.x) + self.assertEqual(tmp, self.v1[0]) + tmp = self.v1.y + self.assertEqual(tmp, self.v1.y) + self.assertEqual(tmp, self.v1[1]) + tmp = self.v1.z + self.assertEqual(tmp, self.v1.z) + self.assertEqual(tmp, self.v1[2]) + self.v1.x = 3.141 + self.assertEqual(self.v1.x, 3.141) + self.v1.y = 3.141 + self.assertEqual(self.v1.y, 3.141) + self.v1.z = 3.141 + self.assertEqual(self.v1.z, 3.141) + + def assign_nonfloat(): + v = Vector2() + v.x = "spam" + + self.assertRaises(TypeError, assign_nonfloat) + + def testCopy(self): + v_copy0 = Vector3(2014.0, 2032.0, 2076.0) + v_copy1 = v_copy0.copy() + self.assertEqual(v_copy0.x, v_copy1.x) + self.assertEqual(v_copy0.y, v_copy1.y) + self.assertEqual(v_copy0.z, v_copy1.z) + + def testSequence(self): + v = Vector3(1.2, 3.4, -9.6) + self.assertEqual(len(v), 3) + self.assertEqual(v[0], 1.2) + self.assertEqual(v[1], 3.4) + self.assertEqual(v[2], -9.6) + self.assertRaises(IndexError, lambda: v[3]) + self.assertEqual(v[-1], -9.6) + self.assertEqual(v[-2], 3.4) + self.assertEqual(v[-3], 1.2) + self.assertRaises(IndexError, lambda: v[-4]) + self.assertEqual(v[:], [1.2, 3.4, -9.6]) + self.assertEqual(v[1:], [3.4, -9.6]) + self.assertEqual(v[:1], [1.2]) + self.assertEqual(v[:-1], [1.2, 3.4]) + self.assertEqual(v[1:2], [3.4]) + self.assertEqual(list(v), [1.2, 3.4, -9.6]) + self.assertEqual(tuple(v), (1.2, 3.4, -9.6)) + v[0] = 5.6 + v[1] = 7.8 + v[2] = -2.1 + self.assertEqual(v.x, 5.6) + self.assertEqual(v.y, 7.8) + self.assertEqual(v.z, -2.1) + v[:] = [9.1, 11.12, -13.41] + self.assertEqual(v.x, 9.1) + self.assertEqual(v.y, 11.12) + self.assertEqual(v.z, -13.41) + + def overpopulate(): + v = Vector3() + v[:] = [1, 2, 3, 4] + + self.assertRaises(ValueError, overpopulate) + + def underpopulate(): + v = Vector3() + v[:] = [1] + + self.assertRaises(ValueError, underpopulate) + + def assign_nonfloat(): + v = Vector2() + v[0] = "spam" + + self.assertRaises(TypeError, assign_nonfloat) + + def testExtendedSlicing(self): + # deletion + def delSlice(vec, start=None, stop=None, step=None): + if start is not None and stop is not None and step is not None: + del vec[start:stop:step] + elif start is not None and stop is None and step is not None: + del vec[start::step] + elif start is None and stop is None and step is not None: + del vec[::step] + + v = Vector3(self.v1) + self.assertRaises(TypeError, delSlice, v, None, None, 2) + self.assertRaises(TypeError, delSlice, v, 1, None, 2) + self.assertRaises(TypeError, delSlice, v, 1, 2, 1) + + # assignment + v = Vector3(self.v1) + v[::2] = [-1.1, -2.2] + self.assertEqual(v, [-1.1, self.v1.y, -2.2]) + v = Vector3(self.v1) + v[::-2] = [10, 20] + self.assertEqual(v, [20, self.v1.y, 10]) + v = Vector3(self.v1) + v[::-1] = v + self.assertEqual(v, [self.v1.z, self.v1.y, self.v1.x]) + a = Vector3(self.v1) + b = Vector3(self.v1) + c = Vector3(self.v1) + a[1:2] = [2.2] + b[slice(1, 2)] = [2.2] + c[1:2:] = (2.2,) + self.assertEqual(a, b) + self.assertEqual(a, c) + self.assertEqual(type(a), type(self.v1)) + self.assertEqual(type(b), type(self.v1)) + self.assertEqual(type(c), type(self.v1)) + + def test_contains(self): + c = Vector3(0, 1, 2) + + # call __contains__ explicitly to test that it is defined + self.assertTrue(c.__contains__(0)) + self.assertTrue(0 in c) + self.assertTrue(1 in c) + self.assertTrue(2 in c) + self.assertTrue(3 not in c) + self.assertFalse(c.__contains__(10)) + + self.assertRaises(TypeError, lambda: "string" in c) + self.assertRaises(TypeError, lambda: 3 + 4j in c) + + def testAdd(self): + v3 = self.v1 + self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x + self.v2.x) + self.assertEqual(v3.y, self.v1.y + self.v2.y) + self.assertEqual(v3.z, self.v1.z + self.v2.z) + v3 = self.v1 + self.t2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x + self.t2[0]) + self.assertEqual(v3.y, self.v1.y + self.t2[1]) + self.assertEqual(v3.z, self.v1.z + self.t2[2]) + v3 = self.v1 + self.l2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x + self.l2[0]) + self.assertEqual(v3.y, self.v1.y + self.l2[1]) + self.assertEqual(v3.z, self.v1.z + self.l2[2]) + v3 = self.t1 + self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.t1[0] + self.v2.x) + self.assertEqual(v3.y, self.t1[1] + self.v2.y) + self.assertEqual(v3.z, self.t1[2] + self.v2.z) + v3 = self.l1 + self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.l1[0] + self.v2.x) + self.assertEqual(v3.y, self.l1[1] + self.v2.y) + self.assertEqual(v3.z, self.l1[2] + self.v2.z) + + def testSub(self): + v3 = self.v1 - self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x - self.v2.x) + self.assertEqual(v3.y, self.v1.y - self.v2.y) + self.assertEqual(v3.z, self.v1.z - self.v2.z) + v3 = self.v1 - self.t2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x - self.t2[0]) + self.assertEqual(v3.y, self.v1.y - self.t2[1]) + self.assertEqual(v3.z, self.v1.z - self.t2[2]) + v3 = self.v1 - self.l2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.v1.x - self.l2[0]) + self.assertEqual(v3.y, self.v1.y - self.l2[1]) + self.assertEqual(v3.z, self.v1.z - self.l2[2]) + v3 = self.t1 - self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.t1[0] - self.v2.x) + self.assertEqual(v3.y, self.t1[1] - self.v2.y) + self.assertEqual(v3.z, self.t1[2] - self.v2.z) + v3 = self.l1 - self.v2 + self.assertTrue(isinstance(v3, type(self.v1))) + self.assertEqual(v3.x, self.l1[0] - self.v2.x) + self.assertEqual(v3.y, self.l1[1] - self.v2.y) + self.assertEqual(v3.z, self.l1[2] - self.v2.z) + + def testScalarMultiplication(self): + v = self.s1 * self.v1 + self.assertTrue(isinstance(v, type(self.v1))) + self.assertEqual(v.x, self.s1 * self.v1.x) + self.assertEqual(v.y, self.s1 * self.v1.y) + self.assertEqual(v.z, self.s1 * self.v1.z) + v = self.v1 * self.s2 + self.assertEqual(v.x, self.v1.x * self.s2) + self.assertEqual(v.y, self.v1.y * self.s2) + self.assertEqual(v.z, self.v1.z * self.s2) + + def testScalarDivision(self): + v = self.v1 / self.s1 + self.assertTrue(isinstance(v, type(self.v1))) + self.assertAlmostEqual(v.x, self.v1.x / self.s1) + self.assertAlmostEqual(v.y, self.v1.y / self.s1) + self.assertAlmostEqual(v.z, self.v1.z / self.s1) + v = self.v1 // self.s2 + self.assertTrue(isinstance(v, type(self.v1))) + self.assertEqual(v.x, self.v1.x // self.s2) + self.assertEqual(v.y, self.v1.y // self.s2) + self.assertEqual(v.z, self.v1.z // self.s2) + + def testBool(self): + self.assertEqual(bool(self.zeroVec), False) + self.assertEqual(bool(self.v1), True) + self.assertTrue(not self.zeroVec) + self.assertTrue(self.v1) + + def testUnary(self): + v = +self.v1 + self.assertTrue(isinstance(v, type(self.v1))) + self.assertEqual(v.x, self.v1.x) + self.assertEqual(v.y, self.v1.y) + self.assertEqual(v.z, self.v1.z) + self.assertNotEqual(id(v), id(self.v1)) + v = -self.v1 + self.assertTrue(isinstance(v, type(self.v1))) + self.assertEqual(v.x, -self.v1.x) + self.assertEqual(v.y, -self.v1.y) + self.assertEqual(v.z, -self.v1.z) + self.assertNotEqual(id(v), id(self.v1)) + + def testCompare(self): + int_vec = Vector3(3, -2, 13) + flt_vec = Vector3(3.0, -2.0, 13.0) + zero_vec = Vector3(0, 0, 0) + self.assertEqual(int_vec == flt_vec, True) + self.assertEqual(int_vec != flt_vec, False) + self.assertEqual(int_vec != zero_vec, True) + self.assertEqual(flt_vec == zero_vec, False) + self.assertEqual(int_vec == (3, -2, 13), True) + self.assertEqual(int_vec != (3, -2, 13), False) + self.assertEqual(int_vec != [0, 0], True) + self.assertEqual(int_vec == [0, 0], False) + self.assertEqual(int_vec != 5, True) + self.assertEqual(int_vec == 5, False) + self.assertEqual(int_vec != [3, -2, 0, 1], True) + self.assertEqual(int_vec == [3, -2, 0, 1], False) + + def testStr(self): + v = Vector3(1.2, 3.4, 5.6) + self.assertEqual(str(v), "[1.2, 3.4, 5.6]") + + def testRepr(self): + v = Vector3(1.2, 3.4, -9.6) + self.assertEqual(v.__repr__(), "") + self.assertEqual(v, Vector3(v.__repr__())) + + def testIter(self): + it = self.v1.__iter__() + next_ = it.__next__ + self.assertEqual(next_(), self.v1[0]) + self.assertEqual(next_(), self.v1[1]) + self.assertEqual(next_(), self.v1[2]) + self.assertRaises(StopIteration, lambda: next_()) + it1 = self.v1.__iter__() + it2 = self.v1.__iter__() + self.assertNotEqual(id(it1), id(it2)) + self.assertEqual(id(it1), id(it1.__iter__())) + self.assertEqual(list(it1), list(it2)) + self.assertEqual(list(self.v1.__iter__()), self.l1) + idx = 0 + for val in self.v1: + self.assertEqual(val, self.v1[idx]) + idx += 1 + + def test___round___basic(self): + self.assertEqual( + round(pygame.Vector3(0.0, 0.0, 0.0)), pygame.Vector3(0.0, 0.0, 0.0) + ) + self.assertEqual(type(round(pygame.Vector3(0.0, 0.0, 0.0))), pygame.Vector3) + self.assertEqual( + round(pygame.Vector3(1.0, 1.0, 1.0)), round(pygame.Vector3(1.0, 1.0, 1.0)) + ) + self.assertEqual( + round(pygame.Vector3(10.0, 10.0, 10.0)), + round(pygame.Vector3(10.0, 10.0, 10.0)), + ) + self.assertEqual( + round(pygame.Vector3(1000000000.0, 1000000000.0, 1000000000.0)), + pygame.Vector3(1000000000.0, 1000000000.0, 1000000000.0), + ) + self.assertEqual( + round(pygame.Vector3(1e20, 1e20, 1e20)), pygame.Vector3(1e20, 1e20, 1e20) + ) + + self.assertEqual( + round(pygame.Vector3(-1.0, -1.0, -1.0)), pygame.Vector3(-1.0, -1.0, -1.0) + ) + self.assertEqual( + round(pygame.Vector3(-10.0, -10.0, -10.0)), + pygame.Vector3(-10.0, -10.0, -10.0), + ) + self.assertEqual( + round(pygame.Vector3(-1000000000.0, -1000000000.0, -1000000000.0)), + pygame.Vector3(-1000000000.0, -1000000000.0, -1000000000.0), + ) + self.assertEqual( + round(pygame.Vector3(-1e20, -1e20, -1e20)), + pygame.Vector3(-1e20, -1e20, -1e20), + ) + + self.assertEqual( + round(pygame.Vector3(0.1, 0.1, 0.1)), pygame.Vector3(0.0, 0.0, 0.0) + ) + self.assertEqual( + round(pygame.Vector3(1.1, 1.1, 1.1)), pygame.Vector3(1.0, 1.0, 1.0) + ) + self.assertEqual( + round(pygame.Vector3(10.1, 10.1, 10.1)), pygame.Vector3(10.0, 10.0, 10.0) + ) + self.assertEqual( + round(pygame.Vector3(1000000000.1, 1000000000.1, 1000000000.1)), + pygame.Vector3(1000000000.0, 1000000000.0, 1000000000.0), + ) + + self.assertEqual( + round(pygame.Vector3(-1.1, -1.1, -1.1)), pygame.Vector3(-1.0, -1.0, -1.0) + ) + self.assertEqual( + round(pygame.Vector3(-10.1, -10.1, -10.1)), + pygame.Vector3(-10.0, -10.0, -10.0), + ) + self.assertEqual( + round(pygame.Vector3(-1000000000.1, -1000000000.1, -1000000000.1)), + pygame.Vector3(-1000000000.0, -1000000000.0, -1000000000.0), + ) + + self.assertEqual( + round(pygame.Vector3(0.9, 0.9, 0.9)), pygame.Vector3(1.0, 1.0, 1.0) + ) + self.assertEqual( + round(pygame.Vector3(9.9, 9.9, 9.9)), pygame.Vector3(10.0, 10.0, 10.0) + ) + self.assertEqual( + round(pygame.Vector3(999999999.9, 999999999.9, 999999999.9)), + pygame.Vector3(1000000000.0, 1000000000.0, 1000000000.0), + ) + + self.assertEqual( + round(pygame.Vector3(-0.9, -0.9, -0.9)), pygame.Vector3(-1.0, -1.0, -1.0) + ) + self.assertEqual( + round(pygame.Vector3(-9.9, -9.9, -9.9)), pygame.Vector3(-10.0, -10.0, -10.0) + ) + self.assertEqual( + round(pygame.Vector3(-999999999.9, -999999999.9, -999999999.9)), + pygame.Vector3(-1000000000.0, -1000000000.0, -1000000000.0), + ) + + self.assertEqual( + round(pygame.Vector3(-8.0, -8.0, -8.0), -1), + pygame.Vector3(-10.0, -10.0, -10.0), + ) + self.assertEqual( + type(round(pygame.Vector3(-8.0, -8.0, -8.0), -1)), pygame.Vector3 + ) + + self.assertEqual( + type(round(pygame.Vector3(-8.0, -8.0, -8.0), 0)), pygame.Vector3 + ) + self.assertEqual( + type(round(pygame.Vector3(-8.0, -8.0, -8.0), 1)), pygame.Vector3 + ) + + # Check even / odd rounding behaviour + self.assertEqual(round(pygame.Vector3(5.5, 5.5, 5.5)), pygame.Vector3(6, 6, 6)) + self.assertEqual( + round(pygame.Vector3(5.4, 5.4, 5.4)), pygame.Vector3(5.0, 5.0, 5.0) + ) + self.assertEqual( + round(pygame.Vector3(5.6, 5.6, 5.6)), pygame.Vector3(6.0, 6.0, 6.0) + ) + self.assertEqual( + round(pygame.Vector3(-5.5, -5.5, -5.5)), pygame.Vector3(-6, -6, -6) + ) + self.assertEqual( + round(pygame.Vector3(-5.4, -5.4, -5.4)), pygame.Vector3(-5, -5, -5) + ) + self.assertEqual( + round(pygame.Vector3(-5.6, -5.6, -5.6)), pygame.Vector3(-6, -6, -6) + ) + + self.assertRaises(TypeError, round, pygame.Vector3(1.0, 1.0, 1.0), 1.5) + self.assertRaises(TypeError, round, pygame.Vector3(1.0, 1.0, 1.0), "a") + + def test_rotate(self): + v1 = Vector3(1, 0, 0) + axis = Vector3(0, 1, 0) + v2 = v1.rotate(90, axis) + v3 = v1.rotate(90 + 360, axis) + self.assertEqual(v1.x, 1) + self.assertEqual(v1.y, 0) + self.assertEqual(v1.z, 0) + self.assertEqual(v2.x, 0) + self.assertEqual(v2.y, 0) + self.assertEqual(v2.z, -1) + self.assertEqual(v3.x, v2.x) + self.assertEqual(v3.y, v2.y) + self.assertEqual(v3.z, v2.z) + v1 = Vector3(-1, -1, -1) + v2 = v1.rotate(-90, axis) + self.assertEqual(v2.x, 1) + self.assertEqual(v2.y, -1) + self.assertEqual(v2.z, -1) + v2 = v1.rotate(360, axis) + self.assertEqual(v1.x, v2.x) + self.assertEqual(v1.y, v2.y) + self.assertEqual(v1.z, v2.z) + v2 = v1.rotate(0, axis) + self.assertEqual(v1.x, v2.x) + self.assertEqual(v1.y, v2.y) + self.assertEqual(v1.z, v2.z) + # issue 214 + self.assertEqual( + Vector3(0, 1, 0).rotate(359.9999999, Vector3(0, 0, 1)), Vector3(0, 1, 0) + ) + + def test_rotate_rad(self): + axis = Vector3(0, 0, 1) + tests = ( + ((1, 0, 0), math.pi), + ((1, 0, 0), math.pi / 2), + ((1, 0, 0), -math.pi / 2), + ((1, 0, 0), math.pi / 4), + ) + for initialVec, radians in tests: + vec = Vector3(initialVec).rotate_rad(radians, axis) + self.assertEqual(vec, (math.cos(radians), math.sin(radians), 0)) + + def test_rotate_ip(self): + v = Vector3(1, 0, 0) + axis = Vector3(0, 1, 0) + self.assertEqual(v.rotate_ip(90, axis), None) + self.assertEqual(v.x, 0) + self.assertEqual(v.y, 0) + self.assertEqual(v.z, -1) + v = Vector3(-1, -1, 1) + v.rotate_ip(-90, axis) + self.assertEqual(v.x, -1) + self.assertEqual(v.y, -1) + self.assertEqual(v.z, -1) + + def test_rotate_rad_ip(self): + axis = Vector3(0, 0, 1) + tests = ( + ((1, 0, 0), math.pi), + ((1, 0, 0), math.pi / 2), + ((1, 0, 0), -math.pi / 2), + ((1, 0, 0), math.pi / 4), + ) + for initialVec, radians in tests: + vec = Vector3(initialVec) + vec.rotate_rad_ip(radians, axis) + self.assertEqual(vec, (math.cos(radians), math.sin(radians), 0)) + + def test_rotate_x(self): + v1 = Vector3(1, 0, 0) + v2 = v1.rotate_x(90) + v3 = v1.rotate_x(90 + 360) + self.assertEqual(v1.x, 1) + self.assertEqual(v1.y, 0) + self.assertEqual(v1.z, 0) + self.assertEqual(v2.x, 1) + self.assertEqual(v2.y, 0) + self.assertEqual(v2.z, 0) + self.assertEqual(v3.x, v2.x) + self.assertEqual(v3.y, v2.y) + self.assertEqual(v3.z, v2.z) + v1 = Vector3(-1, -1, -1) + v2 = v1.rotate_x(-90) + self.assertEqual(v2.x, -1) + self.assertAlmostEqual(v2.y, -1) + self.assertAlmostEqual(v2.z, 1) + v2 = v1.rotate_x(360) + self.assertAlmostEqual(v1.x, v2.x) + self.assertAlmostEqual(v1.y, v2.y) + self.assertAlmostEqual(v1.z, v2.z) + v2 = v1.rotate_x(0) + self.assertEqual(v1.x, v2.x) + self.assertAlmostEqual(v1.y, v2.y) + self.assertAlmostEqual(v1.z, v2.z) + + def test_rotate_x_rad(self): + vec = Vector3(0, 1, 0) + result = vec.rotate_x_rad(math.pi / 2) + self.assertEqual(result, (0, 0, 1)) + + def test_rotate_x_ip(self): + v = Vector3(1, 0, 0) + self.assertEqual(v.rotate_x_ip(90), None) + self.assertEqual(v.x, 1) + self.assertEqual(v.y, 0) + self.assertEqual(v.z, 0) + v = Vector3(-1, -1, 1) + v.rotate_x_ip(-90) + self.assertEqual(v.x, -1) + self.assertAlmostEqual(v.y, 1) + self.assertAlmostEqual(v.z, 1) + + def test_rotate_x_rad_ip(self): + vec = Vector3(0, 1, 0) + vec.rotate_x_rad_ip(math.pi / 2) + self.assertEqual(vec, (0, 0, 1)) + + def test_rotate_y(self): + v1 = Vector3(1, 0, 0) + v2 = v1.rotate_y(90) + v3 = v1.rotate_y(90 + 360) + self.assertEqual(v1.x, 1) + self.assertEqual(v1.y, 0) + self.assertEqual(v1.z, 0) + self.assertAlmostEqual(v2.x, 0) + self.assertEqual(v2.y, 0) + self.assertAlmostEqual(v2.z, -1) + self.assertAlmostEqual(v3.x, v2.x) + self.assertEqual(v3.y, v2.y) + self.assertAlmostEqual(v3.z, v2.z) + v1 = Vector3(-1, -1, -1) + v2 = v1.rotate_y(-90) + self.assertAlmostEqual(v2.x, 1) + self.assertEqual(v2.y, -1) + self.assertAlmostEqual(v2.z, -1) + v2 = v1.rotate_y(360) + self.assertAlmostEqual(v1.x, v2.x) + self.assertEqual(v1.y, v2.y) + self.assertAlmostEqual(v1.z, v2.z) + v2 = v1.rotate_y(0) + self.assertEqual(v1.x, v2.x) + self.assertEqual(v1.y, v2.y) + self.assertEqual(v1.z, v2.z) + + def test_rotate_y_rad(self): + vec = Vector3(1, 0, 0) + result = vec.rotate_y_rad(math.pi / 2) + self.assertEqual(result, (0, 0, -1)) + + def test_rotate_y_ip(self): + v = Vector3(1, 0, 0) + self.assertEqual(v.rotate_y_ip(90), None) + self.assertAlmostEqual(v.x, 0) + self.assertEqual(v.y, 0) + self.assertAlmostEqual(v.z, -1) + v = Vector3(-1, -1, 1) + v.rotate_y_ip(-90) + self.assertAlmostEqual(v.x, -1) + self.assertEqual(v.y, -1) + self.assertAlmostEqual(v.z, -1) + + def test_rotate_y_rad_ip(self): + vec = Vector3(1, 0, 0) + vec.rotate_y_rad_ip(math.pi / 2) + self.assertEqual(vec, (0, 0, -1)) + + def test_rotate_z(self): + v1 = Vector3(1, 0, 0) + v2 = v1.rotate_z(90) + v3 = v1.rotate_z(90 + 360) + self.assertEqual(v1.x, 1) + self.assertEqual(v1.y, 0) + self.assertEqual(v1.z, 0) + self.assertAlmostEqual(v2.x, 0) + self.assertAlmostEqual(v2.y, 1) + self.assertEqual(v2.z, 0) + self.assertAlmostEqual(v3.x, v2.x) + self.assertAlmostEqual(v3.y, v2.y) + self.assertEqual(v3.z, v2.z) + v1 = Vector3(-1, -1, -1) + v2 = v1.rotate_z(-90) + self.assertAlmostEqual(v2.x, -1) + self.assertAlmostEqual(v2.y, 1) + self.assertEqual(v2.z, -1) + v2 = v1.rotate_z(360) + self.assertAlmostEqual(v1.x, v2.x) + self.assertAlmostEqual(v1.y, v2.y) + self.assertEqual(v1.z, v2.z) + v2 = v1.rotate_z(0) + self.assertAlmostEqual(v1.x, v2.x) + self.assertAlmostEqual(v1.y, v2.y) + self.assertEqual(v1.z, v2.z) + + def test_rotate_z_rad(self): + vec = Vector3(1, 0, 0) + result = vec.rotate_z_rad(math.pi / 2) + self.assertEqual(result, (0, 1, 0)) + + def test_rotate_z_ip(self): + v = Vector3(1, 0, 0) + self.assertEqual(v.rotate_z_ip(90), None) + self.assertAlmostEqual(v.x, 0) + self.assertAlmostEqual(v.y, 1) + self.assertEqual(v.z, 0) + v = Vector3(-1, -1, 1) + v.rotate_z_ip(-90) + self.assertAlmostEqual(v.x, -1) + self.assertAlmostEqual(v.y, 1) + self.assertEqual(v.z, 1) + + def test_rotate_z_rad_ip(self): + vec = Vector3(1, 0, 0) + vec.rotate_z_rad_ip(math.pi / 2) + self.assertEqual(vec, (0, 1, 0)) + + def test_normalize(self): + v = self.v1.normalize() + # length is 1 + self.assertAlmostEqual(v.x * v.x + v.y * v.y + v.z * v.z, 1.0) + # v1 is unchanged + self.assertEqual(self.v1.x, self.l1[0]) + self.assertEqual(self.v1.y, self.l1[1]) + self.assertEqual(self.v1.z, self.l1[2]) + # v2 is parallel to v1 (tested via cross product) + cross = ( + (self.v1.y * v.z - self.v1.z * v.y) ** 2 + + (self.v1.z * v.x - self.v1.x * v.z) ** 2 + + (self.v1.x * v.y - self.v1.y * v.x) ** 2 + ) + self.assertAlmostEqual(cross, 0.0) + self.assertRaises(ValueError, lambda: self.zeroVec.normalize()) + + def test_normalize_ip(self): + v = +self.v1 + # v has length != 1 before normalizing + self.assertNotEqual(v.x * v.x + v.y * v.y + v.z * v.z, 1.0) + # inplace operations should return None + self.assertEqual(v.normalize_ip(), None) + # length is 1 + self.assertAlmostEqual(v.x * v.x + v.y * v.y + v.z * v.z, 1.0) + # v2 is parallel to v1 (tested via cross product) + cross = ( + (self.v1.y * v.z - self.v1.z * v.y) ** 2 + + (self.v1.z * v.x - self.v1.x * v.z) ** 2 + + (self.v1.x * v.y - self.v1.y * v.x) ** 2 + ) + self.assertAlmostEqual(cross, 0.0) + self.assertRaises(ValueError, lambda: self.zeroVec.normalize_ip()) + + def test_is_normalized(self): + self.assertEqual(self.v1.is_normalized(), False) + v = self.v1.normalize() + self.assertEqual(v.is_normalized(), True) + self.assertEqual(self.e2.is_normalized(), True) + self.assertEqual(self.zeroVec.is_normalized(), False) + + def test_cross(self): + def cross(a, b): + return Vector3( + a[1] * b[2] - a[2] * b[1], + a[2] * b[0] - a[0] * b[2], + a[0] * b[1] - a[1] * b[0], + ) + + self.assertEqual(self.v1.cross(self.v2), cross(self.v1, self.v2)) + self.assertEqual(self.v1.cross(self.l2), cross(self.v1, self.l2)) + self.assertEqual(self.v1.cross(self.t2), cross(self.v1, self.t2)) + self.assertEqual(self.v1.cross(self.v2), -self.v2.cross(self.v1)) + self.assertEqual(self.v1.cross(self.v1), self.zeroVec) + + def test_dot(self): + self.assertAlmostEqual( + self.v1.dot(self.v2), + self.v1.x * self.v2.x + self.v1.y * self.v2.y + self.v1.z * self.v2.z, + ) + self.assertAlmostEqual( + self.v1.dot(self.l2), + self.v1.x * self.l2[0] + self.v1.y * self.l2[1] + self.v1.z * self.l2[2], + ) + self.assertAlmostEqual( + self.v1.dot(self.t2), + self.v1.x * self.t2[0] + self.v1.y * self.t2[1] + self.v1.z * self.t2[2], + ) + self.assertAlmostEqual(self.v1.dot(self.v2), self.v2.dot(self.v1)) + self.assertAlmostEqual(self.v1.dot(self.v2), self.v1 * self.v2) + + def test_angle_to(self): + self.assertEqual(Vector3(1, 1, 0).angle_to((-1, 1, 0)), 90) + self.assertEqual(Vector3(1, 0, 0).angle_to((0, 0, -1)), 90) + self.assertEqual(Vector3(1, 0, 0).angle_to((-1, 0, 1)), 135) + self.assertEqual(abs(Vector3(1, 0, 1).angle_to((-1, 0, -1))), 180) + # if we rotate v1 by the angle_to v2 around their cross product + # we should look in the same direction + self.assertEqual( + self.v1.rotate( + self.v1.angle_to(self.v2), self.v1.cross(self.v2) + ).normalize(), + self.v2.normalize(), + ) + + def test_scale_to_length(self): + v = Vector3(1, 1, 1) + v.scale_to_length(2.5) + self.assertEqual(v, Vector3(2.5, 2.5, 2.5) / math.sqrt(3)) + self.assertRaises(ValueError, lambda: self.zeroVec.scale_to_length(1)) + self.assertEqual(v.scale_to_length(0), None) + self.assertEqual(v, self.zeroVec) + + def test_length(self): + self.assertEqual(Vector3(3, 4, 5).length(), math.sqrt(3 * 3 + 4 * 4 + 5 * 5)) + self.assertEqual(Vector3(-3, 4, 5).length(), math.sqrt(-3 * -3 + 4 * 4 + 5 * 5)) + self.assertEqual(self.zeroVec.length(), 0) + + def test_length_squared(self): + self.assertEqual(Vector3(3, 4, 5).length_squared(), 3 * 3 + 4 * 4 + 5 * 5) + self.assertEqual(Vector3(-3, 4, 5).length_squared(), -3 * -3 + 4 * 4 + 5 * 5) + self.assertEqual(self.zeroVec.length_squared(), 0) + + def test_reflect(self): + v = Vector3(1, -1, 1) + n = Vector3(0, 1, 0) + self.assertEqual(v.reflect(n), Vector3(1, 1, 1)) + self.assertEqual(v.reflect(3 * n), v.reflect(n)) + self.assertEqual(v.reflect(-v), -v) + self.assertRaises(ValueError, lambda: v.reflect(self.zeroVec)) + + def test_reflect_ip(self): + v1 = Vector3(1, -1, 1) + v2 = Vector3(v1) + n = Vector3(0, 1, 0) + self.assertEqual(v2.reflect_ip(n), None) + self.assertEqual(v2, Vector3(1, 1, 1)) + v2 = Vector3(v1) + v2.reflect_ip(3 * n) + self.assertEqual(v2, v1.reflect(n)) + v2 = Vector3(v1) + v2.reflect_ip(-v1) + self.assertEqual(v2, -v1) + self.assertRaises(ValueError, lambda: v2.reflect_ip(self.zeroVec)) + + def test_distance_to(self): + diff = self.v1 - self.v2 + self.assertEqual(self.e1.distance_to(self.e2), math.sqrt(2)) + self.assertEqual(self.e1.distance_to((0, 1, 0)), math.sqrt(2)) + self.assertEqual(self.e1.distance_to([0, 1, 0]), math.sqrt(2)) + self.assertEqual( + self.v1.distance_to(self.v2), + math.sqrt(diff.x * diff.x + diff.y * diff.y + diff.z * diff.z), + ) + self.assertEqual( + self.v1.distance_to(self.t2), + math.sqrt(diff.x * diff.x + diff.y * diff.y + diff.z * diff.z), + ) + self.assertEqual( + self.v1.distance_to(self.l2), + math.sqrt(diff.x * diff.x + diff.y * diff.y + diff.z * diff.z), + ) + self.assertEqual(self.v1.distance_to(self.v1), 0) + self.assertEqual(self.v1.distance_to(self.t1), 0) + self.assertEqual(self.v1.distance_to(self.l1), 0) + self.assertEqual(self.v1.distance_to(self.v2), self.v2.distance_to(self.v1)) + self.assertEqual(self.v1.distance_to(self.t2), self.v2.distance_to(self.t1)) + self.assertEqual(self.v1.distance_to(self.l2), self.v2.distance_to(self.l1)) + + def test_distance_to_exceptions(self): + v2 = Vector2(10, 10) + v3 = Vector3(1, 1, 1) + + # illegal distance Vector3-Vector2 / Vector2-Vector3 + self.assertRaises(ValueError, v2.distance_to, v3) + self.assertRaises(ValueError, v3.distance_to, v2) + + # distance to illegal tuple/list positions + self.assertRaises(ValueError, v2.distance_to, (1, 1, 1)) + self.assertRaises(ValueError, v2.distance_to, (1, 1, 0)) + self.assertRaises(ValueError, v2.distance_to, (1,)) + self.assertRaises(ValueError, v2.distance_to, [1, 1, 1]) + self.assertRaises(ValueError, v2.distance_to, [1, 1, 0]) + self.assertRaises( + ValueError, + v2.distance_to, + [ + 1, + ], + ) + self.assertRaises(ValueError, v2.distance_to, (1, 1, 1)) + # vec3 + self.assertRaises(ValueError, v3.distance_to, (1, 1)) + self.assertRaises(ValueError, v3.distance_to, (1,)) + self.assertRaises(ValueError, v3.distance_to, [1, 1]) + self.assertRaises( + ValueError, + v3.distance_to, + [ + 1, + ], + ) + + # illegal types as positions + self.assertRaises(TypeError, v2.distance_to, (1, "hello")) + self.assertRaises(TypeError, v2.distance_to, ([], [])) + self.assertRaises(TypeError, v2.distance_to, (1, ("hello",))) + + # illegal args number + self.assertRaises(TypeError, v2.distance_to) + self.assertRaises(TypeError, v2.distance_to, (1, 1), (1, 2)) + self.assertRaises(TypeError, v2.distance_to, (1, 1), (1, 2), 1) + + def test_distance_squared_to_exceptions(self): + v2 = Vector2(10, 10) + v3 = Vector3(1, 1, 1) + dist_t = v2.distance_squared_to + dist_t3 = v3.distance_squared_to + # illegal distance Vector3-Vector2 / Vector2-Vector3 + self.assertRaises(ValueError, dist_t, v3) + self.assertRaises(ValueError, dist_t3, v2) + + # distance to illegal tuple/list positions + self.assertRaises(ValueError, dist_t, (1, 1, 1)) + self.assertRaises(ValueError, dist_t, (1, 1, 0)) + self.assertRaises(ValueError, dist_t, (1,)) + self.assertRaises(ValueError, dist_t, [1, 1, 1]) + self.assertRaises(ValueError, dist_t, [1, 1, 0]) + self.assertRaises( + ValueError, + dist_t, + [ + 1, + ], + ) + self.assertRaises(ValueError, dist_t, (1, 1, 1)) + # vec3 + self.assertRaises(ValueError, dist_t3, (1, 1)) + self.assertRaises(ValueError, dist_t3, (1,)) + self.assertRaises(ValueError, dist_t3, [1, 1]) + self.assertRaises( + ValueError, + dist_t3, + [ + 1, + ], + ) + + # illegal types as positions + self.assertRaises(TypeError, dist_t, (1, "hello")) + self.assertRaises(TypeError, dist_t, ([], [])) + self.assertRaises(TypeError, dist_t, (1, ("hello",))) + + # illegal args number + self.assertRaises(TypeError, dist_t) + self.assertRaises(TypeError, dist_t, (1, 1), (1, 2)) + self.assertRaises(TypeError, dist_t, (1, 1), (1, 2), 1) + + def test_distance_squared_to(self): + diff = self.v1 - self.v2 + self.assertEqual(self.e1.distance_squared_to(self.e2), 2) + self.assertEqual(self.e1.distance_squared_to((0, 1, 0)), 2) + self.assertEqual(self.e1.distance_squared_to([0, 1, 0]), 2) + self.assertAlmostEqual( + self.v1.distance_squared_to(self.v2), + diff.x * diff.x + diff.y * diff.y + diff.z * diff.z, + ) + self.assertAlmostEqual( + self.v1.distance_squared_to(self.t2), + diff.x * diff.x + diff.y * diff.y + diff.z * diff.z, + ) + self.assertAlmostEqual( + self.v1.distance_squared_to(self.l2), + diff.x * diff.x + diff.y * diff.y + diff.z * diff.z, + ) + self.assertEqual(self.v1.distance_squared_to(self.v1), 0) + self.assertEqual(self.v1.distance_squared_to(self.t1), 0) + self.assertEqual(self.v1.distance_squared_to(self.l1), 0) + self.assertEqual( + self.v1.distance_squared_to(self.v2), self.v2.distance_squared_to(self.v1) + ) + self.assertEqual( + self.v1.distance_squared_to(self.t2), self.v2.distance_squared_to(self.t1) + ) + self.assertEqual( + self.v1.distance_squared_to(self.l2), self.v2.distance_squared_to(self.l1) + ) + + def test_swizzle(self): + self.assertEqual(self.v1.yxz, (self.v1.y, self.v1.x, self.v1.z)) + self.assertEqual( + self.v1.xxyyzzxyz, + ( + self.v1.x, + self.v1.x, + self.v1.y, + self.v1.y, + self.v1.z, + self.v1.z, + self.v1.x, + self.v1.y, + self.v1.z, + ), + ) + self.v1.xyz = self.t2 + self.assertEqual(self.v1, self.t2) + self.v1.zxy = self.t2 + self.assertEqual(self.v1, (self.t2[1], self.t2[2], self.t2[0])) + self.v1.yz = self.t2[:2] + self.assertEqual(self.v1, (self.t2[1], self.t2[0], self.t2[1])) + self.assertEqual(type(self.v1), Vector3) + + @unittest.skipIf(IS_PYPY, "known pypy failure") + def test_invalid_swizzle(self): + def invalidSwizzleX(): + Vector3().xx = (1, 2) + + def invalidSwizzleY(): + Vector3().yy = (1, 2) + + def invalidSwizzleZ(): + Vector3().zz = (1, 2) + + def invalidSwizzleW(): + Vector3().ww = (1, 2) + + self.assertRaises(AttributeError, invalidSwizzleX) + self.assertRaises(AttributeError, invalidSwizzleY) + self.assertRaises(AttributeError, invalidSwizzleZ) + self.assertRaises(AttributeError, invalidSwizzleW) + + def invalidAssignment(): + Vector3().xy = 3 + + self.assertRaises(TypeError, invalidAssignment) + + def test_swizzle_return_types(self): + self.assertEqual(type(self.v1.x), float) + self.assertEqual(type(self.v1.xy), Vector2) + self.assertEqual(type(self.v1.xyz), Vector3) + # but we don't have vector4 or above... so tuple. + self.assertEqual(type(self.v1.xyxy), tuple) + self.assertEqual(type(self.v1.xyxyx), tuple) + + def test_dir_works(self): + # not every single one of the attributes... + attributes = {"lerp", "normalize", "normalize_ip", "reflect", "slerp", "x", "y"} + # check if this selection of attributes are all there. + self.assertTrue(attributes.issubset(set(dir(self.v1)))) + + def test_elementwise(self): + # behaviour for "elementwise op scalar" + self.assertEqual( + self.v1.elementwise() + self.s1, + (self.v1.x + self.s1, self.v1.y + self.s1, self.v1.z + self.s1), + ) + self.assertEqual( + self.v1.elementwise() - self.s1, + (self.v1.x - self.s1, self.v1.y - self.s1, self.v1.z - self.s1), + ) + self.assertEqual( + self.v1.elementwise() * self.s2, + (self.v1.x * self.s2, self.v1.y * self.s2, self.v1.z * self.s2), + ) + self.assertEqual( + self.v1.elementwise() / self.s2, + (self.v1.x / self.s2, self.v1.y / self.s2, self.v1.z / self.s2), + ) + self.assertEqual( + self.v1.elementwise() // self.s1, + (self.v1.x // self.s1, self.v1.y // self.s1, self.v1.z // self.s1), + ) + self.assertEqual( + self.v1.elementwise() ** self.s1, + (self.v1.x**self.s1, self.v1.y**self.s1, self.v1.z**self.s1), + ) + self.assertEqual( + self.v1.elementwise() % self.s1, + (self.v1.x % self.s1, self.v1.y % self.s1, self.v1.z % self.s1), + ) + self.assertEqual( + self.v1.elementwise() > self.s1, + self.v1.x > self.s1 and self.v1.y > self.s1 and self.v1.z > self.s1, + ) + self.assertEqual( + self.v1.elementwise() < self.s1, + self.v1.x < self.s1 and self.v1.y < self.s1 and self.v1.z < self.s1, + ) + self.assertEqual( + self.v1.elementwise() == self.s1, + self.v1.x == self.s1 and self.v1.y == self.s1 and self.v1.z == self.s1, + ) + self.assertEqual( + self.v1.elementwise() != self.s1, + self.v1.x != self.s1 and self.v1.y != self.s1 and self.v1.z != self.s1, + ) + self.assertEqual( + self.v1.elementwise() >= self.s1, + self.v1.x >= self.s1 and self.v1.y >= self.s1 and self.v1.z >= self.s1, + ) + self.assertEqual( + self.v1.elementwise() <= self.s1, + self.v1.x <= self.s1 and self.v1.y <= self.s1 and self.v1.z <= self.s1, + ) + # behaviour for "scalar op elementwise" + self.assertEqual(5 + self.v1.elementwise(), Vector3(5, 5, 5) + self.v1) + self.assertEqual(3.5 - self.v1.elementwise(), Vector3(3.5, 3.5, 3.5) - self.v1) + self.assertEqual(7.5 * self.v1.elementwise(), 7.5 * self.v1) + self.assertEqual( + -3.5 / self.v1.elementwise(), + (-3.5 / self.v1.x, -3.5 / self.v1.y, -3.5 / self.v1.z), + ) + self.assertEqual( + -3.5 // self.v1.elementwise(), + (-3.5 // self.v1.x, -3.5 // self.v1.y, -3.5 // self.v1.z), + ) + self.assertEqual( + -(3.5 ** self.v1.elementwise()), + (-(3.5**self.v1.x), -(3.5**self.v1.y), -(3.5**self.v1.z)), + ) + self.assertEqual( + 3 % self.v1.elementwise(), (3 % self.v1.x, 3 % self.v1.y, 3 % self.v1.z) + ) + self.assertEqual( + 2 < self.v1.elementwise(), 2 < self.v1.x and 2 < self.v1.y and 2 < self.v1.z + ) + self.assertEqual( + 2 > self.v1.elementwise(), 2 > self.v1.x and 2 > self.v1.y and 2 > self.v1.z + ) + self.assertEqual( + 1 == self.v1.elementwise(), + 1 == self.v1.x and 1 == self.v1.y and 1 == self.v1.z, + ) + self.assertEqual( + 1 != self.v1.elementwise(), + 1 != self.v1.x and 1 != self.v1.y and 1 != self.v1.z, + ) + self.assertEqual( + 2 <= self.v1.elementwise(), + 2 <= self.v1.x and 2 <= self.v1.y and 2 <= self.v1.z, + ) + self.assertEqual( + -7 >= self.v1.elementwise(), + -7 >= self.v1.x and -7 >= self.v1.y and -7 >= self.v1.z, + ) + self.assertEqual( + -7 != self.v1.elementwise(), + -7 != self.v1.x and -7 != self.v1.y and -7 != self.v1.z, + ) + + # behaviour for "elementwise op vector" + self.assertEqual(type(self.v1.elementwise() * self.v2), type(self.v1)) + self.assertEqual(self.v1.elementwise() + self.v2, self.v1 + self.v2) + self.assertEqual(self.v1.elementwise() + self.v2, self.v1 + self.v2) + self.assertEqual(self.v1.elementwise() - self.v2, self.v1 - self.v2) + self.assertEqual( + self.v1.elementwise() * self.v2, + (self.v1.x * self.v2.x, self.v1.y * self.v2.y, self.v1.z * self.v2.z), + ) + self.assertEqual( + self.v1.elementwise() / self.v2, + (self.v1.x / self.v2.x, self.v1.y / self.v2.y, self.v1.z / self.v2.z), + ) + self.assertEqual( + self.v1.elementwise() // self.v2, + (self.v1.x // self.v2.x, self.v1.y // self.v2.y, self.v1.z // self.v2.z), + ) + self.assertEqual( + self.v1.elementwise() ** self.v2, + (self.v1.x**self.v2.x, self.v1.y**self.v2.y, self.v1.z**self.v2.z), + ) + self.assertEqual( + self.v1.elementwise() % self.v2, + (self.v1.x % self.v2.x, self.v1.y % self.v2.y, self.v1.z % self.v2.z), + ) + self.assertEqual( + self.v1.elementwise() > self.v2, + self.v1.x > self.v2.x and self.v1.y > self.v2.y and self.v1.z > self.v2.z, + ) + self.assertEqual( + self.v1.elementwise() < self.v2, + self.v1.x < self.v2.x and self.v1.y < self.v2.y and self.v1.z < self.v2.z, + ) + self.assertEqual( + self.v1.elementwise() >= self.v2, + self.v1.x >= self.v2.x + and self.v1.y >= self.v2.y + and self.v1.z >= self.v2.z, + ) + self.assertEqual( + self.v1.elementwise() <= self.v2, + self.v1.x <= self.v2.x + and self.v1.y <= self.v2.y + and self.v1.z <= self.v2.z, + ) + self.assertEqual( + self.v1.elementwise() == self.v2, + self.v1.x == self.v2.x + and self.v1.y == self.v2.y + and self.v1.z == self.v2.z, + ) + self.assertEqual( + self.v1.elementwise() != self.v2, + self.v1.x != self.v2.x + and self.v1.y != self.v2.y + and self.v1.z != self.v2.z, + ) + # behaviour for "vector op elementwise" + self.assertEqual(self.v2 + self.v1.elementwise(), self.v2 + self.v1) + self.assertEqual(self.v2 - self.v1.elementwise(), self.v2 - self.v1) + self.assertEqual( + self.v2 * self.v1.elementwise(), + (self.v2.x * self.v1.x, self.v2.y * self.v1.y, self.v2.z * self.v1.z), + ) + self.assertEqual( + self.v2 / self.v1.elementwise(), + (self.v2.x / self.v1.x, self.v2.y / self.v1.y, self.v2.z / self.v1.z), + ) + self.assertEqual( + self.v2 // self.v1.elementwise(), + (self.v2.x // self.v1.x, self.v2.y // self.v1.y, self.v2.z // self.v1.z), + ) + self.assertEqual( + self.v2 ** self.v1.elementwise(), + (self.v2.x**self.v1.x, self.v2.y**self.v1.y, self.v2.z**self.v1.z), + ) + self.assertEqual( + self.v2 % self.v1.elementwise(), + (self.v2.x % self.v1.x, self.v2.y % self.v1.y, self.v2.z % self.v1.z), + ) + self.assertEqual( + self.v2 < self.v1.elementwise(), + self.v2.x < self.v1.x and self.v2.y < self.v1.y and self.v2.z < self.v1.z, + ) + self.assertEqual( + self.v2 > self.v1.elementwise(), + self.v2.x > self.v1.x and self.v2.y > self.v1.y and self.v2.z > self.v1.z, + ) + self.assertEqual( + self.v2 <= self.v1.elementwise(), + self.v2.x <= self.v1.x + and self.v2.y <= self.v1.y + and self.v2.z <= self.v1.z, + ) + self.assertEqual( + self.v2 >= self.v1.elementwise(), + self.v2.x >= self.v1.x + and self.v2.y >= self.v1.y + and self.v2.z >= self.v1.z, + ) + self.assertEqual( + self.v2 == self.v1.elementwise(), + self.v2.x == self.v1.x + and self.v2.y == self.v1.y + and self.v2.z == self.v1.z, + ) + self.assertEqual( + self.v2 != self.v1.elementwise(), + self.v2.x != self.v1.x + and self.v2.y != self.v1.y + and self.v2.z != self.v1.z, + ) + + # behaviour for "elementwise op elementwise" + self.assertEqual( + self.v2.elementwise() + self.v1.elementwise(), self.v2 + self.v1 + ) + self.assertEqual( + self.v2.elementwise() - self.v1.elementwise(), self.v2 - self.v1 + ) + self.assertEqual( + self.v2.elementwise() * self.v1.elementwise(), + (self.v2.x * self.v1.x, self.v2.y * self.v1.y, self.v2.z * self.v1.z), + ) + self.assertEqual( + self.v2.elementwise() / self.v1.elementwise(), + (self.v2.x / self.v1.x, self.v2.y / self.v1.y, self.v2.z / self.v1.z), + ) + self.assertEqual( + self.v2.elementwise() // self.v1.elementwise(), + (self.v2.x // self.v1.x, self.v2.y // self.v1.y, self.v2.z // self.v1.z), + ) + self.assertEqual( + self.v2.elementwise() ** self.v1.elementwise(), + (self.v2.x**self.v1.x, self.v2.y**self.v1.y, self.v2.z**self.v1.z), + ) + self.assertEqual( + self.v2.elementwise() % self.v1.elementwise(), + (self.v2.x % self.v1.x, self.v2.y % self.v1.y, self.v2.z % self.v1.z), + ) + self.assertEqual( + self.v2.elementwise() < self.v1.elementwise(), + self.v2.x < self.v1.x and self.v2.y < self.v1.y and self.v2.z < self.v1.z, + ) + self.assertEqual( + self.v2.elementwise() > self.v1.elementwise(), + self.v2.x > self.v1.x and self.v2.y > self.v1.y and self.v2.z > self.v1.z, + ) + self.assertEqual( + self.v2.elementwise() <= self.v1.elementwise(), + self.v2.x <= self.v1.x + and self.v2.y <= self.v1.y + and self.v2.z <= self.v1.z, + ) + self.assertEqual( + self.v2.elementwise() >= self.v1.elementwise(), + self.v2.x >= self.v1.x + and self.v2.y >= self.v1.y + and self.v2.z >= self.v1.z, + ) + self.assertEqual( + self.v2.elementwise() == self.v1.elementwise(), + self.v2.x == self.v1.x + and self.v2.y == self.v1.y + and self.v2.z == self.v1.z, + ) + self.assertEqual( + self.v2.elementwise() != self.v1.elementwise(), + self.v2.x != self.v1.x + and self.v2.y != self.v1.y + and self.v2.z != self.v1.z, + ) + + # other behaviour + self.assertEqual( + abs(self.v1.elementwise()), (abs(self.v1.x), abs(self.v1.y), abs(self.v1.z)) + ) + self.assertEqual(-self.v1.elementwise(), -self.v1) + self.assertEqual(+self.v1.elementwise(), +self.v1) + self.assertEqual(bool(self.v1.elementwise()), bool(self.v1)) + self.assertEqual(bool(Vector3().elementwise()), bool(Vector3())) + self.assertEqual(self.zeroVec.elementwise() ** 0, (1, 1, 1)) + self.assertRaises(ValueError, lambda: pow(Vector3(-1, 0, 0).elementwise(), 1.2)) + self.assertRaises(ZeroDivisionError, lambda: self.zeroVec.elementwise() ** -1) + self.assertRaises(ZeroDivisionError, lambda: Vector3(1, 1, 1).elementwise() / 0) + self.assertRaises( + ZeroDivisionError, lambda: Vector3(1, 1, 1).elementwise() // 0 + ) + self.assertRaises(ZeroDivisionError, lambda: Vector3(1, 1, 1).elementwise() % 0) + self.assertRaises( + ZeroDivisionError, lambda: Vector3(1, 1, 1).elementwise() / self.zeroVec + ) + self.assertRaises( + ZeroDivisionError, lambda: Vector3(1, 1, 1).elementwise() // self.zeroVec + ) + self.assertRaises( + ZeroDivisionError, lambda: Vector3(1, 1, 1).elementwise() % self.zeroVec + ) + self.assertRaises(ZeroDivisionError, lambda: 2 / self.zeroVec.elementwise()) + self.assertRaises(ZeroDivisionError, lambda: 2 // self.zeroVec.elementwise()) + self.assertRaises(ZeroDivisionError, lambda: 2 % self.zeroVec.elementwise()) + + def test_slerp(self): + self.assertRaises(ValueError, lambda: self.zeroVec.slerp(self.v1, 0.5)) + self.assertRaises(ValueError, lambda: self.v1.slerp(self.zeroVec, 0.5)) + self.assertRaises(ValueError, lambda: self.zeroVec.slerp(self.zeroVec, 0.5)) + steps = 10 + angle_step = self.e1.angle_to(self.e2) / steps + for i, u in ( + (i, self.e1.slerp(self.e2, i / float(steps))) for i in range(steps + 1) + ): + self.assertAlmostEqual(u.length(), 1) + self.assertAlmostEqual(self.e1.angle_to(u), i * angle_step) + self.assertEqual(u, self.e2) + + v1 = Vector3(100, 0, 0) + v2 = Vector3(0, 10, 7) + radial_factor = v2.length() / v1.length() + for i, u in ((i, v1.slerp(v2, -i / float(steps))) for i in range(steps + 1)): + self.assertAlmostEqual( + u.length(), + (v2.length() - v1.length()) * (float(i) / steps) + v1.length(), + ) + self.assertEqual(u, v2) + self.assertEqual(v1.slerp(v1, 0.5), v1) + self.assertEqual(v2.slerp(v2, 0.5), v2) + self.assertRaises(ValueError, lambda: v1.slerp(-v1, 0.5)) + + def test_lerp(self): + v1 = Vector3(0, 0, 0) + v2 = Vector3(10, 10, 10) + self.assertEqual(v1.lerp(v2, 0.5), (5, 5, 5)) + self.assertRaises(ValueError, lambda: v1.lerp(v2, 2.5)) + + v1 = Vector3(-10, -5, -20) + v2 = Vector3(10, 10, -20) + self.assertEqual(v1.lerp(v2, 0.5), (0, 2.5, -20)) + + def test_spherical(self): + v = Vector3() + v.from_spherical(self.v1.as_spherical()) + self.assertEqual(self.v1, v) + self.assertEqual(self.v1, Vector3.from_spherical(self.v1.as_spherical())) + self.assertEqual(self.e1.as_spherical(), (1, 90, 0)) + self.assertEqual(self.e2.as_spherical(), (1, 90, 90)) + self.assertEqual(self.e3.as_spherical(), (1, 0, 0)) + self.assertEqual((2 * self.e2).as_spherical(), (2, 90, 90)) + self.assertRaises(TypeError, lambda: v.from_spherical((None, None, None))) + self.assertRaises(TypeError, lambda: v.from_spherical("abc")) + self.assertRaises(TypeError, lambda: v.from_spherical((None, 1, 2))) + self.assertRaises(TypeError, lambda: v.from_spherical((1, 2, 3, 4))) + self.assertRaises(TypeError, lambda: v.from_spherical((1, 2))) + self.assertRaises(TypeError, lambda: v.from_spherical(1, 2, 3)) + self.assertRaises(TypeError, lambda: Vector3.from_spherical((None, None, None))) + self.assertRaises(TypeError, lambda: Vector3.from_spherical("abc")) + self.assertRaises(TypeError, lambda: Vector3.from_spherical((None, 1, 2))) + self.assertRaises(TypeError, lambda: Vector3.from_spherical((1, 2, 3, 4))) + self.assertRaises(TypeError, lambda: Vector3.from_spherical((1, 2))) + self.assertRaises(TypeError, lambda: Vector3.from_spherical(1, 2, 3)) + v.from_spherical((0.5, 90, 90)) + self.assertEqual(v, 0.5 * self.e2) + self.assertEqual(Vector3.from_spherical((0.5, 90, 90)), 0.5 * self.e2) + self.assertEqual(Vector3.from_spherical((0.5, 90, 90)), v) + + def test_inplace_operators(self): + v = Vector3(1, 1, 1) + v *= 2 + self.assertEqual(v, (2.0, 2.0, 2.0)) + + v = Vector3(4, 4, 4) + v /= 2 + self.assertEqual(v, (2.0, 2.0, 2.0)) + + v = Vector3(3.0, 3.0, 3.0) + v -= (1, 1, 1) + self.assertEqual(v, (2.0, 2.0, 2.0)) + + v = Vector3(3.0, 3.0, 3.0) + v += (1, 1, 1) + self.assertEqual(v, (4.0, 4.0, 4.0)) + + def test_pickle(self): + import pickle + + v2 = Vector2(1, 2) + v3 = Vector3(1, 2, 3) + self.assertEqual(pickle.loads(pickle.dumps(v2)), v2) + self.assertEqual(pickle.loads(pickle.dumps(v3)), v3) + + def test_subclass_operation(self): + class Vector(pygame.math.Vector3): + pass + + v = Vector(2.0, 2.0, 2.0) + v *= 2 + self.assertEqual(v, (4.0, 4.0, 4.0)) + + def test_swizzle_constants(self): + """We can get constant values from a swizzle.""" + v = Vector2(7, 6) + self.assertEqual( + v.xy1, + (7.0, 6.0, 1.0), + ) + + def test_swizzle_four_constants(self): + """We can get 4 constant values from a swizzle.""" + v = Vector2(7, 6) + self.assertEqual( + v.xy01, + (7.0, 6.0, 0.0, 1.0), + ) + + def test_swizzle_oob(self): + """An out-of-bounds swizzle raises an AttributeError.""" + v = Vector2(7, 6) + with self.assertRaises(AttributeError): + v.xyz + + @unittest.skipIf(IS_PYPY, "known pypy failure") + def test_swizzle_set_oob(self): + """An out-of-bounds swizzle set raises an AttributeError.""" + v = Vector2(7, 6) + with self.assertRaises(AttributeError): + v.xz = (1, 1) + + def test_project_v3_onto_x_axis(self): + """Project onto x-axis, e.g. get the component pointing in the x-axis direction.""" + # arrange + v = Vector3(2, 3, 4) + x_axis = Vector3(10, 0, 0) + + # act + actual = v.project(x_axis) + + # assert + self.assertEqual(v.x, actual.x) + self.assertEqual(0, actual.y) + self.assertEqual(0, actual.z) + + def test_project_v3_onto_y_axis(self): + """Project onto y-axis, e.g. get the component pointing in the y-axis direction.""" + # arrange + v = Vector3(2, 3, 4) + y_axis = Vector3(0, 100, 0) + + # act + actual = v.project(y_axis) + + # assert + self.assertEqual(0, actual.x) + self.assertEqual(v.y, actual.y) + self.assertEqual(0, actual.z) + + def test_project_v3_onto_z_axis(self): + """Project onto z-axis, e.g. get the component pointing in the z-axis direction.""" + # arrange + v = Vector3(2, 3, 4) + y_axis = Vector3(0, 0, 77) + + # act + actual = v.project(y_axis) + + # assert + self.assertEqual(0, actual.x) + self.assertEqual(0, actual.y) + self.assertEqual(v.z, actual.z) + + def test_project_v3_onto_other(self): + """Project onto other vector.""" + # arrange + v = Vector3(2, 3, 4) + other = Vector3(3, 5, 7) + + # act + actual = v.project(other) + + # assert + expected = v.dot(other) / other.dot(other) * other + self.assertAlmostEqual(expected.x, actual.x) + self.assertAlmostEqual(expected.y, actual.y) + self.assertAlmostEqual(expected.z, actual.z) + + def test_project_v3_onto_other_as_tuple(self): + """Project onto other tuple as vector.""" + # arrange + v = Vector3(2, 3, 4) + other = Vector3(3, 5, 7) + + # act + actual = v.project(tuple(other)) + + # assert + expected = v.dot(other) / other.dot(other) * other + self.assertAlmostEqual(expected.x, actual.x) + self.assertAlmostEqual(expected.y, actual.y) + self.assertAlmostEqual(expected.z, actual.z) + + def test_project_v3_onto_other_as_list(self): + """Project onto other list as vector.""" + # arrange + v = Vector3(2, 3, 4) + other = Vector3(3, 5, 7) + + # act + actual = v.project(list(other)) + + # assert + expected = v.dot(other) / other.dot(other) * other + self.assertAlmostEqual(expected.x, actual.x) + self.assertAlmostEqual(expected.y, actual.y) + self.assertAlmostEqual(expected.z, actual.z) + + def test_project_v3_raises_if_other_has_zero_length(self): + """Check if exception is raise when projected on vector has zero length.""" + # arrange + v = Vector3(2, 3, 4) + other = Vector3(0, 0, 0) + + # act / assert + self.assertRaises(ValueError, v.project, other) + + def test_project_v3_raises_if_other_is_not_iterable(self): + """Check if exception is raise when projected on vector is not iterable.""" + # arrange + v = Vector3(2, 3, 4) + other = 10 + + # act / assert + self.assertRaises(TypeError, v.project, other) + + def test_collection_abc(self): + v = Vector3(3, 4, 5) + self.assertTrue(isinstance(v, Collection)) + self.assertFalse(isinstance(v, Sequence)) + + def test_clamp_mag_v3_max(self): + v1 = Vector3(7, 2, 2) + v2 = v1.clamp_magnitude(5) + v3 = v1.clamp_magnitude(0, 5) + self.assertEqual(v2, v3) + + v1.clamp_magnitude_ip(5) + self.assertEqual(v1, v2) + + v1.clamp_magnitude_ip(0, 5) + self.assertEqual(v1, v2) + + expected_v2 = Vector3(4.635863249727653, 1.3245323570650438, 1.3245323570650438) + self.assertEqual(expected_v2, v2) + + def test_clamp_mag_v3_min(self): + v1 = Vector3(3, 1, 2) + v2 = v1.clamp_magnitude(5, 10) + v1.clamp_magnitude_ip(5, 10) + expected_v2 = Vector3(4.008918628686366, 1.3363062095621219, 2.6726124191242437) + self.assertEqual(expected_v2, v1) + self.assertEqual(expected_v2, v2) + + def test_clamp_mag_v3_no_change(self): + v1 = Vector3(1, 2, 3) + for args in ( + (1, 6), + (1.12, 5.55), + (0.93, 6.83), + (7.6,), + ): + with self.subTest(args=args): + v2 = v1.clamp_magnitude(*args) + v1.clamp_magnitude_ip(*args) + self.assertEqual(v1, v2) + self.assertEqual(v1, Vector3(1, 2, 3)) + + def test_clamp_mag_v3_edge_cases(self): + v1 = Vector3(1, 2, 1) + v2 = v1.clamp_magnitude(6, 6) + v1.clamp_magnitude_ip(6, 6) + self.assertEqual(v1, v2) + self.assertAlmostEqual(v1.length(), 6) + + v2 = v1.clamp_magnitude(0) + v1.clamp_magnitude_ip(0, 0) + self.assertEqual(v1, v2) + self.assertEqual(v1, Vector3()) + + def test_clamp_mag_v3_errors(self): + v1 = Vector3(1, 2, 2) + for invalid_args in ( + ("foo", "bar"), + (1, 2, 3), + (342.234, "test"), + ): + with self.subTest(invalid_args=invalid_args): + self.assertRaises(TypeError, v1.clamp_magnitude, *invalid_args) + self.assertRaises(TypeError, v1.clamp_magnitude_ip, *invalid_args) + + for invalid_args in ( + (-1,), + (4, 3), # min > max + (-4, 10), + (-4, -2), + ): + with self.subTest(invalid_args=invalid_args): + self.assertRaises(ValueError, v1.clamp_magnitude, *invalid_args) + self.assertRaises(ValueError, v1.clamp_magnitude_ip, *invalid_args) + + # 0 vector + v2 = Vector3() + self.assertRaises(ValueError, v2.clamp_magnitude, 3) + self.assertRaises(ValueError, v2.clamp_magnitude_ip, 4) + + def test_subclassing_v3(self): + """Check if Vector3 is subclassable""" + v = Vector3(4, 2, 0) + + class TestVector(Vector3): + def supermariobrosiscool(self): + return 722 + + other = TestVector(4, 1, 0) + + self.assertEqual(other.supermariobrosiscool(), 722) + self.assertNotEqual(type(v), TestVector) + self.assertNotEqual(type(v), type(other.copy())) + self.assertEqual(TestVector, type(other.reflect(v))) + self.assertEqual(TestVector, type(other.lerp(v, 1))) + self.assertEqual(TestVector, type(other.slerp(v, 1))) + self.assertEqual(TestVector, type(other.rotate(5, v))) + self.assertEqual(TestVector, type(other.rotate_rad(5, v))) + self.assertEqual(TestVector, type(other.project(v))) + self.assertEqual(TestVector, type(other.move_towards(v, 5))) + self.assertEqual(TestVector, type(other.clamp_magnitude(5))) + self.assertEqual(TestVector, type(other.clamp_magnitude(1, 5))) + self.assertEqual(TestVector, type(other.elementwise() + other)) + + other1 = TestVector(4, 2, 0) + + self.assertEqual(type(other + other1), TestVector) + self.assertEqual(type(other - other1), TestVector) + self.assertEqual(type(other * 3), TestVector) + self.assertEqual(type(other / 3), TestVector) + self.assertEqual(type(other.elementwise() ** 3), TestVector) + + def test_move_towards_basic(self): + expected = Vector3(7.93205057, 2006.38284641, 43.80780420) + origin = Vector3(7.22, 2004.0, 42.13) + target = Vector3(12.30, 2021.0, 54.1) + change_ip = origin.copy() + + change = origin.move_towards(target, 3) + change_ip.move_towards_ip(target, 3) + + self.assertEqual(change, expected) + self.assertEqual(change_ip, expected) + + def test_move_towards_max_distance(self): + expected = Vector3(12.30, 2021, 42.5) + origin = Vector3(7.22, 2004.0, 17.5) + change_ip = origin.copy() + + change = origin.move_towards(expected, 100) + change_ip.move_towards_ip(expected, 100) + + self.assertEqual(change, expected) + self.assertEqual(change_ip, expected) + + def test_move_nowhere(self): + origin = Vector3(7.22, 2004.0, 24.5) + target = Vector3(12.30, 2021.0, 3.2) + change_ip = origin.copy() + + change = origin.move_towards(target, 0) + change_ip.move_towards_ip(target, 0) + + self.assertEqual(change, origin) + self.assertEqual(change_ip, origin) + + def test_move_away(self): + expected = Vector3(6.74137906, 2002.39831577, 49.70890994) + origin = Vector3(7.22, 2004.0, 52.2) + target = Vector3(12.30, 2021.0, 78.64) + change_ip = origin.copy() + + change = origin.move_towards(target, -3) + change_ip.move_towards_ip(target, -3) + + self.assertEqual(change, expected) + self.assertEqual(change_ip, expected) + + def test_move_towards_self(self): + vec = Vector3(6.36, 2001.13, -123.14) + vec2 = vec.copy() + for dist in (-3.54, -1, 0, 0.234, 12): + self.assertEqual(vec.move_towards(vec2, dist), vec) + vec2.move_towards_ip(vec, dist) + self.assertEqual(vec, vec2) + + def test_move_towards_errors(self): + origin = Vector3(7.22, 2004.0, 4.1) + target = Vector3(12.30, 2021.0, -421.5) + + self.assertRaises(TypeError, origin.move_towards, target, 3, 2) + self.assertRaises(TypeError, origin.move_towards_ip, target, 3, 2) + self.assertRaises(TypeError, origin.move_towards, target, "a") + self.assertRaises(TypeError, origin.move_towards_ip, target, "b") + self.assertRaises(TypeError, origin.move_towards, "c", 3) + self.assertRaises(TypeError, origin.move_towards_ip, "d", 3) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/midi_test.py b/.venv/Lib/site-packages/pygame/tests/midi_test.py new file mode 100644 index 00000000..f4189a23 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/midi_test.py @@ -0,0 +1,463 @@ +import unittest + + +import pygame + + +class MidiInputTest(unittest.TestCase): + __tags__ = ["interactive"] + + def setUp(self): + import pygame.midi + + pygame.midi.init() + in_id = pygame.midi.get_default_input_id() + if in_id != -1: + self.midi_input = pygame.midi.Input(in_id) + else: + self.midi_input = None + + def tearDown(self): + if self.midi_input: + self.midi_input.close() + pygame.midi.quit() + + def test_Input(self): + i = pygame.midi.get_default_input_id() + if self.midi_input: + self.assertEqual(self.midi_input.device_id, i) + + # try feeding it an input id. + i = pygame.midi.get_default_output_id() + + # can handle some invalid input too. + self.assertRaises(pygame.midi.MidiException, pygame.midi.Input, i) + self.assertRaises(pygame.midi.MidiException, pygame.midi.Input, 9009) + self.assertRaises(pygame.midi.MidiException, pygame.midi.Input, -1) + self.assertRaises(TypeError, pygame.midi.Input, "1234") + self.assertRaises(OverflowError, pygame.midi.Input, pow(2, 99)) + + def test_poll(self): + if not self.midi_input: + self.skipTest("No midi Input device") + + self.assertFalse(self.midi_input.poll()) + # TODO fake some incoming data + + pygame.midi.quit() + self.assertRaises(RuntimeError, self.midi_input.poll) + # set midi_input to None to avoid error in tearDown + self.midi_input = None + + def test_read(self): + if not self.midi_input: + self.skipTest("No midi Input device") + + read = self.midi_input.read(5) + self.assertEqual(read, []) + # TODO fake some incoming data + + pygame.midi.quit() + self.assertRaises(RuntimeError, self.midi_input.read, 52) + # set midi_input to None to avoid error in tearDown + self.midi_input = None + + def test_close(self): + if not self.midi_input: + self.skipTest("No midi Input device") + + self.assertIsNotNone(self.midi_input._input) + self.midi_input.close() + self.assertIsNone(self.midi_input._input) + + +class MidiOutputTest(unittest.TestCase): + __tags__ = ["interactive"] + + def setUp(self): + import pygame.midi + + pygame.midi.init() + m_out_id = pygame.midi.get_default_output_id() + if m_out_id != -1: + self.midi_output = pygame.midi.Output(m_out_id) + else: + self.midi_output = None + + def tearDown(self): + if self.midi_output: + self.midi_output.close() + pygame.midi.quit() + + def test_Output(self): + i = pygame.midi.get_default_output_id() + if self.midi_output: + self.assertEqual(self.midi_output.device_id, i) + + # try feeding it an input id. + i = pygame.midi.get_default_input_id() + + # can handle some invalid input too. + self.assertRaises(pygame.midi.MidiException, pygame.midi.Output, i) + self.assertRaises(pygame.midi.MidiException, pygame.midi.Output, 9009) + self.assertRaises(pygame.midi.MidiException, pygame.midi.Output, -1) + self.assertRaises(TypeError, pygame.midi.Output, "1234") + self.assertRaises(OverflowError, pygame.midi.Output, pow(2, 99)) + + def test_note_off(self): + if self.midi_output: + out = self.midi_output + out.note_on(5, 30, 0) + out.note_off(5, 30, 0) + with self.assertRaises(ValueError) as cm: + out.note_off(5, 30, 25) + self.assertEqual(str(cm.exception), "Channel not between 0 and 15.") + with self.assertRaises(ValueError) as cm: + out.note_off(5, 30, -1) + self.assertEqual(str(cm.exception), "Channel not between 0 and 15.") + + def test_note_on(self): + if self.midi_output: + out = self.midi_output + out.note_on(5, 30, 0) + out.note_on(5, 42, 10) + with self.assertRaises(ValueError) as cm: + out.note_on(5, 30, 25) + self.assertEqual(str(cm.exception), "Channel not between 0 and 15.") + with self.assertRaises(ValueError) as cm: + out.note_on(5, 30, -1) + self.assertEqual(str(cm.exception), "Channel not between 0 and 15.") + + def test_set_instrument(self): + if not self.midi_output: + self.skipTest("No midi device") + out = self.midi_output + out.set_instrument(5) + out.set_instrument(42, channel=2) + with self.assertRaises(ValueError) as cm: + out.set_instrument(-6) + self.assertEqual(str(cm.exception), "Undefined instrument id: -6") + with self.assertRaises(ValueError) as cm: + out.set_instrument(156) + self.assertEqual(str(cm.exception), "Undefined instrument id: 156") + with self.assertRaises(ValueError) as cm: + out.set_instrument(5, -1) + self.assertEqual(str(cm.exception), "Channel not between 0 and 15.") + with self.assertRaises(ValueError) as cm: + out.set_instrument(5, 16) + self.assertEqual(str(cm.exception), "Channel not between 0 and 15.") + + def test_write(self): + if not self.midi_output: + self.skipTest("No midi device") + + out = self.midi_output + out.write([[[0xC0, 0, 0], 20000]]) + # is equivalent to + out.write([[[0xC0], 20000]]) + # example from the docstring : + # 1. choose program change 1 at time 20000 and + # 2. send note 65 with velocity 100 500 ms later + out.write([[[0xC0, 0, 0], 20000], [[0x90, 60, 100], 20500]]) + + out.write([]) + verrry_long = [[[0x90, 60, i % 100], 20000 + 100 * i] for i in range(1024)] + out.write(verrry_long) + + too_long = [[[0x90, 60, i % 100], 20000 + 100 * i] for i in range(1025)] + self.assertRaises(IndexError, out.write, too_long) + # test wrong data + with self.assertRaises(TypeError) as cm: + out.write("Non sens ?") + error_msg = "unsupported operand type(s) for &: 'str' and 'int'" + self.assertEqual(str(cm.exception), error_msg) + + with self.assertRaises(TypeError) as cm: + out.write(["Hey what's that?"]) + self.assertEqual(str(cm.exception), error_msg) + + def test_write_short(self): + if not self.midi_output: + self.skipTest("No midi device") + + out = self.midi_output + # program change + out.write_short(0xC0) + # put a note on, then off. + out.write_short(0x90, 65, 100) + out.write_short(0x80, 65, 100) + out.write_short(0x90) + + def test_write_sys_ex(self): + if not self.midi_output: + self.skipTest("No midi device") + + out = self.midi_output + out.write_sys_ex(pygame.midi.time(), [0xF0, 0x7D, 0x10, 0x11, 0x12, 0x13, 0xF7]) + + def test_pitch_bend(self): + # FIXME : pitch_bend in the code, but not in documentation + if not self.midi_output: + self.skipTest("No midi device") + + out = self.midi_output + with self.assertRaises(ValueError) as cm: + out.pitch_bend(5, channel=-1) + self.assertEqual(str(cm.exception), "Channel not between 0 and 15.") + with self.assertRaises(ValueError) as cm: + out.pitch_bend(5, channel=16) + with self.assertRaises(ValueError) as cm: + out.pitch_bend(-10001, 1) + self.assertEqual( + str(cm.exception), + "Pitch bend value must be between " "-8192 and +8191, not -10001.", + ) + with self.assertRaises(ValueError) as cm: + out.pitch_bend(10665, 2) + + def test_close(self): + if not self.midi_output: + self.skipTest("No midi device") + self.assertIsNotNone(self.midi_output._output) + self.midi_output.close() + self.assertIsNone(self.midi_output._output) + + def test_abort(self): + if not self.midi_output: + self.skipTest("No midi device") + self.assertEqual(self.midi_output._aborted, 0) + self.midi_output.abort() + self.assertEqual(self.midi_output._aborted, 1) + + +class MidiModuleTest(unittest.TestCase): + """Midi module tests that require midi hardware or midi.init(). + + See MidiModuleNonInteractiveTest for non-interactive module tests. + """ + + __tags__ = ["interactive"] + + def setUp(self): + import pygame.midi + + pygame.midi.init() + + def tearDown(self): + pygame.midi.quit() + + def test_get_count(self): + c = pygame.midi.get_count() + self.assertIsInstance(c, int) + self.assertTrue(c >= 0) + + def test_get_default_input_id(self): + midin_id = pygame.midi.get_default_input_id() + # if there is a not None return make sure it is an int. + self.assertIsInstance(midin_id, int) + self.assertTrue(midin_id >= -1) + pygame.midi.quit() + self.assertRaises(RuntimeError, pygame.midi.get_default_output_id) + + def test_get_default_output_id(self): + c = pygame.midi.get_default_output_id() + self.assertIsInstance(c, int) + self.assertTrue(c >= -1) + pygame.midi.quit() + self.assertRaises(RuntimeError, pygame.midi.get_default_output_id) + + def test_get_device_info(self): + an_id = pygame.midi.get_default_output_id() + if an_id != -1: + interf, name, input, output, opened = pygame.midi.get_device_info(an_id) + self.assertEqual(output, 1) + self.assertEqual(input, 0) + self.assertEqual(opened, 0) + + an_in_id = pygame.midi.get_default_input_id() + if an_in_id != -1: + r = pygame.midi.get_device_info(an_in_id) + # if r is None, it means that the id is out of range. + interf, name, input, output, opened = r + + self.assertEqual(output, 0) + self.assertEqual(input, 1) + self.assertEqual(opened, 0) + out_of_range = pygame.midi.get_count() + for num in range(out_of_range): + self.assertIsNotNone(pygame.midi.get_device_info(num)) + info = pygame.midi.get_device_info(out_of_range) + self.assertIsNone(info) + + def test_init(self): + pygame.midi.quit() + self.assertRaises(RuntimeError, pygame.midi.get_count) + # initialising many times should be fine. + pygame.midi.init() + pygame.midi.init() + pygame.midi.init() + pygame.midi.init() + + self.assertTrue(pygame.midi.get_init()) + + def test_quit(self): + # It is safe to call this more than once. + pygame.midi.quit() + pygame.midi.init() + pygame.midi.quit() + pygame.midi.quit() + pygame.midi.init() + pygame.midi.init() + pygame.midi.quit() + + self.assertFalse(pygame.midi.get_init()) + + def test_get_init(self): + # Already initialized as pygame.midi.init() was called in setUp(). + self.assertTrue(pygame.midi.get_init()) + + def test_time(self): + mtime = pygame.midi.time() + self.assertIsInstance(mtime, int) + # should be close to 2-3... since the timer is just init'd. + self.assertTrue(0 <= mtime < 100) + + +class MidiModuleNonInteractiveTest(unittest.TestCase): + """Midi module tests that do not require midi hardware or midi.init(). + + See MidiModuleTest for interactive module tests. + """ + + def setUp(self): + import pygame.midi + + def test_midiin(self): + """Ensures the MIDIIN event id exists in the midi module. + + The MIDIIN event id can be accessed via the midi module for backward + compatibility. + """ + self.assertEqual(pygame.midi.MIDIIN, pygame.MIDIIN) + self.assertEqual(pygame.midi.MIDIIN, pygame.locals.MIDIIN) + + self.assertNotEqual(pygame.midi.MIDIIN, pygame.MIDIOUT) + self.assertNotEqual(pygame.midi.MIDIIN, pygame.locals.MIDIOUT) + + def test_midiout(self): + """Ensures the MIDIOUT event id exists in the midi module. + + The MIDIOUT event id can be accessed via the midi module for backward + compatibility. + """ + self.assertEqual(pygame.midi.MIDIOUT, pygame.MIDIOUT) + self.assertEqual(pygame.midi.MIDIOUT, pygame.locals.MIDIOUT) + + self.assertNotEqual(pygame.midi.MIDIOUT, pygame.MIDIIN) + self.assertNotEqual(pygame.midi.MIDIOUT, pygame.locals.MIDIIN) + + def test_MidiException(self): + """Ensures the MidiException is raised as expected.""" + + def raiseit(): + raise pygame.midi.MidiException("Hello Midi param") + + with self.assertRaises(pygame.midi.MidiException) as cm: + raiseit() + + self.assertEqual(cm.exception.parameter, "Hello Midi param") + + def test_midis2events(self): + """Ensures midi events are properly converted to pygame events.""" + # List/tuple indexes. + MIDI_DATA = 0 + MD_STATUS = 0 + MD_DATA1 = 1 + MD_DATA2 = 2 + MD_DATA3 = 3 + + TIMESTAMP = 1 + + # Midi events take the form of: + # ((status, data1, data2, data3), timestamp) + midi_events = ( + ((0xC0, 0, 1, 2), 20000), + ((0x90, 60, 1000, "string_data"), 20001), + (("0", "1", "2", "3"), "4"), + ) + expected_num_events = len(midi_events) + + # Test different device ids. + for device_id in range(3): + pg_events = pygame.midi.midis2events(midi_events, device_id) + + self.assertEqual(len(pg_events), expected_num_events) + + for i, pg_event in enumerate(pg_events): + # Get the original midi data for comparison. + midi_event = midi_events[i] + midi_event_data = midi_event[MIDI_DATA] + + # Can't directly check event instance as pygame.event.Event is + # a function. + # self.assertIsInstance(pg_event, pygame.event.Event) + self.assertEqual(pg_event.__class__.__name__, "Event") + self.assertEqual(pg_event.type, pygame.MIDIIN) + self.assertEqual(pg_event.status, midi_event_data[MD_STATUS]) + self.assertEqual(pg_event.data1, midi_event_data[MD_DATA1]) + self.assertEqual(pg_event.data2, midi_event_data[MD_DATA2]) + self.assertEqual(pg_event.data3, midi_event_data[MD_DATA3]) + self.assertEqual(pg_event.timestamp, midi_event[TIMESTAMP]) + self.assertEqual(pg_event.vice_id, device_id) + + def test_midis2events__missing_event_data(self): + """Ensures midi events with missing values are handled properly.""" + midi_event_missing_data = ((0xC0, 0, 1), 20000) + midi_event_missing_timestamp = ((0xC0, 0, 1, 2),) + + for midi_event in (midi_event_missing_data, midi_event_missing_timestamp): + with self.assertRaises(ValueError): + events = pygame.midi.midis2events([midi_event], 0) + + def test_midis2events__extra_event_data(self): + """Ensures midi events with extra values are handled properly.""" + midi_event_extra_data = ((0xC0, 0, 1, 2, "extra"), 20000) + midi_event_extra_timestamp = ((0xC0, 0, 1, 2), 20000, "extra") + + for midi_event in (midi_event_extra_data, midi_event_extra_timestamp): + with self.assertRaises(ValueError): + events = pygame.midi.midis2events([midi_event], 0) + + def test_midis2events__extra_event_data_missing_timestamp(self): + """Ensures midi events with extra data and no timestamps are handled + properly. + """ + midi_event_extra_data_no_timestamp = ((0xC0, 0, 1, 2, "extra"),) + + with self.assertRaises(ValueError): + events = pygame.midi.midis2events([midi_event_extra_data_no_timestamp], 0) + + def test_conversions(self): + """of frequencies to midi note numbers and ansi note names.""" + from pygame.midi import frequency_to_midi, midi_to_frequency, midi_to_ansi_note + + self.assertEqual(frequency_to_midi(27.5), 21) + self.assertEqual(frequency_to_midi(36.7), 26) + self.assertEqual(frequency_to_midi(4186.0), 108) + self.assertEqual(midi_to_frequency(21), 27.5) + self.assertEqual(midi_to_frequency(26), 36.7) + self.assertEqual(midi_to_frequency(108), 4186.0) + self.assertEqual(midi_to_ansi_note(21), "A0") + self.assertEqual(midi_to_ansi_note(71), "B4") + self.assertEqual(midi_to_ansi_note(82), "A#5") + self.assertEqual(midi_to_ansi_note(83), "B5") + self.assertEqual(midi_to_ansi_note(93), "A6") + self.assertEqual(midi_to_ansi_note(94), "A#6") + self.assertEqual(midi_to_ansi_note(95), "B6") + self.assertEqual(midi_to_ansi_note(96), "C7") + self.assertEqual(midi_to_ansi_note(102), "F#7") + self.assertEqual(midi_to_ansi_note(108), "C8") + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/mixer_music_tags.py b/.venv/Lib/site-packages/pygame/tests/mixer_music_tags.py new file mode 100644 index 00000000..30f68937 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/mixer_music_tags.py @@ -0,0 +1,7 @@ +__tags__ = [] + +import pygame +import sys + +if "pygame.mixer_music" not in sys.modules: + __tags__.extend(("ignore", "subprocess_ignore")) diff --git a/.venv/Lib/site-packages/pygame/tests/mixer_music_test.py b/.venv/Lib/site-packages/pygame/tests/mixer_music_test.py new file mode 100644 index 00000000..b62ae1ec --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/mixer_music_test.py @@ -0,0 +1,439 @@ +import os +import sys +import platform +import unittest +import time + +from pygame.tests.test_utils import example_path +import pygame + + +class MixerMusicModuleTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + # Initializing the mixer is slow, so minimize the times it is called. + pygame.mixer.init() + + @classmethod + def tearDownClass(cls): + pygame.mixer.quit() + + def setUp(cls): + # This makes sure the mixer is always initialized before each test (in + # case a test calls pygame.mixer.quit()). + if pygame.mixer.get_init() is None: + pygame.mixer.init() + + def test_load_mp3(self): + "|tags:music|" + self.music_load("mp3") + + def test_load_ogg(self): + "|tags:music|" + self.music_load("ogg") + + def test_load_wav(self): + "|tags:music|" + self.music_load("wav") + + def music_load(self, format): + data_fname = example_path("data") + + path = os.path.join(data_fname, f"house_lo.{format}") + if os.sep == "\\": + path = path.replace("\\", "\\\\") + umusfn = str(path) + bmusfn = umusfn.encode() + + pygame.mixer.music.load(umusfn) + pygame.mixer.music.load(bmusfn) + + def test_load_object(self): + """test loading music from file-like objects.""" + formats = ["ogg", "wav"] + data_fname = example_path("data") + for f in formats: + path = os.path.join(data_fname, f"house_lo.{f}") + if os.sep == "\\": + path = path.replace("\\", "\\\\") + bmusfn = path.encode() + + with open(bmusfn, "rb") as musf: + pygame.mixer.music.load(musf) + + def test_object_namehint(self): + """test loading & queuing music from file-like objects with namehint argument.""" + formats = ["wav", "ogg"] + data_fname = example_path("data") + for f in formats: + path = os.path.join(data_fname, f"house_lo.{f}") + if os.sep == "\\": + path = path.replace("\\", "\\\\") + bmusfn = path.encode() + + # these two "with open" blocks need to be separate, which is kinda weird + with open(bmusfn, "rb") as musf: + pygame.mixer.music.load(musf, f) + + with open(bmusfn, "rb") as musf: + pygame.mixer.music.queue(musf, f) + + with open(bmusfn, "rb") as musf: + pygame.mixer.music.load(musf, namehint=f) + + with open(bmusfn, "rb") as musf: + pygame.mixer.music.queue(musf, namehint=f) + + def test_load_unicode(self): + """test non-ASCII unicode path""" + import shutil + + ep = example_path("data") + temp_file = os.path.join(ep, "你好.wav") + org_file = os.path.join(ep, "house_lo.wav") + try: + with open(temp_file, "w") as f: + pass + os.remove(temp_file) + except OSError: + raise unittest.SkipTest("the path cannot be opened") + shutil.copy(org_file, temp_file) + try: + pygame.mixer.music.load(temp_file) + pygame.mixer.music.load(org_file) # unload + finally: + os.remove(temp_file) + + def test_unload(self): + import shutil + import tempfile + + ep = example_path("data") + org_file = os.path.join(ep, "house_lo.wav") + tmpfd, tmppath = tempfile.mkstemp(".wav") + os.close(tmpfd) + shutil.copy(org_file, tmppath) + try: + pygame.mixer.music.load(tmppath) + pygame.mixer.music.unload() + finally: + os.remove(tmppath) + + def test_queue_mp3(self): + """Ensures queue() accepts mp3 files. + + |tags:music| + """ + filename = example_path(os.path.join("data", "house_lo.mp3")) + pygame.mixer.music.queue(filename) + + def test_queue_ogg(self): + """Ensures queue() accepts ogg files. + + |tags:music| + """ + filename = example_path(os.path.join("data", "house_lo.ogg")) + pygame.mixer.music.queue(filename) + + def test_queue_wav(self): + """Ensures queue() accepts wav files. + + |tags:music| + """ + filename = example_path(os.path.join("data", "house_lo.wav")) + pygame.mixer.music.queue(filename) + + def test_queue__multiple_calls(self): + """Ensures queue() can be called multiple times.""" + ogg_file = example_path(os.path.join("data", "house_lo.ogg")) + wav_file = example_path(os.path.join("data", "house_lo.wav")) + + pygame.mixer.music.queue(ogg_file) + pygame.mixer.music.queue(wav_file) + + def test_queue__arguments(self): + """Ensures queue() can be called with proper arguments.""" + wav_file = example_path(os.path.join("data", "house_lo.wav")) + + pygame.mixer.music.queue(wav_file, loops=2) + pygame.mixer.music.queue(wav_file, namehint="") + pygame.mixer.music.queue(wav_file, "") + pygame.mixer.music.queue(wav_file, "", 2) + + def test_queue__no_file(self): + """Ensures queue() correctly handles missing the file argument.""" + with self.assertRaises(TypeError): + pygame.mixer.music.queue() + + def test_queue__invalid_sound_type(self): + """Ensures queue() correctly handles invalid file types.""" + not_a_sound_file = example_path(os.path.join("data", "city.png")) + + with self.assertRaises(pygame.error): + pygame.mixer.music.queue(not_a_sound_file) + + def test_queue__invalid_filename(self): + """Ensures queue() correctly handles invalid filenames.""" + with self.assertRaises(pygame.error): + pygame.mixer.music.queue("") + + def test_music_pause__unpause(self): + """Ensure music has the correct position immediately after unpausing + + |tags:music| + """ + filename = example_path(os.path.join("data", "house_lo.mp3")) + pygame.mixer.music.load(filename) + pygame.mixer.music.play() + + # Wait 0.05s, then pause + time.sleep(0.05) + pygame.mixer.music.pause() + # Wait 0.05s, get position, unpause, then get position again + time.sleep(0.05) + before_unpause = pygame.mixer.music.get_pos() + pygame.mixer.music.unpause() + after_unpause = pygame.mixer.music.get_pos() + + self.assertEqual(before_unpause, after_unpause) + + def test_stop(self): + # __doc__ (as of 2008-08-02) for pygame.mixer_music.stop: + + # Stops the music playback if it is currently playing. + filename = example_path(os.path.join("data", "house_lo.mp3")) + pygame.mixer.music.load(filename) + pygame.mixer.music.play() + + pygame.mixer.music.stop() + self.assertEqual(pygame.mixer.music.get_busy(), False) + + def todo_test_rewind(self): + # __doc__ (as of 2008-08-02) for pygame.mixer_music.rewind: + + # Resets playback of the current music to the beginning. + + self.fail() + + def todo_test_get_pos(self): + # __doc__ (as of 2008-08-02) for pygame.mixer_music.get_pos: + + # This gets the number of milliseconds that the music has been playing + # for. The returned time only represents how long the music has been + # playing; it does not take into account any starting position + # offsets. + # + + self.fail() + + # def test_fadeout(self): + # filename = example_path(os.path.join("data", "house_lo.mp3")) + # pygame.mixer.music.load(filename) + # pygame.mixer.music.play() + + # pygame.mixer.music.fadeout(50) + # time.sleep(0.3) + # self.assertEqual(pygame.mixer.music.get_busy(), False) + + @unittest.skipIf( + os.environ.get("SDL_AUDIODRIVER") == "disk", + 'disk audio driver "playback" writing to disk is slow', + ) + def test_play__start_time(self): + pygame.display.init() + + # music file is 7 seconds long + filename = example_path(os.path.join("data", "house_lo.ogg")) + pygame.mixer.music.load(filename) + start_time_in_seconds = 6.0 # 6 seconds + + music_finished = False + clock = pygame.time.Clock() + start_time_in_ms = clock.tick() + # should play the last 1 second + pygame.mixer.music.play(0, start=start_time_in_seconds) + running = True + while running: + pygame.event.pump() + + if not (pygame.mixer.music.get_busy() or music_finished): + music_finished = True + time_to_finish = (clock.tick() - start_time_in_ms) // 1000 + self.assertEqual(time_to_finish, 1) + running = False + + def test_play(self): + # __doc__ (as of 2008-08-02) for pygame.mixer_music.play: + + # This will play the loaded music stream. If the music is already + # playing it will be restarted. + # + # The loops argument controls the number of repeats a music will play. + # play(5) will cause the music to played once, then repeated five + # times, for a total of six. If the loops is -1 then the music will + # repeat indefinitely. + # + # The starting position argument controls where in the music the song + # starts playing. The starting position is dependent on the format of + # music playing. MP3 and OGG use the position as time (in seconds). + # MOD music it is the pattern order number. Passing a startpos will + # raise a NotImplementedError if it cannot set the start position + # + filename = example_path(os.path.join("data", "house_lo.mp3")) + pygame.mixer.music.load(filename) + pygame.mixer.music.play() + + self.assertTrue(pygame.mixer.music.get_busy()) + + pygame.mixer.music.stop() + + def todo_test_load(self): + # __doc__ (as of 2008-08-02) for pygame.mixer_music.load: + + # This will load a music file and prepare it for playback. If a music + # stream is already playing it will be stopped. This does not start + # the music playing. + # + # Music can only be loaded from filenames, not python file objects + # like the other pygame loading functions. + # + + self.fail() + + def test_get_volume(self): + # __doc__ (as of 2008-08-02) for pygame.mixer_music.get_volume: + + # Returns the current volume for the mixer. The value will be between + # 0.0 and 1.0. + # + filename = example_path(os.path.join("data", "house_lo.mp3")) + pygame.mixer.music.load(filename) + pygame.mixer.music.play() + + vol = pygame.mixer.music.get_volume() + self.assertGreaterEqual(vol, 0) + self.assertLessEqual(vol, 1) + + pygame.mixer.music.stop() + + def todo_test_set_endevent(self): + # __doc__ (as of 2008-08-02) for pygame.mixer_music.set_endevent: + + # This causes Pygame to signal (by means of the event queue) when the + # music is done playing. The argument determines the type of event + # that will be queued. + # + # The event will be queued every time the music finishes, not just the + # first time. To stop the event from being queued, call this method + # with no argument. + # + + self.fail() + + def test_pause(self): + # __doc__ (as of 2008-08-02) for pygame.mixer_music.pause: + + # Temporarily stop playback of the music stream. It can be resumed + # with the pygame.mixer.music.unpause() function. + # + self.music_load("ogg") + self.assertFalse(pygame.mixer.music.get_busy()) + pygame.mixer.music.play() + self.assertTrue(pygame.mixer.music.get_busy()) + pygame.mixer.music.pause() + self.assertFalse(pygame.mixer.music.get_busy()) + + def test_get_busy(self): + # __doc__ (as of 2008-08-02) for pygame.mixer_music.get_busy: + + # Returns True when the music stream is actively playing. When the + # music is idle this returns False. + # + + self.music_load("ogg") + self.assertFalse(pygame.mixer.music.get_busy()) + pygame.mixer.music.play() + self.assertTrue(pygame.mixer.music.get_busy()) + pygame.mixer.music.pause() + self.assertFalse(pygame.mixer.music.get_busy()) + + def todo_test_get_endevent(self): + # __doc__ (as of 2008-08-02) for pygame.mixer_music.get_endevent: + + # Returns the event type to be sent every time the music finishes + # playback. If there is no endevent the function returns + # pygame.NOEVENT. + # + + self.fail() + + def test_unpause(self): + # __doc__ (as of 2008-08-02) for pygame.mixer_music.unpause: + + # This will resume the playback of a music stream after it has been paused. + + filename = example_path(os.path.join("data", "house_lo.mp3")) + pygame.mixer.music.load(filename) + pygame.mixer.music.play() + self.assertTrue(pygame.mixer.music.get_busy()) + time.sleep(0.1) + pygame.mixer.music.pause() + self.assertFalse(pygame.mixer.music.get_busy()) + before = pygame.mixer.music.get_pos() + pygame.mixer.music.unpause() + after = pygame.mixer.music.get_pos() + self.assertTrue(pygame.mixer.music.get_busy()) + # It could rarely be that it is +/- 1 different + # But mostly, after should equal before. + self.assertTrue(before - 1 <= after <= before + 1) + + pygame.mixer.music.stop() + + def test_set_volume(self): + # __doc__ (as of 2008-08-02) for pygame.mixer_music.set_volume: + + # Set the volume of the music playback. The value argument is between + # 0.0 and 1.0. When new music is loaded the volume is reset. + # + filename = example_path(os.path.join("data", "house_lo.mp3")) + pygame.mixer.music.load(filename) + pygame.mixer.music.play() + + pygame.mixer.music.set_volume(0.5) + vol = pygame.mixer.music.get_volume() + self.assertEqual(vol, 0.5) + + pygame.mixer.music.stop() + + def todo_test_set_pos(self): + # __doc__ (as of 2010-24-05) for pygame.mixer_music.set_pos: + + # This sets the position in the music file where playback will start. The + # meaning of "pos", a float (or a number that can be converted to a float), + # depends on the music format. Newer versions of SDL_mixer have better + # positioning support than earlier. An SDLError is raised if a particular + # format does not support positioning. + # + + self.fail() + + def test_init(self): + """issue #955. unload music whenever mixer.quit() is called""" + import tempfile + import shutil + + testfile = example_path(os.path.join("data", "house_lo.wav")) + tempcopy = os.path.join(tempfile.gettempdir(), "tempfile.wav") + + for i in range(10): + pygame.mixer.init() + try: + shutil.copy2(testfile, tempcopy) + pygame.mixer.music.load(tempcopy) + pygame.mixer.quit() + finally: + os.remove(tempcopy) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/mixer_tags.py b/.venv/Lib/site-packages/pygame/tests/mixer_tags.py new file mode 100644 index 00000000..06a9de2a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/mixer_tags.py @@ -0,0 +1,7 @@ +__tags__ = [] + +import pygame +import sys + +if "pygame.mixer" not in sys.modules: + __tags__.extend(("ignore", "subprocess_ignore")) diff --git a/.venv/Lib/site-packages/pygame/tests/mixer_test.py b/.venv/Lib/site-packages/pygame/tests/mixer_test.py new file mode 100644 index 00000000..a85e01ac --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/mixer_test.py @@ -0,0 +1,1439 @@ +import sys +import os +import unittest +import pathlib +import platform +import time + +from pygame.tests.test_utils import example_path + +import pygame +from pygame import mixer + +IS_PYPY = "PyPy" == platform.python_implementation() + +################################### CONSTANTS ################################## + +FREQUENCIES = [11025, 22050, 44100, 48000] +SIZES = [-16, -8, 8, 16] # fixme +# size 32 failed in test_get_init__returns_exact_values_used_for_init +CHANNELS = [1, 2] +BUFFERS = [3024] + +CONFIGS = [ + {"frequency": f, "size": s, "channels": c} + for f in FREQUENCIES + for s in SIZES + for c in CHANNELS +] +# Using all CONFIGS fails on a Mac; probably older SDL_mixer; we could do: +# if platform.system() == 'Darwin': +# But using all CONFIGS is very slow (> 10 sec for example) +# And probably, we don't need to be so exhaustive, hence: + +CONFIG = {"frequency": 44100, "size": 32, "channels": 2, "allowedchanges": 0} + + +class InvalidBool: + """To help test invalid bool values.""" + + __bool__ = None + + +############################## MODULE LEVEL TESTS ############################# + + +class MixerModuleTest(unittest.TestCase): + def tearDown(self): + mixer.quit() + mixer.pre_init(0, 0, 0, 0) + + def test_init__keyword_args(self): + # note: this test used to loop over all CONFIGS, but it's very slow.. + mixer.init(**CONFIG) + mixer_conf = mixer.get_init() + + self.assertEqual(mixer_conf[0], CONFIG["frequency"]) + # Not all "sizes" are supported on all systems, hence "abs". + self.assertEqual(abs(mixer_conf[1]), abs(CONFIG["size"])) + self.assertGreaterEqual(mixer_conf[2], CONFIG["channels"]) + + def test_pre_init__keyword_args(self): + # note: this test used to loop over all CONFIGS, but it's very slow.. + mixer.pre_init(**CONFIG) + mixer.init() + + mixer_conf = mixer.get_init() + + self.assertEqual(mixer_conf[0], CONFIG["frequency"]) + # Not all "sizes" are supported on all systems, hence "abs". + self.assertEqual(abs(mixer_conf[1]), abs(CONFIG["size"])) + self.assertGreaterEqual(mixer_conf[2], CONFIG["channels"]) + + def test_pre_init__zero_values(self): + # Ensure that argument values of 0 are replaced with + # default values. No way to check buffer size though. + mixer.pre_init(22050, -8, 1) # Non default values + mixer.pre_init(0, 0, 0) # Should reset to default values + mixer.init(allowedchanges=0) + self.assertEqual(mixer.get_init()[0], 44100) + self.assertEqual(mixer.get_init()[1], -16) + self.assertGreaterEqual(mixer.get_init()[2], 2) + + def test_init__zero_values(self): + # Ensure that argument values of 0 are replaced with + # preset values. No way to check buffer size though. + mixer.pre_init(44100, 8, 1, allowedchanges=0) # None default values + mixer.init(0, 0, 0) + self.assertEqual(mixer.get_init(), (44100, 8, 1)) + + # def test_get_init__returns_exact_values_used_for_init(self): + # # TODO: size 32 fails in this test (maybe SDL_mixer bug) + + # # TODO: 2) When you start the mixer, you request the settings. + # # But it can be that a sound system doesn’t support what you request… + # # and it gives you back something close to what you request but not equal. + # # So, you can’t test for equality. + # # See allowedchanges + + # for init_conf in CONFIGS: + # frequency, size, channels = init_conf.values() + # if (frequency, size) == (22050, 16): + # continue + # mixer.init(frequency, size, channels) + + # mixer_conf = mixer.get_init() + # self.assertEqual(tuple(init_conf.values()), mixer_conf) + # mixer.quit() + + def test_get_init__returns_None_if_mixer_not_initialized(self): + self.assertIsNone(mixer.get_init()) + + def test_get_num_channels__defaults_eight_after_init(self): + mixer.init() + self.assertEqual(mixer.get_num_channels(), 8) + + def test_set_num_channels(self): + mixer.init() + + default_num_channels = mixer.get_num_channels() + for i in range(1, default_num_channels + 1): + mixer.set_num_channels(i) + self.assertEqual(mixer.get_num_channels(), i) + + def test_quit(self): + """get_num_channels() Should throw pygame.error if uninitialized + after mixer.quit()""" + mixer.init() + mixer.quit() + self.assertRaises(pygame.error, mixer.get_num_channels) + + # TODO: FIXME: appveyor and pypy (on linux) fails here sometimes. + @unittest.skipIf(sys.platform.startswith("win"), "See github issue 892.") + @unittest.skipIf(IS_PYPY, "random errors here with pypy") + def test_sound_args(self): + def get_bytes(snd): + return snd.get_raw() + + mixer.init() + + sample = b"\x00\xff" * 24 + wave_path = example_path(os.path.join("data", "house_lo.wav")) + uwave_path = str(wave_path) + bwave_path = uwave_path.encode(sys.getfilesystemencoding()) + snd = mixer.Sound(file=wave_path) + self.assertTrue(snd.get_length() > 0.5) + snd_bytes = get_bytes(snd) + self.assertTrue(len(snd_bytes) > 1000) + + self.assertEqual(get_bytes(mixer.Sound(wave_path)), snd_bytes) + + self.assertEqual(get_bytes(mixer.Sound(file=uwave_path)), snd_bytes) + self.assertEqual(get_bytes(mixer.Sound(uwave_path)), snd_bytes) + arg_emsg = "Sound takes either 1 positional or 1 keyword argument" + + with self.assertRaises(TypeError) as cm: + mixer.Sound() + self.assertEqual(str(cm.exception), arg_emsg) + with self.assertRaises(TypeError) as cm: + mixer.Sound(wave_path, buffer=sample) + self.assertEqual(str(cm.exception), arg_emsg) + with self.assertRaises(TypeError) as cm: + mixer.Sound(sample, file=wave_path) + self.assertEqual(str(cm.exception), arg_emsg) + with self.assertRaises(TypeError) as cm: + mixer.Sound(buffer=sample, file=wave_path) + self.assertEqual(str(cm.exception), arg_emsg) + + with self.assertRaises(TypeError) as cm: + mixer.Sound(foobar=sample) + self.assertEqual(str(cm.exception), "Unrecognized keyword argument 'foobar'") + + snd = mixer.Sound(wave_path, **{}) + self.assertEqual(get_bytes(snd), snd_bytes) + snd = mixer.Sound(*[], **{"file": wave_path}) + + with self.assertRaises(TypeError) as cm: + mixer.Sound([]) + self.assertEqual(str(cm.exception), "Unrecognized argument (type list)") + + with self.assertRaises(TypeError) as cm: + snd = mixer.Sound(buffer=[]) + emsg = "Expected object with buffer interface: got a list" + self.assertEqual(str(cm.exception), emsg) + + ufake_path = "12345678" + self.assertRaises(IOError, mixer.Sound, ufake_path) + self.assertRaises(IOError, mixer.Sound, "12345678") + + with self.assertRaises(TypeError) as cm: + mixer.Sound(buffer="something") + emsg = "Unicode object not allowed as buffer object" + self.assertEqual(str(cm.exception), emsg) + self.assertEqual(get_bytes(mixer.Sound(buffer=sample)), sample) + if type(sample) != str: + somebytes = get_bytes(mixer.Sound(sample)) + # on python 2 we do not allow using string except as file name. + self.assertEqual(somebytes, sample) + self.assertEqual(get_bytes(mixer.Sound(file=bwave_path)), snd_bytes) + self.assertEqual(get_bytes(mixer.Sound(bwave_path)), snd_bytes) + + snd = mixer.Sound(wave_path) + with self.assertRaises(TypeError) as cm: + mixer.Sound(wave_path, array=snd) + self.assertEqual(str(cm.exception), arg_emsg) + with self.assertRaises(TypeError) as cm: + mixer.Sound(buffer=sample, array=snd) + self.assertEqual(str(cm.exception), arg_emsg) + snd2 = mixer.Sound(array=snd) + self.assertEqual(snd.get_raw(), snd2.get_raw()) + + def test_sound_unicode(self): + """test non-ASCII unicode path""" + mixer.init() + import shutil + + ep = example_path("data") + temp_file = os.path.join(ep, "你好.wav") + org_file = os.path.join(ep, "house_lo.wav") + shutil.copy(org_file, temp_file) + try: + with open(temp_file, "rb") as f: + pass + except OSError: + raise unittest.SkipTest("the path cannot be opened") + + try: + sound = mixer.Sound(temp_file) + del sound + finally: + os.remove(temp_file) + + @unittest.skipIf( + os.environ.get("SDL_AUDIODRIVER") == "disk", + "this test fails without real sound card", + ) + def test_array_keyword(self): + try: + from numpy import ( + array, + arange, + zeros, + int8, + uint8, + int16, + uint16, + int32, + uint32, + ) + except ImportError: + self.skipTest("requires numpy") + + freq = 22050 + format_list = [-8, 8, -16, 16] + channels_list = [1, 2] + + a_lists = {f: [] for f in format_list} + a32u_mono = arange(0, 256, 1, uint32) + a16u_mono = a32u_mono.astype(uint16) + a8u_mono = a32u_mono.astype(uint8) + au_list_mono = [(1, a) for a in [a8u_mono, a16u_mono, a32u_mono]] + for format in format_list: + if format > 0: + a_lists[format].extend(au_list_mono) + a32s_mono = arange(-128, 128, 1, int32) + a16s_mono = a32s_mono.astype(int16) + a8s_mono = a32s_mono.astype(int8) + as_list_mono = [(1, a) for a in [a8s_mono, a16s_mono, a32s_mono]] + for format in format_list: + if format < 0: + a_lists[format].extend(as_list_mono) + a32u_stereo = zeros([a32u_mono.shape[0], 2], uint32) + a32u_stereo[:, 0] = a32u_mono + a32u_stereo[:, 1] = 255 - a32u_mono + a16u_stereo = a32u_stereo.astype(uint16) + a8u_stereo = a32u_stereo.astype(uint8) + au_list_stereo = [(2, a) for a in [a8u_stereo, a16u_stereo, a32u_stereo]] + for format in format_list: + if format > 0: + a_lists[format].extend(au_list_stereo) + a32s_stereo = zeros([a32s_mono.shape[0], 2], int32) + a32s_stereo[:, 0] = a32s_mono + a32s_stereo[:, 1] = -1 - a32s_mono + a16s_stereo = a32s_stereo.astype(int16) + a8s_stereo = a32s_stereo.astype(int8) + as_list_stereo = [(2, a) for a in [a8s_stereo, a16s_stereo, a32s_stereo]] + for format in format_list: + if format < 0: + a_lists[format].extend(as_list_stereo) + + for format in format_list: + for channels in channels_list: + try: + mixer.init(freq, format, channels) + except pygame.error: + # Some formats (e.g. 16) may not be supported. + continue + try: + __, f, c = mixer.get_init() + if f != format or c != channels: + # Some formats (e.g. -8) may not be supported. + continue + for c, a in a_lists[format]: + self._test_array_argument(format, a, c == channels) + finally: + mixer.quit() + + def _test_array_argument(self, format, a, test_pass): + from numpy import array, all as all_ + + try: + snd = mixer.Sound(array=a) + except ValueError: + if not test_pass: + return + self.fail("Raised ValueError: Format %i, dtype %s" % (format, a.dtype)) + if not test_pass: + self.fail( + "Did not raise ValueError: Format %i, dtype %s" % (format, a.dtype) + ) + a2 = array(snd) + a3 = a.astype(a2.dtype) + lshift = abs(format) - 8 * a.itemsize + if lshift >= 0: + # This is asymmetric with respect to downcasting. + a3 <<= lshift + self.assertTrue(all_(a2 == a3), "Format %i, dtype %s" % (format, a.dtype)) + + def _test_array_interface_fail(self, a): + self.assertRaises(ValueError, mixer.Sound, array=a) + + def test_array_interface(self): + mixer.init(22050, -16, 1, allowedchanges=0) + snd = mixer.Sound(buffer=b"\x00\x7f" * 20) + d = snd.__array_interface__ + self.assertTrue(isinstance(d, dict)) + if pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN: + typestr = "") if is_lil_endian else (">", "<") + shape = (10, channels)[:ndim] + strides = (channels * itemsize, itemsize)[2 - ndim :] + exp = Exporter(shape, format=frev + "i") + snd = mixer.Sound(array=exp) + buflen = len(exp) * itemsize * channels + imp = Importer(snd, buftools.PyBUF_SIMPLE) + self.assertEqual(imp.ndim, 0) + self.assertTrue(imp.format is None) + self.assertEqual(imp.len, buflen) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.shape is None) + self.assertTrue(imp.strides is None) + self.assertTrue(imp.suboffsets is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.buf, snd._samples_address) + imp = Importer(snd, buftools.PyBUF_WRITABLE) + self.assertEqual(imp.ndim, 0) + self.assertTrue(imp.format is None) + self.assertEqual(imp.len, buflen) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.shape is None) + self.assertTrue(imp.strides is None) + self.assertTrue(imp.suboffsets is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.buf, snd._samples_address) + imp = Importer(snd, buftools.PyBUF_FORMAT) + self.assertEqual(imp.ndim, 0) + self.assertEqual(imp.format, format) + self.assertEqual(imp.len, buflen) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.shape is None) + self.assertTrue(imp.strides is None) + self.assertTrue(imp.suboffsets is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.buf, snd._samples_address) + imp = Importer(snd, buftools.PyBUF_ND) + self.assertEqual(imp.ndim, ndim) + self.assertTrue(imp.format is None) + self.assertEqual(imp.len, buflen) + self.assertEqual(imp.itemsize, itemsize) + self.assertEqual(imp.shape, shape) + self.assertTrue(imp.strides is None) + self.assertTrue(imp.suboffsets is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.buf, snd._samples_address) + imp = Importer(snd, buftools.PyBUF_STRIDES) + self.assertEqual(imp.ndim, ndim) + self.assertTrue(imp.format is None) + self.assertEqual(imp.len, buflen) + self.assertEqual(imp.itemsize, itemsize) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertTrue(imp.suboffsets is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.buf, snd._samples_address) + imp = Importer(snd, buftools.PyBUF_FULL_RO) + self.assertEqual(imp.ndim, ndim) + self.assertEqual(imp.format, format) + self.assertEqual(imp.len, buflen) + self.assertEqual(imp.itemsize, 2) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertTrue(imp.suboffsets is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.buf, snd._samples_address) + imp = Importer(snd, buftools.PyBUF_FULL_RO) + self.assertEqual(imp.ndim, ndim) + self.assertEqual(imp.format, format) + self.assertEqual(imp.len, buflen) + self.assertEqual(imp.itemsize, itemsize) + self.assertEqual(imp.shape, exp.shape) + self.assertEqual(imp.strides, strides) + self.assertTrue(imp.suboffsets is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.buf, snd._samples_address) + imp = Importer(snd, buftools.PyBUF_C_CONTIGUOUS) + self.assertEqual(imp.ndim, ndim) + self.assertTrue(imp.format is None) + self.assertEqual(imp.strides, strides) + imp = Importer(snd, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertEqual(imp.ndim, ndim) + self.assertTrue(imp.format is None) + self.assertEqual(imp.strides, strides) + if ndim == 1: + imp = Importer(snd, buftools.PyBUF_F_CONTIGUOUS) + self.assertEqual(imp.ndim, 1) + self.assertTrue(imp.format is None) + self.assertEqual(imp.strides, strides) + else: + self.assertRaises(BufferError, Importer, snd, buftools.PyBUF_F_CONTIGUOUS) + + def test_fadeout(self): + """Ensure pygame.mixer.fadeout() stops playback after fading out the sound.""" + if mixer.get_init() is None: + mixer.init() + sound = pygame.mixer.Sound(example_path("data/house_lo.wav")) + channel = pygame.mixer.find_channel() + channel.play(sound) + fadeout_time = 200 # milliseconds + channel.fadeout(fadeout_time) + pygame.time.wait(fadeout_time + 30) + + # Ensure the channel is no longer busy + self.assertFalse(channel.get_busy()) + + def test_find_channel(self): + # __doc__ (as of 2008-08-02) for pygame.mixer.find_channel: + + # pygame.mixer.find_channel(force=False): return Channel + # find an unused channel + mixer.init() + + filename = example_path(os.path.join("data", "house_lo.wav")) + sound = mixer.Sound(file=filename) + + num_channels = mixer.get_num_channels() + + if num_channels > 0: + found_channel = mixer.find_channel() + self.assertIsNotNone(found_channel) + + # try playing on all channels + channels = [] + for channel_id in range(0, num_channels): + channel = mixer.Channel(channel_id) + channel.play(sound) + channels.append(channel) + + # should fail without being forceful + found_channel = mixer.find_channel() + self.assertIsNone(found_channel) + + # try forcing without keyword + found_channel = mixer.find_channel(True) + self.assertIsNotNone(found_channel) + + # try forcing with keyword + found_channel = mixer.find_channel(force=True) + self.assertIsNotNone(found_channel) + + for channel in channels: + channel.stop() + found_channel = mixer.find_channel() + self.assertIsNotNone(found_channel) + + @unittest.expectedFailure + def test_pause(self): + """Ensure pygame.mixer.pause() temporarily stops playback of all sound channels.""" + if mixer.get_init() is None: + mixer.init() + sound = mixer.Sound(example_path("data/house_lo.wav")) + channel = mixer.find_channel() + channel.play(sound) + + mixer.pause() + + # TODO: this currently fails? + # Ensure the channel is paused + self.assertFalse(channel.get_busy()) + + mixer.unpause() + + # Ensure the channel is no longer paused + self.assertTrue(channel.get_busy()) + + def test_set_reserved(self): + """Ensure pygame.mixer.set_reserved() reserves the given number of channels.""" + + # pygame.mixer.set_reserved(count): return count + mixer.init() + default_num_channels = mixer.get_num_channels() + + # try reserving all the channels + result = mixer.set_reserved(default_num_channels) + self.assertEqual(result, default_num_channels) + + # try reserving all the channels + 1 + result = mixer.set_reserved(default_num_channels + 1) + # should still be default + self.assertEqual(result, default_num_channels) + + # try unreserving all + result = mixer.set_reserved(0) + # should still be default + self.assertEqual(result, 0) + + # try reserving half + result = mixer.set_reserved(int(default_num_channels / 2)) + # should still be default + self.assertEqual(result, int(default_num_channels / 2)) + + def test_stop(self): + """Stops playback of all active sound channels.""" + if mixer.get_init() is None: + mixer.init() + sound = pygame.mixer.Sound(example_path("data/house_lo.wav")) + channel = pygame.mixer.Channel(0) + channel.play(sound) + pygame.mixer.stop() + for i in range(pygame.mixer.get_num_channels()): + self.assertFalse(pygame.mixer.Channel(i).get_busy()) + + def test_get_sdl_mixer_version(self): + """Ensures get_sdl_mixer_version works correctly with no args.""" + expected_length = 3 + expected_type = tuple + expected_item_type = int + + version = pygame.mixer.get_sdl_mixer_version() + + self.assertIsInstance(version, expected_type) + self.assertEqual(len(version), expected_length) + + for item in version: + self.assertIsInstance(item, expected_item_type) + + def test_get_sdl_mixer_version__args(self): + """Ensures get_sdl_mixer_version works correctly using args.""" + expected_length = 3 + expected_type = tuple + expected_item_type = int + + for value in (True, False): + version = pygame.mixer.get_sdl_mixer_version(value) + + self.assertIsInstance(version, expected_type) + self.assertEqual(len(version), expected_length) + + for item in version: + self.assertIsInstance(item, expected_item_type) + + def test_get_sdl_mixer_version__kwargs(self): + """Ensures get_sdl_mixer_version works correctly using kwargs.""" + expected_length = 3 + expected_type = tuple + expected_item_type = int + + for value in (True, False): + version = pygame.mixer.get_sdl_mixer_version(linked=value) + + self.assertIsInstance(version, expected_type) + self.assertEqual(len(version), expected_length) + + for item in version: + self.assertIsInstance(item, expected_item_type) + + def test_get_sdl_mixer_version__invalid_args_kwargs(self): + """Ensures get_sdl_mixer_version handles invalid args and kwargs.""" + invalid_bool = InvalidBool() + + with self.assertRaises(TypeError): + version = pygame.mixer.get_sdl_mixer_version(invalid_bool) + + with self.assertRaises(TypeError): + version = pygame.mixer.get_sdl_mixer_version(linked=invalid_bool) + + def test_get_sdl_mixer_version__linked_equals_compiled(self): + """Ensures get_sdl_mixer_version's linked/compiled versions are equal.""" + linked_version = pygame.mixer.get_sdl_mixer_version(linked=True) + complied_version = pygame.mixer.get_sdl_mixer_version(linked=False) + + self.assertTupleEqual(linked_version, complied_version) + + +############################## CHANNEL CLASS TESTS ############################# + + +class ChannelTypeTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + # Initializing the mixer is slow, so minimize the times it is called. + mixer.init() + + @classmethod + def tearDownClass(cls): + mixer.quit() + + def setUp(cls): + # This makes sure the mixer is always initialized before each test (in + # case a test calls pygame.mixer.quit()). + if mixer.get_init() is None: + mixer.init() + + def test_channel(self): + """Ensure Channel() creation works.""" + channel = mixer.Channel(0) + + self.assertIsInstance(channel, mixer.ChannelType) + self.assertEqual(channel.__class__.__name__, "Channel") + + def test_channel__without_arg(self): + """Ensure exception for Channel() creation with no argument.""" + with self.assertRaises(TypeError): + mixer.Channel() + + def test_channel__invalid_id(self): + """Ensure exception for Channel() creation with an invalid id.""" + with self.assertRaises(IndexError): + mixer.Channel(-1) + + def test_channel__before_init(self): + """Ensure exception for Channel() creation with non-init mixer.""" + mixer.quit() + + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + mixer.Channel(0) + + def test_fadeout(self): + """Ensure Channel.fadeout() stops playback after fading out.""" + channel = mixer.Channel(0) + sound = mixer.Sound(example_path("data/house_lo.wav")) + channel.play(sound) + + fadeout_time = 1000 + channel.fadeout(fadeout_time) + + # Wait for the fadeout to complete. + pygame.time.wait(fadeout_time + 100) + + self.assertFalse(channel.get_busy()) + + def test_get_busy(self): + """Ensure an idle channel's busy state is correct.""" + expected_busy = False + channel = mixer.Channel(0) + + busy = channel.get_busy() + + self.assertEqual(busy, expected_busy) + + def test_get_busy__active(self): + """Ensure an active channel's busy state is correct.""" + channel = mixer.Channel(0) + sound = mixer.Sound(example_path("data/house_lo.wav")) + channel.play(sound) + + self.assertTrue(channel.get_busy()) + + def todo_test_get_endevent(self): + # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.get_endevent: + + # Channel.get_endevent(): return type + # get the event a channel sends when playback stops + # + # Returns the event type to be sent every time the Channel finishes + # playback of a Sound. If there is no endevent the function returns + # pygame.NOEVENT. + # + + self.fail() + + def test_get_queue(self): + """Ensure Channel.get_queue() returns any queued Sound.""" + channel = mixer.Channel(0) + frequency, format, channels = mixer.get_init() + sound_length_in_ms = 200 + sound_length_in_ms_2 = 400 + bytes_per_ms = int((frequency / 1000) * channels * (abs(format) // 8)) + sound1 = mixer.Sound(b"\x00" * int(sound_length_in_ms * bytes_per_ms)) + sound2 = mixer.Sound(b"\x00" * (int(sound_length_in_ms_2 * bytes_per_ms))) + + channel.play(sound1) + channel.queue(sound2) + + # Ensure the second queued sound is returned. + self.assertEqual(channel.get_queue().get_length(), sound2.get_length()) + # TODO: should sound1.stop() clear it from the queue too? Currently it doesn't. + pygame.time.wait(sound_length_in_ms + 100) + + # TODO: I think here there should be nothing queued. + # Because the sound should be off the queue. Currently it doesn't do this. + # self.assertIsNone(channel.get_queue()) + + # the second sound is now playing + self.assertEqual(channel.get_sound().get_length(), sound2.get_length()) + pygame.time.wait((sound_length_in_ms_2) + 100) + + # Now there is nothing on the queue. + self.assertIsNone(channel.get_queue()) + + def test_get_sound(self): + """Ensure Channel.get_sound() returns the currently playing Sound.""" + channel = mixer.Channel(0) + sound = mixer.Sound(example_path("data/house_lo.wav")) + channel.play(sound) + + # Ensure the correct Sound object is returned. + got_sound = channel.get_sound() + self.assertEqual(got_sound, sound) + + # Stop the sound and ensure None is returned. + channel.stop() + got_sound = channel.get_sound() + self.assertIsNone(got_sound) + + def test_get_volume(self): + """Ensure a channel's volume can be retrieved.""" + expected_volume = 1.0 # default + channel = mixer.Channel(0) + + volume = channel.get_volume() + + self.assertAlmostEqual(volume, expected_volume) + + def test_pause_unpause(self): + """ + Test if the Channel can be paused and unpaused. + """ + if mixer.get_init() is None: + mixer.init() + sound = pygame.mixer.Sound(example_path("data/house_lo.wav")) + channel = sound.play() + channel.pause() + self.assertTrue( + channel.get_busy(), msg="Channel should be paused but it's not." + ) + channel.unpause() + self.assertTrue( + channel.get_busy(), msg="Channel should be unpaused but it's not." + ) + sound.stop() + + def test_pause_unpause__before_init(self): + """ + Ensure exception for Channel.pause() with non-init mixer. + """ + sound = mixer.Sound(example_path("data/house_lo.wav")) + channel = sound.play() + mixer.quit() + + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + channel.pause() + + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + channel.unpause() + + def todo_test_queue(self): + # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.queue: + + # Channel.queue(Sound): return None + # queue a Sound object to follow the current + # + # When a Sound is queued on a Channel, it will begin playing + # immediately after the current Sound is finished. Each channel can + # only have a single Sound queued at a time. The queued Sound will + # only play if the current playback finished automatically. It is + # cleared on any other call to Channel.stop() or Channel.play(). + # + # If there is no sound actively playing on the Channel then the Sound + # will begin playing immediately. + # + + self.fail() + + def test_stop(self): + # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.stop: + + # Channel.stop(): return None + # stop playback on a Channel + # + # Stop sound playback on a channel. After playback is stopped the + # channel becomes available for new Sounds to play on it. + # + + channel = mixer.Channel(0) + sound = mixer.Sound(example_path("data/house_lo.wav")) + + # simple check + channel.play(sound) + channel.stop() + self.assertFalse(channel.get_busy()) + # check that queued sounds also stop + channel.queue(sound) + channel.stop() + self.assertFalse(channel.get_busy()) + # check that new sounds can be played + channel.play(sound) + self.assertTrue(channel.get_busy()) + + +class ChannelSetVolumeTest(unittest.TestCase): + def setUp(self): + mixer.init() + self.channel = pygame.mixer.Channel(0) + self.sound = pygame.mixer.Sound(example_path("data/boom.wav")) + + def tearDown(self): + mixer.quit() + + def test_set_volume_with_one_argument(self): + self.channel.play(self.sound) + self.channel.set_volume(0.5) + self.assertEqual(self.channel.get_volume(), 0.5) + + @unittest.expectedFailure + def test_set_volume_with_two_arguments(self): + # TODO: why doesn't this work? Seems to ignore stereo setting. + # https://www.pygame.org/docs/ref/mixer.html#pygame.mixer.Channel.set_volume + self.channel.play(self.sound) + self.channel.set_volume(0.3, 0.7) + self.assertEqual(self.channel.get_volume(), (0.3, 0.7)) + + +class ChannelEndEventTest(unittest.TestCase): + def setUp(self): + pygame.display.init() + pygame.display.set_mode((40, 40)) + if mixer.get_init() is None: + mixer.init() + + def tearDown(self): + pygame.display.quit() + mixer.quit() + + def test_get_endevent(self): + """Ensure Channel.get_endevent() returns the correct event type.""" + channel = mixer.Channel(0) + sound = mixer.Sound(example_path("data/house_lo.wav")) + channel.play(sound) + + # Set the end event for the channel. + END_EVENT = pygame.USEREVENT + 1 + channel.set_endevent(END_EVENT) + got_end_event = channel.get_endevent() + self.assertEqual(got_end_event, END_EVENT) + + # Wait for the sound to finish playing. + channel.stop() + while channel.get_busy(): + pygame.time.wait(10) + + # Check that the end event was sent. + events = pygame.event.get(got_end_event) + self.assertTrue(len(events) > 0) + + +############################### SOUND CLASS TESTS ############################## + + +class TestSoundPlay(unittest.TestCase): + def setUp(self): + mixer.init() + self.filename = example_path(os.path.join("data", "house_lo.wav")) + self.sound = mixer.Sound(file=self.filename) + + def tearDown(self): + mixer.quit() + + def test_play_once(self): + """Test playing a sound once.""" + channel = self.sound.play() + self.assertIsInstance(channel, pygame.mixer.Channel) + self.assertTrue(channel.get_busy()) + + def test_play_multiple_times(self): + """Test playing a sound multiple times.""" + + # create a sound 100ms long + frequency, format, channels = mixer.get_init() + sound_length_in_ms = 100 + bytes_per_ms = int((frequency / 1000) * channels * (abs(format) // 8)) + sound = mixer.Sound(b"\x00" * int(sound_length_in_ms * bytes_per_ms)) + + self.assertAlmostEqual( + sound.get_length(), sound_length_in_ms / 1000.0, places=2 + ) + + num_loops = 5 + channel = sound.play(loops=num_loops) + self.assertIsInstance(channel, pygame.mixer.Channel) + + # the sound should be playing + pygame.time.wait((sound_length_in_ms * num_loops) - 100) + self.assertTrue(channel.get_busy()) + + # the sound should not be playing anymore + pygame.time.wait(sound_length_in_ms + 200) + self.assertFalse(channel.get_busy()) + + def test_play_indefinitely(self): + """Test playing a sound indefinitely.""" + frequency, format, channels = mixer.get_init() + sound_length_in_ms = 100 + bytes_per_ms = int((frequency / 1000) * channels * (abs(format) // 8)) + sound = mixer.Sound(b"\x00" * int(sound_length_in_ms * bytes_per_ms)) + + channel = sound.play(loops=-1) + self.assertIsInstance(channel, pygame.mixer.Channel) + + # we can't wait forever... so we wait 2 loops + for _ in range(2): + self.assertTrue(channel.get_busy()) + pygame.time.wait(sound_length_in_ms) + + def test_play_with_maxtime(self): + """Test playing a sound with maxtime.""" + channel = self.sound.play(maxtime=200) + self.assertIsInstance(channel, pygame.mixer.Channel) + self.assertTrue(channel.get_busy()) + pygame.time.wait(200 + 50) + self.assertFalse(channel.get_busy()) + + def test_play_with_fade_ms(self): + """Test playing a sound with fade_ms.""" + channel = self.sound.play(fade_ms=500) + self.assertIsInstance(channel, pygame.mixer.Channel) + self.assertTrue(channel.get_busy()) + pygame.time.wait(250) + + self.assertGreater(channel.get_volume(), 0.3) + self.assertLess(channel.get_volume(), 0.80) + + pygame.time.wait(300) + self.assertEqual(channel.get_volume(), 1.0) + + def test_play_with_invalid_loops(self): + """Test playing a sound with invalid loops.""" + with self.assertRaises(TypeError): + self.sound.play(loops="invalid") + + def test_play_with_invalid_maxtime(self): + """Test playing a sound with invalid maxtime.""" + with self.assertRaises(TypeError): + self.sound.play(maxtime="invalid") + + def test_play_with_invalid_fade_ms(self): + """Test playing a sound with invalid fade_ms.""" + with self.assertRaises(TypeError): + self.sound.play(fade_ms="invalid") + + +class SoundTypeTest(unittest.TestCase): + @classmethod + def tearDownClass(cls): + mixer.quit() + + def setUp(cls): + # This makes sure the mixer is always initialized before each test (in + # case a test calls pygame.mixer.quit()). + if mixer.get_init() is None: + mixer.init() + + # See MixerModuleTest's methods test_sound_args(), test_sound_unicode(), + # and test_array_keyword() for additional testing of Sound() creation. + def test_sound(self): + """Ensure Sound() creation with a filename works.""" + filename = example_path(os.path.join("data", "house_lo.wav")) + sound1 = mixer.Sound(filename) + sound2 = mixer.Sound(file=filename) + + self.assertIsInstance(sound1, mixer.Sound) + self.assertIsInstance(sound2, mixer.Sound) + + def test_sound__from_file_object(self): + """Ensure Sound() creation with a file object works.""" + filename = example_path(os.path.join("data", "house_lo.wav")) + + # Using 'with' ensures the file is closed even if test fails. + with open(filename, "rb") as file_obj: + sound = mixer.Sound(file_obj) + + self.assertIsInstance(sound, mixer.Sound) + + def test_sound__from_sound_object(self): + """Ensure Sound() creation with a Sound() object works.""" + filename = example_path(os.path.join("data", "house_lo.wav")) + sound_obj = mixer.Sound(file=filename) + + sound = mixer.Sound(sound_obj) + + self.assertIsInstance(sound, mixer.Sound) + + def test_sound__from_pathlib(self): + """Ensure Sound() creation with a pathlib.Path object works.""" + path = pathlib.Path(example_path(os.path.join("data", "house_lo.wav"))) + sound1 = mixer.Sound(path) + sound2 = mixer.Sound(file=path) + self.assertIsInstance(sound1, mixer.Sound) + self.assertIsInstance(sound2, mixer.Sound) + + def todo_test_sound__from_buffer(self): + """Ensure Sound() creation with a buffer works.""" + self.fail() + + def todo_test_sound__from_array(self): + """Ensure Sound() creation with an array works.""" + self.fail() + + def test_sound__without_arg(self): + """Ensure exception raised for Sound() creation with no argument.""" + with self.assertRaises(TypeError): + mixer.Sound() + + def test_sound__before_init(self): + """Ensure exception raised for Sound() creation with non-init mixer.""" + mixer.quit() + filename = example_path(os.path.join("data", "house_lo.wav")) + + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + mixer.Sound(file=filename) + + @unittest.skipIf(IS_PYPY, "pypy skip") + def test_samples_address(self): + """Test the _samples_address getter.""" + try: + from ctypes import pythonapi, c_void_p, py_object + + Bytes_FromString = pythonapi.PyBytes_FromString + + Bytes_FromString.restype = c_void_p + Bytes_FromString.argtypes = [py_object] + samples = b"abcdefgh" # keep byte size a multiple of 4 + sample_bytes = Bytes_FromString(samples) + + snd = mixer.Sound(buffer=samples) + + self.assertNotEqual(snd._samples_address, sample_bytes) + finally: + pygame.mixer.quit() + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + snd._samples_address + + def test_get_length(self): + """Tests if get_length returns a correct length.""" + try: + for size in SIZES: + pygame.mixer.quit() + pygame.mixer.init(size=size) + filename = example_path(os.path.join("data", "punch.wav")) + sound = mixer.Sound(file=filename) + # The sound data is in the mixer output format. So dividing the + # length of the raw sound data by the mixer settings gives + # the expected length of the sound. + sound_bytes = sound.get_raw() + mix_freq, mix_bits, mix_channels = pygame.mixer.get_init() + mix_bytes = abs(mix_bits) / 8 + expected_length = ( + float(len(sound_bytes)) / mix_freq / mix_bytes / mix_channels + ) + self.assertAlmostEqual(expected_length, sound.get_length()) + finally: + pygame.mixer.quit() + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + sound.get_length() + + def test_get_num_channels(self): + """ + Tests if Sound.get_num_channels returns the correct number + of channels playing a specific sound. + """ + try: + filename = example_path(os.path.join("data", "house_lo.wav")) + sound = mixer.Sound(file=filename) + + self.assertEqual(sound.get_num_channels(), 0) + sound.play() + self.assertEqual(sound.get_num_channels(), 1) + sound.play() + self.assertEqual(sound.get_num_channels(), 2) + sound.stop() + self.assertEqual(sound.get_num_channels(), 0) + finally: + pygame.mixer.quit() + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + sound.get_num_channels() + + def test_get_volume(self): + """Ensure a sound's volume can be retrieved.""" + try: + expected_volume = 1.0 # default + filename = example_path(os.path.join("data", "house_lo.wav")) + sound = mixer.Sound(file=filename) + + volume = sound.get_volume() + + self.assertAlmostEqual(volume, expected_volume) + finally: + pygame.mixer.quit() + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + sound.get_volume() + + def test_get_volume__while_playing(self): + """Ensure a sound's volume can be retrieved while playing.""" + try: + expected_volume = 1.0 # default + filename = example_path(os.path.join("data", "house_lo.wav")) + sound = mixer.Sound(file=filename) + sound.play(-1) + + volume = sound.get_volume() + + self.assertAlmostEqual(volume, expected_volume) + finally: + pygame.mixer.quit() + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + sound.get_volume() + + def test_set_volume(self): + """Ensure a sound's volume can be set.""" + try: + float_delta = 1.0 / 128 # SDL volume range is 0 to 128 + filename = example_path(os.path.join("data", "house_lo.wav")) + sound = mixer.Sound(file=filename) + current_volume = sound.get_volume() + + # (volume_set_value : expected_volume) + volumes = ( + (-1, current_volume), # value < 0 won't change volume + (0, 0.0), + (0.01, 0.01), + (0.1, 0.1), + (0.5, 0.5), + (0.9, 0.9), + (0.99, 0.99), + (1, 1.0), + (1.1, 1.0), + (2.0, 1.0), + ) + + for volume_set_value, expected_volume in volumes: + sound.set_volume(volume_set_value) + + self.assertAlmostEqual( + sound.get_volume(), expected_volume, delta=float_delta + ) + finally: + pygame.mixer.quit() + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + sound.set_volume(1) + + def test_set_volume__while_playing(self): + """Ensure a sound's volume can be set while playing.""" + try: + float_delta = 1.0 / 128 # SDL volume range is 0 to 128 + filename = example_path(os.path.join("data", "house_lo.wav")) + sound = mixer.Sound(file=filename) + current_volume = sound.get_volume() + + # (volume_set_value : expected_volume) + volumes = ( + (-1, current_volume), # value < 0 won't change volume + (0, 0.0), + (0.01, 0.01), + (0.1, 0.1), + (0.5, 0.5), + (0.9, 0.9), + (0.99, 0.99), + (1, 1.0), + (1.1, 1.0), + (2.0, 1.0), + ) + + sound.play(loops=-1) + for volume_set_value, expected_volume in volumes: + sound.set_volume(volume_set_value) + + self.assertAlmostEqual( + sound.get_volume(), expected_volume, delta=float_delta + ) + finally: + pygame.mixer.quit() + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + sound.set_volume(1) + + def test_stop(self): + """Ensure stop can be called while not playing a sound.""" + try: + expected_channels = 0 + filename = example_path(os.path.join("data", "house_lo.wav")) + sound = mixer.Sound(file=filename) + + sound.stop() + + self.assertEqual(sound.get_num_channels(), expected_channels) + finally: + pygame.mixer.quit() + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + sound.stop() + + def test_stop__while_playing(self): + """Ensure stop stops a playing sound.""" + try: + expected_channels = 0 + filename = example_path(os.path.join("data", "house_lo.wav")) + sound = mixer.Sound(file=filename) + + sound.play(-1) + sound.stop() + + self.assertEqual(sound.get_num_channels(), expected_channels) + finally: + pygame.mixer.quit() + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + sound.stop() + + def test_get_raw(self): + """Ensure get_raw returns the correct bytestring.""" + try: + samples = b"abcdefgh" # keep byte size a multiple of 4 + snd = mixer.Sound(buffer=samples) + + raw = snd.get_raw() + + self.assertIsInstance(raw, bytes) + self.assertEqual(raw, samples) + finally: + pygame.mixer.quit() + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + snd.get_raw() + + def test_correct_subclassing(self): + class CorrectSublass(mixer.Sound): + def __init__(self, file): + super().__init__(file=file) + + filename = example_path(os.path.join("data", "house_lo.wav")) + correct = CorrectSublass(filename) + + try: + correct.get_volume() + except Exception: + self.fail("This should not raise an exception.") + + def test_incorrect_subclassing(self): + class IncorrectSuclass(mixer.Sound): + def __init__(self): + pass + + incorrect = IncorrectSuclass() + + self.assertRaises(RuntimeError, incorrect.get_volume) + + +class TestSoundFadeout(unittest.TestCase): + def setUp(self): + if mixer.get_init() is None: + pygame.mixer.init() + + def tearDown(self): + pygame.mixer.quit() + + def test_fadeout_with_valid_time(self): + """Tests if fadeout stops sound playback after fading it out over the time argument in milliseconds.""" + filename = example_path(os.path.join("data", "punch.wav")) + sound = mixer.Sound(file=filename) + channel = sound.play() + channel.fadeout(1000) + pygame.time.wait(2000) + self.assertFalse(channel.get_busy()) + + # TODO: this fails. + # def test_fadeout_with_zero_time(self): + # """Tests if fadeout stops sound playback immediately when time argument is zero.""" + # filename = example_path(os.path.join("data", "punch.wav")) + # sound = mixer.Sound(file=filename) + # channel = sound.play() + # channel.fadeout(0) + # self.assertFalse(channel.get_busy()) + + # TODO: this fails. + # def test_fadeout_with_negative_time(self): + # """Tests if fadeout stops sound playback immediately when time argument is negative.""" + # filename = example_path(os.path.join("data", "punch.wav")) + # sound = mixer.Sound(file=filename) + # channel = sound.play() + # channel.fadeout(-1000) + # self.assertFalse(channel.get_busy()) + + # TODO: What should happen here? + # def test_fadeout_with_large_time(self): + # """Tests if fadeout stops sound playback after fading it out over the time argument in milliseconds, even if time is larger than the sound length.""" + # filename = example_path(os.path.join("data", "punch.wav")) + # sound = mixer.Sound(file=filename) + # channel = sound.play() + # channel.fadeout(...?) + # pygame.time.wait(...?) + # self.assertFalse(channel.get_busy()) + + +class TestGetBusy(unittest.TestCase): + """Test pygame.mixer.get_busy. + + |tags:slow| + """ + + def setUp(self): + pygame.mixer.init() + + def tearDown(self): + pygame.mixer.quit() + + def test_no_sound_playing(self): + """ + Test that get_busy returns False when no sound is playing. + """ + self.assertFalse(pygame.mixer.get_busy()) + + def test_one_sound_playing(self): + """ + Test that get_busy returns True when one sound is playing. + """ + sound = pygame.mixer.Sound(example_path("data/house_lo.wav")) + sound.play() + time.sleep(0.2) + self.assertTrue(pygame.mixer.get_busy()) + sound.stop() + + def test_multiple_sounds_playing(self): + """ + Test that get_busy returns True when multiple sounds are playing. + """ + sound1 = pygame.mixer.Sound(example_path("data/house_lo.wav")) + sound2 = pygame.mixer.Sound(example_path("data/house_lo.wav")) + sound1.play() + sound2.play() + time.sleep(0.2) + self.assertTrue(pygame.mixer.get_busy()) + sound1.stop() + sound2.stop() + + def test_all_sounds_stopped(self): + """ + Test that get_busy returns False when all sounds are stopped. + """ + sound1 = pygame.mixer.Sound(example_path("data/house_lo.wav")) + sound2 = pygame.mixer.Sound(example_path("data/house_lo.wav")) + sound1.play() + sound2.play() + time.sleep(0.2) + sound1.stop() + sound2.stop() + time.sleep(0.2) + self.assertFalse(pygame.mixer.get_busy()) + + def test_all_sounds_stopped_with_fadeout(self): + """ + Test that get_busy returns False when all sounds are stopped with + fadeout. + """ + sound1 = pygame.mixer.Sound(example_path("data/house_lo.wav")) + sound2 = pygame.mixer.Sound(example_path("data/house_lo.wav")) + sound1.play() + sound2.play() + time.sleep(0.2) + sound1.fadeout(100) + sound2.fadeout(100) + time.sleep(0.3) + self.assertFalse(pygame.mixer.get_busy()) + + def test_sound_fading_out(self): + """Tests that get_busy() returns True when a sound is fading out""" + sound = pygame.mixer.Sound(example_path("data/house_lo.wav")) + sound.play(fade_ms=1000) + time.sleep(1.1) + self.assertTrue(pygame.mixer.get_busy()) + sound.stop() + + +##################################### MAIN ##################################### + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/mouse_test.py b/.venv/Lib/site-packages/pygame/tests/mouse_test.py new file mode 100644 index 00000000..dc3d5789 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/mouse_test.py @@ -0,0 +1,348 @@ +import unittest +import os +import platform +import warnings +import pygame + + +DARWIN = "Darwin" in platform.platform() + + +class MouseTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + # The display needs to be initialized for mouse functions. + pygame.display.init() + + @classmethod + def tearDownClass(cls): + pygame.display.quit() + + +class MouseModuleInteractiveTest(MouseTests): + __tags__ = ["interactive"] + + def test_set_pos(self): + """Ensures set_pos works correctly. + Requires tester to move the mouse to be on the window. + """ + pygame.display.set_mode((500, 500)) + pygame.event.get() # Pump event queue to make window get focus on macos. + + if not pygame.mouse.get_focused(): + # The window needs to be focused for the mouse.set_pos to work on macos. + return + clock = pygame.time.Clock() + + expected_pos = ((10, 0), (0, 0), (499, 0), (499, 499), (341, 143), (94, 49)) + + for x, y in expected_pos: + pygame.mouse.set_pos(x, y) + pygame.event.get() + found_pos = pygame.mouse.get_pos() + + clock.tick() + time_passed = 0.0 + ready_to_test = False + + while not ready_to_test and time_passed <= 1000.0: # Avoid endless loop + time_passed += clock.tick() + for event in pygame.event.get(): + if event.type == pygame.MOUSEMOTION: + ready_to_test = True + + self.assertEqual(found_pos, (x, y)) + + +class MouseModuleTest(MouseTests): + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER", "") == "dummy", + "Cursors not supported on headless test machines", + ) + def test_get_cursor(self): + """Ensures get_cursor works correctly.""" + + # error should be raised when the display is uninitialized + with self.assertRaises(pygame.error): + pygame.display.quit() + pygame.mouse.get_cursor() + + pygame.display.init() + + size = (8, 8) + hotspot = (0, 0) + xormask = (0, 96, 120, 126, 112, 96, 0, 0) + andmask = (224, 240, 254, 255, 254, 240, 96, 0) + + expected_length = 4 + expected_cursor = pygame.cursors.Cursor(size, hotspot, xormask, andmask) + pygame.mouse.set_cursor(expected_cursor) + + try: + cursor = pygame.mouse.get_cursor() + + self.assertIsInstance(cursor, pygame.cursors.Cursor) + self.assertEqual(len(cursor), expected_length) + + for info in cursor: + self.assertIsInstance(info, tuple) + + pygame.mouse.set_cursor(size, hotspot, xormask, andmask) + self.assertEqual(pygame.mouse.get_cursor(), expected_cursor) + + # SDLError should be raised when the mouse cursor is NULL + except pygame.error: + with self.assertRaises(pygame.error): + pygame.mouse.get_cursor() + + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER", "") == "dummy", + "mouse.set_system_cursor only available in SDL2", + ) + def test_set_system_cursor(self): + """Ensures set_system_cursor works correctly.""" + + with warnings.catch_warnings(record=True) as w: + """From Pygame 2.0.1, set_system_cursor() should raise a deprecation warning""" + # Cause all warnings to always be triggered. + warnings.simplefilter("always") + + # Error should be raised when the display is uninitialized + with self.assertRaises(pygame.error): + pygame.display.quit() + pygame.mouse.set_system_cursor(pygame.SYSTEM_CURSOR_HAND) + + pygame.display.init() + + # TypeError raised when PyArg_ParseTuple fails to parse parameters + with self.assertRaises(TypeError): + pygame.mouse.set_system_cursor("b") + with self.assertRaises(TypeError): + pygame.mouse.set_system_cursor(None) + with self.assertRaises(TypeError): + pygame.mouse.set_system_cursor((8, 8), (0, 0)) + + # Right type, invalid value + with self.assertRaises(pygame.error): + pygame.mouse.set_system_cursor(2000) + + # Working as intended + self.assertEqual( + pygame.mouse.set_system_cursor(pygame.SYSTEM_CURSOR_ARROW), None + ) + + # Making sure the warnings are working properly + self.assertEqual(len(w), 6) + self.assertTrue( + all([issubclass(warn.category, DeprecationWarning) for warn in w]) + ) + + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER", "") == "dummy", + "Cursors not supported on headless test machines", + ) + def test_set_cursor(self): + """Ensures set_cursor works correctly.""" + + # Bitmap cursor information + size = (8, 8) + hotspot = (0, 0) + xormask = (0, 126, 64, 64, 32, 16, 0, 0) + andmask = (254, 255, 254, 112, 56, 28, 12, 0) + bitmap_cursor = pygame.cursors.Cursor(size, hotspot, xormask, andmask) + + # System cursor information + constant = pygame.SYSTEM_CURSOR_ARROW + system_cursor = pygame.cursors.Cursor(constant) + + # Color cursor information (also uses hotspot variable from Bitmap cursor info) + surface = pygame.Surface((10, 10)) + color_cursor = pygame.cursors.Cursor(hotspot, surface) + + pygame.display.quit() + + # Bitmap: Error should be raised when the display is uninitialized + with self.assertRaises(pygame.error): + pygame.mouse.set_cursor(bitmap_cursor) + + # System: Error should be raised when the display is uninitialized + with self.assertRaises(pygame.error): + pygame.mouse.set_cursor(system_cursor) + + # Color: Error should be raised when the display is uninitialized + with self.assertRaises(pygame.error): + pygame.mouse.set_cursor(color_cursor) + + pygame.display.init() + + # Bitmap: TypeError raised when PyArg_ParseTuple fails to parse parameters + with self.assertRaises(TypeError): + pygame.mouse.set_cursor(("w", "h"), hotspot, xormask, andmask) + with self.assertRaises(TypeError): + pygame.mouse.set_cursor(size, ("0", "0"), xormask, andmask) + with self.assertRaises(TypeError): + pygame.mouse.set_cursor(size, ("x", "y", "z"), xormask, andmask) + + # Bitmap: TypeError raised when either mask is not a sequence + with self.assertRaises(TypeError): + pygame.mouse.set_cursor(size, hotspot, 12345678, andmask) + with self.assertRaises(TypeError): + pygame.mouse.set_cursor(size, hotspot, xormask, 12345678) + + # Bitmap: TypeError raised when element of mask is not an integer + with self.assertRaises(TypeError): + pygame.mouse.set_cursor(size, hotspot, "00000000", andmask) + with self.assertRaises(TypeError): + pygame.mouse.set_cursor(size, hotspot, xormask, (2, [0], 4, 0, 0, 8, 0, 1)) + + # Bitmap: ValueError raised when width not divisible by 8 + with self.assertRaises(ValueError): + pygame.mouse.set_cursor((3, 8), hotspot, xormask, andmask) + + # Bitmap: ValueError raised when length of either mask != width * height / 8 + with self.assertRaises(ValueError): + pygame.mouse.set_cursor((16, 2), hotspot, (128, 64, 32), andmask) + with self.assertRaises(ValueError): + pygame.mouse.set_cursor((16, 2), hotspot, xormask, (192, 96, 48, 0, 1)) + + # Bitmap: Working as intended + self.assertEqual( + pygame.mouse.set_cursor((16, 1), hotspot, (8, 0), (0, 192)), None + ) + pygame.mouse.set_cursor(size, hotspot, xormask, andmask) + self.assertEqual(pygame.mouse.get_cursor(), bitmap_cursor) + + # Bitmap: Working as intended + lists + masks with no references + pygame.mouse.set_cursor(size, hotspot, list(xormask), list(andmask)) + self.assertEqual(pygame.mouse.get_cursor(), bitmap_cursor) + + # System: TypeError raised when constant is invalid + with self.assertRaises(TypeError): + pygame.mouse.set_cursor(-50021232) + with self.assertRaises(TypeError): + pygame.mouse.set_cursor("yellow") + + # System: Working as intended + self.assertEqual(pygame.mouse.set_cursor(constant), None) + pygame.mouse.set_cursor(constant) + self.assertEqual(pygame.mouse.get_cursor(), system_cursor) + pygame.mouse.set_cursor(system_cursor) + self.assertEqual(pygame.mouse.get_cursor(), system_cursor) + + # Color: TypeError raised with invalid parameters + with self.assertRaises(TypeError): + pygame.mouse.set_cursor(("x", "y"), surface) + with self.assertRaises(TypeError): + pygame.mouse.set_cursor(hotspot, "not_a_surface") + + # Color: Working as intended + self.assertEqual(pygame.mouse.set_cursor(hotspot, surface), None) + pygame.mouse.set_cursor(hotspot, surface) + self.assertEqual(pygame.mouse.get_cursor(), color_cursor) + pygame.mouse.set_cursor(color_cursor) + self.assertEqual(pygame.mouse.get_cursor(), color_cursor) + + # Color: Working as intended + Surface with no references is returned okay + pygame.mouse.set_cursor((0, 0), pygame.Surface((20, 20))) + cursor = pygame.mouse.get_cursor() + self.assertEqual(cursor.type, "color") + self.assertEqual(cursor.data[0], (0, 0)) + self.assertEqual(cursor.data[1].get_size(), (20, 20)) + + def test_get_focused(self): + """Ensures get_focused returns the correct type.""" + focused = pygame.mouse.get_focused() + + self.assertIsInstance(focused, int) + + def test_get_pressed(self): + """Ensures get_pressed returns the correct types.""" + expected_length = 3 + buttons_pressed = pygame.mouse.get_pressed() + self.assertIsInstance(buttons_pressed, tuple) + self.assertEqual(len(buttons_pressed), expected_length) + for value in buttons_pressed: + self.assertIsInstance(value, bool) + + expected_length = 5 + buttons_pressed = pygame.mouse.get_pressed(num_buttons=5) + self.assertIsInstance(buttons_pressed, tuple) + self.assertEqual(len(buttons_pressed), expected_length) + for value in buttons_pressed: + self.assertIsInstance(value, bool) + + expected_length = 3 + buttons_pressed = pygame.mouse.get_pressed(3) + self.assertIsInstance(buttons_pressed, tuple) + self.assertEqual(len(buttons_pressed), expected_length) + for value in buttons_pressed: + self.assertIsInstance(value, bool) + + expected_length = 5 + buttons_pressed = pygame.mouse.get_pressed(5) + self.assertIsInstance(buttons_pressed, tuple) + self.assertEqual(len(buttons_pressed), expected_length) + for value in buttons_pressed: + self.assertIsInstance(value, bool) + + with self.assertRaises(ValueError): + pygame.mouse.get_pressed(4) + + def test_get_pos(self): + """Ensures get_pos returns the correct types.""" + expected_length = 2 + + pos = pygame.mouse.get_pos() + + self.assertIsInstance(pos, tuple) + self.assertEqual(len(pos), expected_length) + for value in pos: + self.assertIsInstance(value, int) + + def test_set_pos__invalid_pos(self): + """Ensures set_pos handles invalid positions correctly.""" + for invalid_pos in ((1,), [1, 2, 3], 1, "1", (1, "1"), []): + with self.assertRaises(TypeError): + pygame.mouse.set_pos(invalid_pos) + + def test_get_rel(self): + """Ensures get_rel returns the correct types.""" + expected_length = 2 + + rel = pygame.mouse.get_rel() + + self.assertIsInstance(rel, tuple) + self.assertEqual(len(rel), expected_length) + for value in rel: + self.assertIsInstance(value, int) + + def test_get_visible(self): + """Ensures get_visible works correctly.""" + for expected_value in (False, True): + pygame.mouse.set_visible(expected_value) + + visible = pygame.mouse.get_visible() + + self.assertEqual(visible, expected_value) + + def test_set_visible(self): + """Ensures set_visible returns the correct values.""" + # Set to a known state. + pygame.mouse.set_visible(True) + + for expected_visible in (False, True): + prev_visible = pygame.mouse.set_visible(expected_visible) + + self.assertEqual(prev_visible, not expected_visible) + + def test_set_visible__invalid_value(self): + """Ensures set_visible handles invalid positions correctly.""" + for invalid_value in ((1,), [1, 2, 3], 1.1, "1", (1, "1"), []): + with self.assertRaises(TypeError): + prev_visible = pygame.mouse.set_visible(invalid_value) + + +################################################################################ + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/pixelarray_test.py b/.venv/Lib/site-packages/pygame/tests/pixelarray_test.py new file mode 100644 index 00000000..7b1cf420 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/pixelarray_test.py @@ -0,0 +1,1667 @@ +import gc +import operator +import platform +import sys +import unittest +import weakref +from functools import reduce + +from pygame.tests.test_utils import SurfaceSubclass + +try: + from pygame.tests.test_utils import arrinter +except NameError: + pass + +import pygame + + +IS_PYPY = "PyPy" == platform.python_implementation() + + +class TestMixin: + def assert_surfaces_equal(self, s1, s2, msg=None): + """Checks if two surfaces are equal in size and color.""" + w, h = s1.get_size() + + self.assertTupleEqual((w, h), s2.get_size(), msg) + + msg = "" if msg is None else f"{msg}, " + msg += f"size: ({w}, {h})" + + for x in range(w): + for y in range(h): + self.assertEqual( + s1.get_at((x, y)), + s2.get_at((x, y)), + f"{msg}, position: ({x}, {y})", + ) + + def assert_surface_filled(self, surface, expected_color, msg=None): + """Checks if the surface is filled with the given color.""" + width, height = surface.get_size() + + surface.lock() # Lock for possible speed up. + for pos in ((x, y) for y in range(height) for x in range(width)): + self.assertEqual(surface.get_at(pos), expected_color, msg) + surface.unlock() + + +@unittest.skipIf(IS_PYPY, "pypy having issues") +class PixelArrayTypeTest(unittest.TestCase, TestMixin): + def test_compare(self): + # __doc__ (as of 2008-06-25) for pygame.pixelarray.PixelArray.compare: + + # PixelArray.compare (array, distance=0, weights=(0.299, 0.587, 0.114)): Return PixelArray + # Compares the PixelArray with another one. + + w = 10 + h = 20 + size = w, h + sf = pygame.Surface(size, 0, 32) + ar = pygame.PixelArray(sf) + sf2 = pygame.Surface(size, 0, 32) + self.assertRaises(TypeError, ar.compare, sf2) + ar2 = pygame.PixelArray(sf2) + ar3 = ar.compare(ar2) + self.assertTrue(isinstance(ar3, pygame.PixelArray)) + self.assertEqual(ar3.shape, size) + sf2.fill(pygame.Color("white")) + self.assert_surfaces_equal(sf2, ar3.surface) + del ar3 + r = pygame.Rect(2, 5, 6, 13) + sf.fill(pygame.Color("blue"), r) + sf2.fill(pygame.Color("red")) + sf2.fill(pygame.Color("blue"), r) + ar3 = ar.compare(ar2) + sf.fill(pygame.Color("white"), r) + self.assert_surfaces_equal(sf, ar3.surface) + + # FINISH ME! + # Test other bit depths, slices, and distance != 0. + + def test_compare__same_colors_within_distance(self): + """Ensures compare works correctly with same colored surfaces.""" + size = (3, 5) + pixelarray_result_color = pygame.Color("white") + surface_color = (127, 127, 127, 255) + + for depth in (8, 16, 24, 32): + expected_pixelarray_surface = pygame.Surface(size, depth=depth) + expected_pixelarray_surface.fill(pixelarray_result_color) + + # Copy the surface to ensure same dimensions/formatting. + surf_a = expected_pixelarray_surface.copy() + surf_a.fill(surface_color) + # For non-32 bit depths, the actual color can be different from what + # was filled. + expected_surface_color = surf_a.get_at((0, 0)) + + pixelarray_a = pygame.PixelArray(surf_a) + pixelarray_b = pygame.PixelArray(surf_a.copy()) + + for distance in (0.0, 0.01, 0.1, 1.0): + pixelarray_result = pixelarray_a.compare( + pixelarray_b, distance=distance + ) + + # Ensure the resulting pixelarray is correct and that the original + # surfaces were not changed. + self.assert_surfaces_equal( + pixelarray_result.surface, + expected_pixelarray_surface, + (depth, distance), + ) + self.assert_surface_filled( + pixelarray_a.surface, expected_surface_color, (depth, distance) + ) + self.assert_surface_filled( + pixelarray_b.surface, expected_surface_color, (depth, distance) + ) + + pixelarray_a.close() + pixelarray_b.close() + pixelarray_result.close() + + def test_compare__different_colors_within_distance(self): + """Ensures compare works correctly with different colored surfaces + and the color difference is within the given distance. + """ + size = (3, 5) + pixelarray_result_color = pygame.Color("white") + surface_a_color = (127, 127, 127, 255) + surface_b_color = (128, 127, 127, 255) + + for depth in (8, 16, 24, 32): + expected_pixelarray_surface = pygame.Surface(size, depth=depth) + expected_pixelarray_surface.fill(pixelarray_result_color) + + # Copy the surface to ensure same dimensions/formatting. + surf_a = expected_pixelarray_surface.copy() + surf_a.fill(surface_a_color) + # For non-32 bit depths, the actual color can be different from what + # was filled. + expected_surface_a_color = surf_a.get_at((0, 0)) + pixelarray_a = pygame.PixelArray(surf_a) + + surf_b = expected_pixelarray_surface.copy() + surf_b.fill(surface_b_color) + # For non-32 bit depths, the actual color can be different from what + # was filled. + expected_surface_b_color = surf_b.get_at((0, 0)) + pixelarray_b = pygame.PixelArray(surf_b) + + for distance in (0.2, 0.3, 0.5, 1.0): + pixelarray_result = pixelarray_a.compare( + pixelarray_b, distance=distance + ) + + # Ensure the resulting pixelarray is correct and that the original + # surfaces were not changed. + self.assert_surfaces_equal( + pixelarray_result.surface, + expected_pixelarray_surface, + (depth, distance), + ) + self.assert_surface_filled( + pixelarray_a.surface, expected_surface_a_color, (depth, distance) + ) + self.assert_surface_filled( + pixelarray_b.surface, expected_surface_b_color, (depth, distance) + ) + + pixelarray_a.close() + pixelarray_b.close() + pixelarray_result.close() + + def test_compare__different_colors_not_within_distance(self): + """Ensures compare works correctly with different colored surfaces + and the color difference is not within the given distance. + """ + size = (3, 5) + pixelarray_result_color = pygame.Color("black") + surface_a_color = (127, 127, 127, 255) + surface_b_color = (128, 127, 127, 255) + + for depth in (8, 16, 24, 32): + expected_pixelarray_surface = pygame.Surface(size, depth=depth) + expected_pixelarray_surface.fill(pixelarray_result_color) + + # Copy the surface to ensure same dimensions/formatting. + surf_a = expected_pixelarray_surface.copy() + surf_a.fill(surface_a_color) + # For non-32 bit depths, the actual color can be different from what + # was filled. + expected_surface_a_color = surf_a.get_at((0, 0)) + pixelarray_a = pygame.PixelArray(surf_a) + + surf_b = expected_pixelarray_surface.copy() + surf_b.fill(surface_b_color) + # For non-32 bit depths, the actual color can be different from what + # was filled. + expected_surface_b_color = surf_b.get_at((0, 0)) + pixelarray_b = pygame.PixelArray(surf_b) + + for distance in (0.0, 0.00001, 0.0001, 0.001): + pixelarray_result = pixelarray_a.compare( + pixelarray_b, distance=distance + ) + + # Ensure the resulting pixelarray is correct and that the original + # surfaces were not changed. + self.assert_surfaces_equal( + pixelarray_result.surface, + expected_pixelarray_surface, + (depth, distance), + ) + self.assert_surface_filled( + pixelarray_a.surface, expected_surface_a_color, (depth, distance) + ) + self.assert_surface_filled( + pixelarray_b.surface, expected_surface_b_color, (depth, distance) + ) + + pixelarray_a.close() + pixelarray_b.close() + pixelarray_result.close() + + def test_close(self): + """does not crash when it is deleted.""" + s = pygame.Surface((10, 10)) + a = pygame.PixelArray(s) + a.close() + del a + + def test_close_raises(self): + """when you try to do an operation after it is closed.""" + s = pygame.Surface((10, 10)) + a = pygame.PixelArray(s) + a.close() + + def access_after(): + a[:] + + self.assertRaises(ValueError, access_after) + + def assign_all_after(): + a[:] = 1 + + self.assertRaises(ValueError, assign_all_after) + + def make_surface_after(): + a.make_surface() + + self.assertRaises(ValueError, make_surface_after) + + def iter_after(): + for x in a: + pass + + self.assertRaises(ValueError, iter_after) + + def close_after(): + a.close() + + self.assertRaises(ValueError, close_after) + + def surface_after(): + a.surface + + self.assertRaises(ValueError, surface_after) + + def itemsize_after(): + a.itemsize + + self.assertRaises(ValueError, itemsize_after) + + def transpose_after(): + a.transpose() + + self.assertRaises(ValueError, transpose_after) + + def test_context_manager(self): + """closes properly.""" + s = pygame.Surface((10, 10)) + with pygame.PixelArray(s) as a: + a[:] + + # Test pixel array write... will also catch refcount issues and + # segfault + with pygame.PixelArray(s) as a: + a[:] = pygame.Color("deepskyblue") + + def test_pixel_array(self): + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((10, 20), 0, bpp) + sf.fill((0, 0, 0)) + ar = pygame.PixelArray(sf) + + self.assertEqual(ar._pixels_address, sf._pixels_address) + + if sf.mustlock(): + self.assertTrue(sf.get_locked()) + + self.assertEqual(len(ar), 10) + + del ar + if sf.mustlock(): + self.assertFalse(sf.get_locked()) + + def test_as_class(self): + # Check general new-style class freatures. + sf = pygame.Surface((2, 3), 0, 32) + ar = pygame.PixelArray(sf) + self.assertRaises(AttributeError, getattr, ar, "nonnative") + ar.nonnative = "value" + self.assertEqual(ar.nonnative, "value") + r = weakref.ref(ar) + self.assertTrue(r() is ar) + del ar + gc.collect() + self.assertTrue(r() is None) + + class C(pygame.PixelArray): + def __str__(self): + return "string (%i, %i)" % self.shape + + ar = C(sf) + self.assertEqual(str(ar), "string (2, 3)") + r = weakref.ref(ar) + self.assertTrue(r() is ar) + del ar + gc.collect() + self.assertTrue(r() is None) + + def test_pixelarray__subclassed_surface(self): + """Ensure the PixelArray constructor accepts subclassed surfaces.""" + surface = SurfaceSubclass((3, 5), 0, 32) + pixelarray = pygame.PixelArray(surface) + + self.assertIsInstance(pixelarray, pygame.PixelArray) + + # Sequence interfaces + def test_get_column(self): + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((6, 8), 0, bpp) + sf.fill((0, 0, 255)) + val = sf.map_rgb((0, 0, 255)) + ar = pygame.PixelArray(sf) + + ar2 = ar.__getitem__(1) + self.assertEqual(len(ar2), 8) + self.assertEqual(ar2.__getitem__(0), val) + self.assertEqual(ar2.__getitem__(1), val) + self.assertEqual(ar2.__getitem__(2), val) + + ar2 = ar.__getitem__(-1) + self.assertEqual(len(ar2), 8) + self.assertEqual(ar2.__getitem__(0), val) + self.assertEqual(ar2.__getitem__(1), val) + self.assertEqual(ar2.__getitem__(2), val) + + @unittest.skipIf(IS_PYPY, "pypy malloc abort") + def test_get_pixel(self): + w = 10 + h = 20 + size = w, h + bg_color = (0, 0, 255) + fg_color_y = (0, 0, 128) + fg_color_x = (0, 0, 11) + for bpp in (8, 16, 24, 32): + sf = pygame.Surface(size, 0, bpp) + mapped_bg_color = sf.map_rgb(bg_color) + mapped_fg_color_y = sf.map_rgb(fg_color_y) + mapped_fg_color_x = sf.map_rgb(fg_color_x) + self.assertNotEqual( + mapped_fg_color_y, + mapped_bg_color, + "Unusable test colors for bpp %i" % (bpp,), + ) + self.assertNotEqual( + mapped_fg_color_x, + mapped_bg_color, + "Unusable test colors for bpp %i" % (bpp,), + ) + self.assertNotEqual( + mapped_fg_color_y, + mapped_fg_color_x, + "Unusable test colors for bpp %i" % (bpp,), + ) + sf.fill(bg_color) + + ar = pygame.PixelArray(sf) + + ar_y = ar.__getitem__(1) + for y in range(h): + ar2 = ar_y.__getitem__(y) + self.assertEqual( + ar2, + mapped_bg_color, + "ar[1][%i] == %i, mapped_bg_color == %i" + % (y, ar2, mapped_bg_color), + ) + + sf.set_at((1, y), fg_color_y) + ar2 = ar_y.__getitem__(y) + self.assertEqual( + ar2, + mapped_fg_color_y, + "ar[1][%i] == %i, mapped_fg_color_y == %i" + % (y, ar2, mapped_fg_color_y), + ) + + sf.set_at((1, 1), bg_color) + for x in range(w): + ar2 = ar.__getitem__(x).__getitem__(1) + self.assertEqual( + ar2, + mapped_bg_color, + "ar[%i][1] = %i, mapped_bg_color = %i" % (x, ar2, mapped_bg_color), + ) + sf.set_at((x, 1), fg_color_x) + ar2 = ar.__getitem__(x).__getitem__(1) + self.assertEqual( + ar2, + mapped_fg_color_x, + "ar[%i][1] = %i, mapped_fg_color_x = %i" + % (x, ar2, mapped_fg_color_x), + ) + + ar2 = ar.__getitem__(0).__getitem__(0) + self.assertEqual(ar2, mapped_bg_color, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(1).__getitem__(0) + self.assertEqual(ar2, mapped_fg_color_y, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(-4).__getitem__(1) + self.assertEqual(ar2, mapped_fg_color_x, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(-4).__getitem__(5) + self.assertEqual(ar2, mapped_bg_color, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(-4).__getitem__(0) + self.assertEqual(ar2, mapped_bg_color, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(-w + 1).__getitem__(0) + self.assertEqual(ar2, mapped_fg_color_y, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(-w).__getitem__(0) + self.assertEqual(ar2, mapped_bg_color, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(5).__getitem__(-4) + self.assertEqual(ar2, mapped_bg_color, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(5).__getitem__(-h + 1) + self.assertEqual(ar2, mapped_fg_color_x, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(5).__getitem__(-h) + self.assertEqual(ar2, mapped_bg_color, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(0).__getitem__(-h + 1) + self.assertEqual(ar2, mapped_fg_color_x, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(0).__getitem__(-h) + self.assertEqual(ar2, mapped_bg_color, "bpp = %i" % (bpp,)) + + def test_set_pixel(self): + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((10, 20), 0, bpp) + sf.fill((0, 0, 0)) + ar = pygame.PixelArray(sf) + + ar.__getitem__(0).__setitem__(0, (0, 255, 0)) + self.assertEqual(ar[0][0], sf.map_rgb((0, 255, 0))) + + ar.__getitem__(1).__setitem__(1, (128, 128, 128)) + self.assertEqual(ar[1][1], sf.map_rgb((128, 128, 128))) + + ar.__getitem__(-1).__setitem__(-1, (128, 128, 128)) + self.assertEqual(ar[9][19], sf.map_rgb((128, 128, 128))) + + ar.__getitem__(-2).__setitem__(-2, (128, 128, 128)) + self.assertEqual(ar[8][-2], sf.map_rgb((128, 128, 128))) + + def test_set_column(self): + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((6, 8), 0, bpp) + sf.fill((0, 0, 0)) + ar = pygame.PixelArray(sf) + + sf2 = pygame.Surface((6, 8), 0, bpp) + sf2.fill((0, 255, 255)) + ar2 = pygame.PixelArray(sf2) + + # Test single value assignment + ar.__setitem__(2, (128, 128, 128)) + self.assertEqual(ar[2][0], sf.map_rgb((128, 128, 128))) + self.assertEqual(ar[2][1], sf.map_rgb((128, 128, 128))) + + ar.__setitem__(-1, (0, 255, 255)) + self.assertEqual(ar[5][0], sf.map_rgb((0, 255, 255))) + self.assertEqual(ar[-1][1], sf.map_rgb((0, 255, 255))) + + ar.__setitem__(-2, (255, 255, 0)) + self.assertEqual(ar[4][0], sf.map_rgb((255, 255, 0))) + self.assertEqual(ar[-2][1], sf.map_rgb((255, 255, 0))) + + # Test list assignment. + ar.__setitem__(0, [(255, 255, 255)] * 8) + self.assertEqual(ar[0][0], sf.map_rgb((255, 255, 255))) + self.assertEqual(ar[0][1], sf.map_rgb((255, 255, 255))) + + # Test tuple assignment. + # Changed in Pygame 1.9.2 - Raises an exception. + self.assertRaises( + ValueError, + ar.__setitem__, + 1, + ( + (204, 0, 204), + (17, 17, 17), + (204, 0, 204), + (17, 17, 17), + (204, 0, 204), + (17, 17, 17), + (204, 0, 204), + (17, 17, 17), + ), + ) + + # Test pixel array assignment. + ar.__setitem__(1, ar2.__getitem__(3)) + self.assertEqual(ar[1][0], sf.map_rgb((0, 255, 255))) + self.assertEqual(ar[1][1], sf.map_rgb((0, 255, 255))) + + def test_get_slice(self): + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((10, 20), 0, bpp) + sf.fill((0, 0, 0)) + ar = pygame.PixelArray(sf) + + self.assertEqual(len(ar[0:2]), 2) + self.assertEqual(len(ar[3:7][3]), 20) + + self.assertEqual(ar[0:0], None) + self.assertEqual(ar[5:5], None) + self.assertEqual(ar[9:9], None) + + # Has to resolve to ar[7:8] + self.assertEqual(len(ar[-3:-2]), 1) # 2D + self.assertEqual(len(ar[-3:-2][0]), 20) # 1D + + # Try assignments. + + # 2D assignment. + ar[2:5] = (255, 255, 255) + + # 1D assignment + ar[3][3:7] = (10, 10, 10) + self.assertEqual(ar[3][5], sf.map_rgb((10, 10, 10))) + self.assertEqual(ar[3][6], sf.map_rgb((10, 10, 10))) + + @unittest.skipIf(IS_PYPY, "skipping for PyPy (segfaults on mac pypy3 6.0.0)") + def test_contains(self): + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((10, 20), 0, bpp) + sf.fill((0, 0, 0)) + sf.set_at((8, 8), (255, 255, 255)) + + ar = pygame.PixelArray(sf) + self.assertTrue((0, 0, 0) in ar) + self.assertTrue((255, 255, 255) in ar) + self.assertFalse((255, 255, 0) in ar) + self.assertFalse(0x0000FF in ar) + + # Test sliced array + self.assertTrue((0, 0, 0) in ar[8]) + self.assertTrue((255, 255, 255) in ar[8]) + self.assertFalse((255, 255, 0) in ar[8]) + self.assertFalse(0x0000FF in ar[8]) + + def test_get_surface(self): + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((10, 20), 0, bpp) + sf.fill((0, 0, 0)) + ar = pygame.PixelArray(sf) + self.assertTrue(ar.surface is sf) + + def test_get_surface__subclassed_surface(self): + """Ensure the surface attribute can handle subclassed surfaces.""" + expected_surface = SurfaceSubclass((5, 3), 0, 32) + pixelarray = pygame.PixelArray(expected_surface) + + surface = pixelarray.surface + + self.assertIs(surface, expected_surface) + self.assertIsInstance(surface, pygame.Surface) + self.assertIsInstance(surface, SurfaceSubclass) + + def test_set_slice(self): + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((6, 8), 0, bpp) + sf.fill((0, 0, 0)) + ar = pygame.PixelArray(sf) + + # Test single value assignment + val = sf.map_rgb((128, 128, 128)) + ar[0:2] = val + self.assertEqual(ar[0][0], val) + self.assertEqual(ar[0][1], val) + self.assertEqual(ar[1][0], val) + self.assertEqual(ar[1][1], val) + + val = sf.map_rgb((0, 255, 255)) + ar[-3:-1] = val + self.assertEqual(ar[3][0], val) + self.assertEqual(ar[-2][1], val) + + val = sf.map_rgb((255, 255, 255)) + ar[-3:] = (255, 255, 255) + self.assertEqual(ar[4][0], val) + self.assertEqual(ar[-1][1], val) + + # Test array size mismatch. + # Changed in ver. 1.9.2 + # (was "Test list assignment, this is a vertical assignment.") + val = sf.map_rgb((0, 255, 0)) + self.assertRaises(ValueError, ar.__setitem__, slice(2, 4), [val] * 8) + + # And the horizontal assignment. + val = sf.map_rgb((255, 0, 0)) + val2 = sf.map_rgb((128, 0, 255)) + ar[0:2] = [val, val2] + self.assertEqual(ar[0][0], val) + self.assertEqual(ar[1][0], val2) + self.assertEqual(ar[0][1], val) + self.assertEqual(ar[1][1], val2) + self.assertEqual(ar[0][4], val) + self.assertEqual(ar[1][4], val2) + self.assertEqual(ar[0][5], val) + self.assertEqual(ar[1][5], val2) + + # Test pixelarray assignment. + ar[:] = (0, 0, 0) + sf2 = pygame.Surface((6, 8), 0, bpp) + sf2.fill((255, 0, 255)) + + val = sf.map_rgb((255, 0, 255)) + ar2 = pygame.PixelArray(sf2) + + ar[:] = ar2[:] + self.assertEqual(ar[0][0], val) + self.assertEqual(ar[5][7], val) + + # Ensure p1 ... pn are freed for array[...] = [p1, ..., pn] + # Bug fix: reference counting. + if hasattr(sys, "getrefcount"): + + class Int(int): + """Unique int instances""" + + pass + + sf = pygame.Surface((5, 2), 0, 32) + ar = pygame.PixelArray(sf) + pixel_list = [Int(i) for i in range(ar.shape[0])] + refcnts_before = [sys.getrefcount(i) for i in pixel_list] + ar[...] = pixel_list + refcnts_after = [sys.getrefcount(i) for i in pixel_list] + gc.collect() + self.assertEqual(refcnts_after, refcnts_before) + + def test_subscript(self): + # By default we do not need to work with any special __***__ + # methods as map subscripts are the first looked up by the + # object system. + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((6, 8), 0, bpp) + sf.set_at((1, 3), (0, 255, 0)) + sf.set_at((0, 0), (0, 255, 0)) + sf.set_at((4, 4), (0, 255, 0)) + val = sf.map_rgb((0, 255, 0)) + + ar = pygame.PixelArray(sf) + + # Test single value requests. + self.assertEqual(ar[1, 3], val) + self.assertEqual(ar[0, 0], val) + self.assertEqual(ar[4, 4], val) + self.assertEqual(ar[1][3], val) + self.assertEqual(ar[0][0], val) + self.assertEqual(ar[4][4], val) + + # Test ellipse working. + self.assertEqual(len(ar[..., ...]), 6) + self.assertEqual(len(ar[1, ...]), 8) + self.assertEqual(len(ar[..., 3]), 6) + + # Test simple slicing + self.assertEqual(len(ar[:, :]), 6) + self.assertEqual( + len(ar[:,]), + 6, + ) + self.assertEqual(len(ar[1, :]), 8) + self.assertEqual(len(ar[:, 2]), 6) + # Empty slices + self.assertEqual( + ar[4:4,], + None, + ) + self.assertEqual(ar[4:4, ...], None) + self.assertEqual(ar[4:4, 2:2], None) + self.assertEqual(ar[4:4, 1:4], None) + self.assertEqual( + ar[4:4:2,], + None, + ) + self.assertEqual( + ar[4:4:-2,], + None, + ) + self.assertEqual(ar[4:4:1, ...], None) + self.assertEqual(ar[4:4:-1, ...], None) + self.assertEqual(ar[4:4:1, 2:2], None) + self.assertEqual(ar[4:4:-1, 1:4], None) + self.assertEqual(ar[..., 4:4], None) + self.assertEqual(ar[1:4, 4:4], None) + self.assertEqual(ar[..., 4:4:1], None) + self.assertEqual(ar[..., 4:4:-1], None) + self.assertEqual(ar[2:2, 4:4:1], None) + self.assertEqual(ar[1:4, 4:4:-1], None) + + # Test advanced slicing + ar[0] = 0 + ar[1] = 1 + ar[2] = 2 + ar[3] = 3 + ar[4] = 4 + ar[5] = 5 + + # We should receive something like [0,2,4] + self.assertEqual(ar[::2, 1][0], 0) + self.assertEqual(ar[::2, 1][1], 2) + self.assertEqual(ar[::2, 1][2], 4) + # We should receive something like [2,2,2] + self.assertEqual(ar[2, ::2][0], 2) + self.assertEqual(ar[2, ::2][1], 2) + self.assertEqual(ar[2, ::2][2], 2) + + # Should create a 3x3 array of [0,2,4] + ar2 = ar[::2, ::2] + self.assertEqual(len(ar2), 3) + self.assertEqual(ar2[0][0], 0) + self.assertEqual(ar2[0][1], 0) + self.assertEqual(ar2[0][2], 0) + self.assertEqual(ar2[2][0], 4) + self.assertEqual(ar2[2][1], 4) + self.assertEqual(ar2[2][2], 4) + self.assertEqual(ar2[1][0], 2) + self.assertEqual(ar2[2][0], 4) + self.assertEqual(ar2[1][1], 2) + + # Should create a reversed 3x8 array over X of [1,2,3] -> [3,2,1] + ar2 = ar[3:0:-1] + self.assertEqual(len(ar2), 3) + self.assertEqual(ar2[0][0], 3) + self.assertEqual(ar2[0][1], 3) + self.assertEqual(ar2[0][2], 3) + self.assertEqual(ar2[0][7], 3) + self.assertEqual(ar2[2][0], 1) + self.assertEqual(ar2[2][1], 1) + self.assertEqual(ar2[2][2], 1) + self.assertEqual(ar2[2][7], 1) + self.assertEqual(ar2[1][0], 2) + self.assertEqual(ar2[1][1], 2) + # Should completely reverse the array over X -> [5,4,3,2,1,0] + ar2 = ar[::-1] + self.assertEqual(len(ar2), 6) + self.assertEqual(ar2[0][0], 5) + self.assertEqual(ar2[0][1], 5) + self.assertEqual(ar2[0][3], 5) + self.assertEqual(ar2[0][-1], 5) + self.assertEqual(ar2[1][0], 4) + self.assertEqual(ar2[1][1], 4) + self.assertEqual(ar2[1][3], 4) + self.assertEqual(ar2[1][-1], 4) + self.assertEqual(ar2[-1][-1], 0) + self.assertEqual(ar2[-2][-2], 1) + self.assertEqual(ar2[-3][-1], 2) + + # Test advanced slicing + ar[:] = 0 + ar2 = ar[:, 1] + ar2[:] = [99] * len(ar2) + self.assertEqual(ar2[0], 99) + self.assertEqual(ar2[-1], 99) + self.assertEqual(ar2[-2], 99) + self.assertEqual(ar2[2], 99) + self.assertEqual(ar[0, 1], 99) + self.assertEqual(ar[1, 1], 99) + self.assertEqual(ar[2, 1], 99) + self.assertEqual(ar[-1, 1], 99) + self.assertEqual(ar[-2, 1], 99) + + # Cases where a 2d array should have a dimension of length 1. + ar2 = ar[1:2, :] + self.assertEqual(ar2.shape, (1, ar.shape[1])) + ar2 = ar[:, 1:2] + self.assertEqual(ar2.shape, (ar.shape[0], 1)) + sf2 = pygame.Surface((1, 5), 0, 32) + ar2 = pygame.PixelArray(sf2) + self.assertEqual(ar2.shape, sf2.get_size()) + sf2 = pygame.Surface((7, 1), 0, 32) + ar2 = pygame.PixelArray(sf2) + self.assertEqual(ar2.shape, sf2.get_size()) + + # Array has a single ellipsis subscript: the identity operator + ar2 = ar[...] + self.assertTrue(ar2 is ar) + + # Ensure x and y are freed for p = array[x, y] + # Bug fix: reference counting + if hasattr(sys, "getrefcount"): + + class Int(int): + """Unique int instances""" + + pass + + sf = pygame.Surface((2, 2), 0, 32) + ar = pygame.PixelArray(sf) + x, y = Int(0), Int(1) + rx_before, ry_before = sys.getrefcount(x), sys.getrefcount(y) + p = ar[x, y] + rx_after, ry_after = sys.getrefcount(x), sys.getrefcount(y) + self.assertEqual(rx_after, rx_before) + self.assertEqual(ry_after, ry_before) + + def test_ass_subscript(self): + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((6, 8), 0, bpp) + sf.fill((255, 255, 255)) + ar = pygame.PixelArray(sf) + + # Test ellipse working + ar[..., ...] = (0, 0, 0) + self.assertEqual(ar[0, 0], 0) + self.assertEqual(ar[1, 0], 0) + self.assertEqual(ar[-1, -1], 0) + ar[...,] = (0, 0, 255) + self.assertEqual(ar[0, 0], sf.map_rgb((0, 0, 255))) + self.assertEqual(ar[1, 0], sf.map_rgb((0, 0, 255))) + self.assertEqual(ar[-1, -1], sf.map_rgb((0, 0, 255))) + ar[:, ...] = (255, 0, 0) + self.assertEqual(ar[0, 0], sf.map_rgb((255, 0, 0))) + self.assertEqual(ar[1, 0], sf.map_rgb((255, 0, 0))) + self.assertEqual(ar[-1, -1], sf.map_rgb((255, 0, 0))) + ar[...] = (0, 255, 0) + self.assertEqual(ar[0, 0], sf.map_rgb((0, 255, 0))) + self.assertEqual(ar[1, 0], sf.map_rgb((0, 255, 0))) + self.assertEqual(ar[-1, -1], sf.map_rgb((0, 255, 0))) + + # Ensure x and y are freed for array[x, y] = p + # Bug fix: reference counting + if hasattr(sys, "getrefcount"): + + class Int(int): + """Unique int instances""" + + pass + + sf = pygame.Surface((2, 2), 0, 32) + ar = pygame.PixelArray(sf) + x, y = Int(0), Int(1) + rx_before, ry_before = sys.getrefcount(x), sys.getrefcount(y) + ar[x, y] = 0 + rx_after, ry_after = sys.getrefcount(x), sys.getrefcount(y) + self.assertEqual(rx_after, rx_before) + self.assertEqual(ry_after, ry_before) + + def test_pixels_field(self): + for bpp in [1, 2, 3, 4]: + sf = pygame.Surface((11, 7), 0, bpp * 8) + ar = pygame.PixelArray(sf) + ar2 = ar[1:, :] + self.assertEqual(ar2._pixels_address - ar._pixels_address, ar.itemsize) + ar2 = ar[:, 1:] + self.assertEqual(ar2._pixels_address - ar._pixels_address, ar.strides[1]) + ar2 = ar[::-1, :] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, + (ar.shape[0] - 1) * ar.itemsize, + ) + ar2 = ar[::-2, :] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, + (ar.shape[0] - 1) * ar.itemsize, + ) + ar2 = ar[:, ::-1] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, + (ar.shape[1] - 1) * ar.strides[1], + ) + ar3 = ar2[::-1, :] + self.assertEqual( + ar3._pixels_address - ar._pixels_address, + (ar.shape[0] - 1) * ar.strides[0] + (ar.shape[1] - 1) * ar.strides[1], + ) + ar2 = ar[:, ::-2] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, + (ar.shape[1] - 1) * ar.strides[1], + ) + ar2 = ar[2::, 3::] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, + ar.strides[0] * 2 + ar.strides[1] * 3, + ) + ar2 = ar[2::2, 3::4] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, + ar.strides[0] * 2 + ar.strides[1] * 3, + ) + ar2 = ar[9:2:-1, :] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, ar.strides[0] * 9 + ) + ar2 = ar[:, 5:2:-1] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, ar.strides[1] * 5 + ) + ##? ar2 = ar[:,9:2:-1] + + def test_make_surface(self): + bg_color = pygame.Color(255, 255, 255) + fg_color = pygame.Color(128, 100, 0) + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((10, 20), 0, bpp) + bg_color_adj = sf.unmap_rgb(sf.map_rgb(bg_color)) + fg_color_adj = sf.unmap_rgb(sf.map_rgb(fg_color)) + sf.fill(bg_color_adj) + sf.fill(fg_color_adj, (2, 5, 4, 11)) + ar = pygame.PixelArray(sf) + newsf = ar[::2, ::2].make_surface() + rect = newsf.get_rect() + self.assertEqual(rect.width, 5) + self.assertEqual(rect.height, 10) + for p in [ + (0, 2), + (0, 3), + (1, 2), + (2, 2), + (3, 2), + (3, 3), + (0, 7), + (0, 8), + (1, 8), + (2, 8), + (3, 8), + (3, 7), + ]: + self.assertEqual(newsf.get_at(p), bg_color_adj) + for p in [(1, 3), (2, 3), (1, 5), (2, 5), (1, 7), (2, 7)]: + self.assertEqual(newsf.get_at(p), fg_color_adj) + + # Bug when array width is not a multiple of the slice step. + w = 17 + lst = list(range(w)) + w_slice = len(lst[::2]) + h = 3 + sf = pygame.Surface((w, h), 0, 32) + ar = pygame.PixelArray(sf) + ar2 = ar[::2, :] + sf2 = ar2.make_surface() + w2, h2 = sf2.get_size() + self.assertEqual(w2, w_slice) + self.assertEqual(h2, h) + + # Bug when array height is not a multiple of the slice step. + # This can hang the Python interpreter. + h = 17 + lst = list(range(h)) + h_slice = len(lst[::2]) + w = 3 + sf = pygame.Surface((w, h), 0, 32) + ar = pygame.PixelArray(sf) + ar2 = ar[:, ::2] + sf2 = ar2.make_surface() # Hangs here. + w2, h2 = sf2.get_size() + self.assertEqual(w2, w) + self.assertEqual(h2, h_slice) + + def test_make_surface__subclassed_surface(self): + """Ensure make_surface can handle subclassed surfaces.""" + expected_size = (3, 5) + expected_flags = 0 + expected_depth = 32 + original_surface = SurfaceSubclass( + expected_size, expected_flags, expected_depth + ) + pixelarray = pygame.PixelArray(original_surface) + + surface = pixelarray.make_surface() + + self.assertIsNot(surface, original_surface) + self.assertIsInstance(surface, pygame.Surface) + self.assertNotIsInstance(surface, SurfaceSubclass) + self.assertEqual(surface.get_size(), expected_size) + self.assertEqual(surface.get_flags(), expected_flags) + self.assertEqual(surface.get_bitsize(), expected_depth) + + def test_iter(self): + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((5, 10), 0, bpp) + ar = pygame.PixelArray(sf) + iterations = 0 + for col in ar: + self.assertEqual(len(col), 10) + iterations += 1 + self.assertEqual(iterations, 5) + + def test_replace(self): + # print("replace start") + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((10, 10), 0, bpp) + sf.fill((255, 0, 0)) + rval = sf.map_rgb((0, 0, 255)) + oval = sf.map_rgb((255, 0, 0)) + ar = pygame.PixelArray(sf) + ar[::2].replace((255, 0, 0), (0, 0, 255)) + self.assertEqual(ar[0][0], rval) + self.assertEqual(ar[1][0], oval) + self.assertEqual(ar[2][3], rval) + self.assertEqual(ar[3][6], oval) + self.assertEqual(ar[8][9], rval) + self.assertEqual(ar[9][9], oval) + + ar[::2].replace((0, 0, 255), (255, 0, 0), weights=(10, 20, 50)) + self.assertEqual(ar[0][0], oval) + self.assertEqual(ar[2][3], oval) + self.assertEqual(ar[3][6], oval) + self.assertEqual(ar[8][9], oval) + self.assertEqual(ar[9][9], oval) + # print("replace end") + + def test_extract(self): + # print("extract start") + for bpp in (8, 16, 24, 32): + sf = pygame.Surface((10, 10), 0, bpp) + sf.fill((0, 0, 255)) + sf.fill((255, 0, 0), (2, 2, 6, 6)) + + white = sf.map_rgb((255, 255, 255)) + black = sf.map_rgb((0, 0, 0)) + + ar = pygame.PixelArray(sf) + newar = ar.extract((255, 0, 0)) + + self.assertEqual(newar[0][0], black) + self.assertEqual(newar[1][0], black) + self.assertEqual(newar[2][3], white) + self.assertEqual(newar[3][6], white) + self.assertEqual(newar[8][9], black) + self.assertEqual(newar[9][9], black) + + newar = ar.extract((255, 0, 0), weights=(10, 0.1, 50)) + self.assertEqual(newar[0][0], black) + self.assertEqual(newar[1][0], black) + self.assertEqual(newar[2][3], white) + self.assertEqual(newar[3][6], white) + self.assertEqual(newar[8][9], black) + self.assertEqual(newar[9][9], black) + # print("extract end") + + def test_2dslice_assignment(self): + w = 2 * 5 * 8 + h = 3 * 5 * 9 + sf = pygame.Surface((w, h), 0, 32) + ar = pygame.PixelArray(sf) + size = (w, h) + strides = (1, w) + offset = 0 + self._test_assignment(sf, ar, size, strides, offset) + xslice = slice(None, None, 2) + yslice = slice(None, None, 3) + ar, size, strides, offset = self._array_slice( + ar, size, (xslice, yslice), strides, offset + ) + self._test_assignment(sf, ar, size, strides, offset) + xslice = slice(5, None, 5) + yslice = slice(5, None, 5) + ar, size, strides, offset = self._array_slice( + ar, size, (xslice, yslice), strides, offset + ) + self._test_assignment(sf, ar, size, strides, offset) + + def _test_assignment(self, sf, ar, ar_size, ar_strides, ar_offset): + self.assertEqual(ar.shape, ar_size) + ar_w, ar_h = ar_size + ar_xstride, ar_ystride = ar_strides + sf_w, sf_h = sf.get_size() + black = pygame.Color("black") + color = pygame.Color(0, 0, 12) + pxcolor = sf.map_rgb(color) + sf.fill(black) + for ar_x, ar_y in [ + (0, 0), + (0, ar_h - 4), + (ar_w - 3, 0), + (0, ar_h - 1), + (ar_w - 1, 0), + (ar_w - 1, ar_h - 1), + ]: + sf_offset = ar_offset + ar_x * ar_xstride + ar_y * ar_ystride + sf_y = sf_offset // sf_w + sf_x = sf_offset - sf_y * sf_w + sf_posn = (sf_x, sf_y) + sf_pix = sf.get_at(sf_posn) + self.assertEqual( + sf_pix, + black, + "at pixarr posn (%i, %i) (surf posn (%i, %i)): " + "%s != %s" % (ar_x, ar_y, sf_x, sf_y, sf_pix, black), + ) + ar[ar_x, ar_y] = pxcolor + sf_pix = sf.get_at(sf_posn) + self.assertEqual( + sf_pix, + color, + "at pixarr posn (%i, %i) (surf posn (%i, %i)): " + "%s != %s" % (ar_x, ar_y, sf_x, sf_y, sf_pix, color), + ) + + def _array_slice(self, ar, size, slices, strides, offset): + ar = ar[slices] + xslice, yslice = slices + w, h = size + xstart, xstop, xstep = xslice.indices(w) + ystart, ystop, ystep = yslice.indices(h) + w = (xstop - xstart + xstep - 1) // xstep + h = (ystop - ystart + ystep - 1) // ystep + xstride, ystride = strides + offset += xstart * xstride + ystart * ystride + xstride *= xstep + ystride *= ystep + return ar, (w, h), (xstride, ystride), offset + + def test_array_properties(self): + # itemsize, ndim, shape, and strides. + for bpp in [1, 2, 3, 4]: + sf = pygame.Surface((2, 2), 0, bpp * 8) + ar = pygame.PixelArray(sf) + self.assertEqual(ar.itemsize, bpp) + + for shape in [(4, 16), (5, 13)]: + w, h = shape + sf = pygame.Surface(shape, 0, 32) + bpp = sf.get_bytesize() + pitch = sf.get_pitch() + ar = pygame.PixelArray(sf) + self.assertEqual(ar.ndim, 2) + self.assertEqual(ar.shape, shape) + self.assertEqual(ar.strides, (bpp, pitch)) + ar2 = ar[::2, :] + w2 = len(([0] * w)[::2]) + self.assertEqual(ar2.ndim, 2) + self.assertEqual(ar2.shape, (w2, h)) + self.assertEqual(ar2.strides, (2 * bpp, pitch)) + ar2 = ar[:, ::2] + h2 = len(([0] * h)[::2]) + self.assertEqual(ar2.ndim, 2) + self.assertEqual(ar2.shape, (w, h2)) + self.assertEqual(ar2.strides, (bpp, 2 * pitch)) + ar2 = ar[1] + self.assertEqual(ar2.ndim, 1) + self.assertEqual(ar2.shape, (h,)) + self.assertEqual(ar2.strides, (pitch,)) + ar2 = ar[:, 1] + self.assertEqual(ar2.ndim, 1) + self.assertEqual(ar2.shape, (w,)) + self.assertEqual(ar2.strides, (bpp,)) + + def test_self_assign(self): + # This differs from NumPy arrays. + w = 10 + max_x = w - 1 + h = 20 + max_y = h - 1 + for bpp in [1, 2, 3, 4]: + sf = pygame.Surface((w, h), 0, bpp * 8) + ar = pygame.PixelArray(sf) + for i in range(w * h): + ar[i % w, i // w] = i + ar[:, :] = ar[::-1, :] + for i in range(w * h): + self.assertEqual(ar[max_x - i % w, i // w], i) + ar = pygame.PixelArray(sf) + for i in range(w * h): + ar[i % w, i // w] = i + ar[:, :] = ar[:, ::-1] + for i in range(w * h): + self.assertEqual(ar[i % w, max_y - i // w], i) + ar = pygame.PixelArray(sf) + for i in range(w * h): + ar[i % w, i // w] = i + ar[:, :] = ar[::-1, ::-1] + for i in range(w * h): + self.assertEqual(ar[max_x - i % w, max_y - i // w], i) + + def test_color_value(self): + # Confirm that a PixelArray slice assignment distinguishes between + # pygame.Color and tuple objects as single (r, g, b[, a]) colors + # and other sequences as sequences of colors to be treated as + # slices. + sf = pygame.Surface((5, 5), 0, 32) + ar = pygame.PixelArray(sf) + index = slice(None, None, 1) + ar.__setitem__(index, (1, 2, 3)) + self.assertEqual(ar[0, 0], sf.map_rgb((1, 2, 3))) + ar.__setitem__(index, pygame.Color(10, 11, 12)) + self.assertEqual(ar[0, 0], sf.map_rgb((10, 11, 12))) + self.assertRaises(ValueError, ar.__setitem__, index, (1, 2, 3, 4, 5)) + self.assertRaises(ValueError, ar.__setitem__, (index, index), (1, 2, 3, 4, 5)) + self.assertRaises(ValueError, ar.__setitem__, index, [1, 2, 3]) + self.assertRaises(ValueError, ar.__setitem__, (index, index), [1, 2, 3]) + sf = pygame.Surface((3, 3), 0, 32) + ar = pygame.PixelArray(sf) + ar[:] = (20, 30, 40) + self.assertEqual(ar[0, 0], sf.map_rgb((20, 30, 40))) + ar[:] = [20, 30, 40] + self.assertEqual(ar[0, 0], 20) + self.assertEqual(ar[1, 0], 30) + self.assertEqual(ar[2, 0], 40) + + def test_transpose(self): + # PixelArray.transpose(): swap axis on a 2D array, add a length + # 1 x axis to a 1D array. + sf = pygame.Surface((3, 7), 0, 32) + ar = pygame.PixelArray(sf) + w, h = ar.shape + dx, dy = ar.strides + for i in range(w * h): + x = i % w + y = i // w + ar[x, y] = i + ar_t = ar.transpose() + self.assertEqual(ar_t.shape, (h, w)) + self.assertEqual(ar_t.strides, (dy, dx)) + for i in range(w * h): + x = i % w + y = i // w + self.assertEqual(ar_t[y, x], ar[x, y]) + ar1D = ar[0] + ar2D = ar1D.transpose() + self.assertEqual(ar2D.shape, (1, h)) + for y in range(h): + self.assertEqual(ar1D[y], ar2D[0, y]) + ar1D = ar[:, 0] + ar2D = ar1D.transpose() + self.assertEqual(ar2D.shape, (1, w)) + for x in range(2): + self.assertEqual(ar1D[x], ar2D[0, x]) + + def test_length_1_dimension_broadcast(self): + w = 5 + sf = pygame.Surface((w, w), 0, 32) + ar = pygame.PixelArray(sf) + # y-axis broadcast. + sf_x = pygame.Surface((w, 1), 0, 32) + ar_x = pygame.PixelArray(sf_x) + for i in range(w): + ar_x[i, 0] = (w + 1) * 10 + ar[...] = ar_x + for y in range(w): + for x in range(w): + self.assertEqual(ar[x, y], ar_x[x, 0]) + # x-axis broadcast. + ar[...] = 0 + sf_y = pygame.Surface((1, w), 0, 32) + ar_y = pygame.PixelArray(sf_y) + for i in range(w): + ar_y[0, i] = (w + 1) * 10 + ar[...] = ar_y + for x in range(w): + for y in range(w): + self.assertEqual(ar[x, y], ar_y[0, y]) + # (1, 1) array broadcast. + ar[...] = 0 + sf_1px = pygame.Surface((1, 1), 0, 32) + ar_1px = pygame.PixelArray(sf_1px) + ar_1px[0, 0] = 42 # Well it had to show up somewhere. + ar[...] = ar_1px + for y in range(w): + for x in range(w): + self.assertEqual(ar[x, y], 42) + + def test_assign_size_mismatch(self): + sf = pygame.Surface((7, 11), 0, 32) + ar = pygame.PixelArray(sf) + self.assertRaises(ValueError, ar.__setitem__, Ellipsis, ar[:, 0:2]) + self.assertRaises(ValueError, ar.__setitem__, Ellipsis, ar[0:2, :]) + + def test_repr(self): + # Python 3.x bug: the tp_repr slot function returned NULL instead + # of a Unicode string, triggering an exception. + sf = pygame.Surface((3, 1), pygame.SRCALPHA, 16) + ar = pygame.PixelArray(sf) + ar[...] = 42 + pixel = sf.get_at_mapped((0, 0)) + self.assertEqual(repr(ar), type(ar).__name__ + "([\n [42, 42, 42]]\n)") + + +@unittest.skipIf(IS_PYPY, "pypy having issues") +class PixelArrayArrayInterfaceTest(unittest.TestCase, TestMixin): + @unittest.skipIf(IS_PYPY, "skipping for PyPy (why?)") + def test_basic(self): + # Check unchanging fields. + sf = pygame.Surface((2, 2), 0, 32) + ar = pygame.PixelArray(sf) + + ai = arrinter.ArrayInterface(ar) + self.assertEqual(ai.two, 2) + self.assertEqual(ai.typekind, "u") + self.assertEqual(ai.nd, 2) + self.assertEqual(ai.data, ar._pixels_address) + + @unittest.skipIf(IS_PYPY, "skipping for PyPy (why?)") + def test_shape(self): + for shape in [[4, 16], [5, 13]]: + w, h = shape + sf = pygame.Surface(shape, 0, 32) + ar = pygame.PixelArray(sf) + ai = arrinter.ArrayInterface(ar) + ai_shape = [ai.shape[i] for i in range(ai.nd)] + self.assertEqual(ai_shape, shape) + ar2 = ar[::2, :] + ai2 = arrinter.ArrayInterface(ar2) + w2 = len(([0] * w)[::2]) + ai_shape = [ai2.shape[i] for i in range(ai2.nd)] + self.assertEqual(ai_shape, [w2, h]) + ar2 = ar[:, ::2] + ai2 = arrinter.ArrayInterface(ar2) + h2 = len(([0] * h)[::2]) + ai_shape = [ai2.shape[i] for i in range(ai2.nd)] + self.assertEqual(ai_shape, [w, h2]) + + @unittest.skipIf(IS_PYPY, "skipping for PyPy (why?)") + def test_itemsize(self): + for bytes_per_pixel in range(1, 5): + bits_per_pixel = 8 * bytes_per_pixel + sf = pygame.Surface((2, 2), 0, bits_per_pixel) + ar = pygame.PixelArray(sf) + ai = arrinter.ArrayInterface(ar) + self.assertEqual(ai.itemsize, bytes_per_pixel) + + @unittest.skipIf(IS_PYPY, "skipping for PyPy (why?)") + def test_flags(self): + aim = arrinter + common_flags = aim.PAI_NOTSWAPPED | aim.PAI_WRITEABLE | aim.PAI_ALIGNED + s = pygame.Surface((10, 2), 0, 32) + ar = pygame.PixelArray(s) + ai = aim.ArrayInterface(ar) + self.assertEqual(ai.flags, common_flags | aim.PAI_FORTRAN) + + ar2 = ar[::2, :] + ai = aim.ArrayInterface(ar2) + self.assertEqual(ai.flags, common_flags) + + s = pygame.Surface((8, 2), 0, 24) + ar = pygame.PixelArray(s) + ai = aim.ArrayInterface(ar) + self.assertEqual(ai.flags, common_flags | aim.PAI_FORTRAN) + + s = pygame.Surface((7, 2), 0, 24) + ar = pygame.PixelArray(s) + ai = aim.ArrayInterface(ar) + self.assertEqual(ai.flags, common_flags) + + def test_slicing(self): + # This will implicitly test data and strides fields. + # + # Need an 8 bit test surfaces because pixelcopy.make_surface + # returns an 8 bit surface for a 2d array. + + factors = [7, 3, 11] + + w = reduce(operator.mul, factors, 1) + h = 13 + sf = pygame.Surface((w, h), 0, 8) + color = sf.map_rgb((1, 17, 128)) + ar = pygame.PixelArray(sf) + for f in factors[:-1]: + w = w // f + sf.fill((0, 0, 0)) + ar = ar[f : f + w, :] + ar[0][0] = color + ar[-1][-2] = color + ar[0][-3] = color + sf2 = ar.make_surface() + sf3 = pygame.pixelcopy.make_surface(ar) + self.assert_surfaces_equal(sf3, sf2) + + h = reduce(operator.mul, factors, 1) + w = 13 + sf = pygame.Surface((w, h), 0, 8) + color = sf.map_rgb((1, 17, 128)) + ar = pygame.PixelArray(sf) + for f in factors[:-1]: + h = h // f + sf.fill((0, 0, 0)) + ar = ar[:, f : f + h] + ar[0][0] = color + ar[-1][-2] = color + ar[0][-3] = color + sf2 = ar.make_surface() + sf3 = pygame.pixelcopy.make_surface(ar) + self.assert_surfaces_equal(sf3, sf2) + + w = 20 + h = 10 + sf = pygame.Surface((w, h), 0, 8) + color = sf.map_rgb((1, 17, 128)) + ar = pygame.PixelArray(sf) + for slices in [ + (slice(w), slice(h)), + (slice(0, w, 2), slice(h)), + (slice(0, w, 3), slice(h)), + (slice(w), slice(0, h, 2)), + (slice(w), slice(0, h, 3)), + (slice(0, w, 2), slice(0, h, 2)), + (slice(0, w, 3), slice(0, h, 3)), + ]: + sf.fill((0, 0, 0)) + ar2 = ar[slices] + ar2[0][0] = color + ar2[-1][-2] = color + ar2[0][-3] = color + sf2 = ar2.make_surface() + sf3 = pygame.pixelcopy.make_surface(ar2) + self.assert_surfaces_equal(sf3, sf2) + + +@unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") +@unittest.skipIf(IS_PYPY, "pypy having issues") +class PixelArrayNewBufferTest(unittest.TestCase, TestMixin): + if pygame.HAVE_NEWBUF: + from pygame.tests.test_utils import buftools + + bitsize_to_format = {8: "B", 16: "=H", 24: "3x", 32: "=I"} + + def test_newbuf_2D(self): + buftools = self.buftools + Importer = buftools.Importer + + for bit_size in [8, 16, 24, 32]: + s = pygame.Surface((10, 2), 0, bit_size) + ar = pygame.PixelArray(s) + format = self.bitsize_to_format[bit_size] + itemsize = ar.itemsize + shape = ar.shape + w, h = shape + strides = ar.strides + length = w * h * itemsize + imp = Importer(ar, buftools.PyBUF_FULL) + self.assertTrue(imp.obj, ar) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 2) + self.assertEqual(imp.itemsize, itemsize) + self.assertEqual(imp.format, format) + self.assertFalse(imp.readonly) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertTrue(imp.suboffsets is None) + self.assertEqual(imp.buf, s._pixels_address) + + s = pygame.Surface((8, 16), 0, 32) + ar = pygame.PixelArray(s) + format = self.bitsize_to_format[s.get_bitsize()] + itemsize = ar.itemsize + shape = ar.shape + w, h = shape + strides = ar.strides + length = w * h * itemsize + imp = Importer(ar, buftools.PyBUF_SIMPLE) + self.assertTrue(imp.obj, ar) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 0) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.format is None) + self.assertFalse(imp.readonly) + self.assertTrue(imp.shape is None) + self.assertTrue(imp.strides is None) + self.assertTrue(imp.suboffsets is None) + self.assertEqual(imp.buf, s._pixels_address) + imp = Importer(ar, buftools.PyBUF_FORMAT) + self.assertEqual(imp.ndim, 0) + self.assertEqual(imp.format, format) + imp = Importer(ar, buftools.PyBUF_WRITABLE) + self.assertEqual(imp.ndim, 0) + self.assertTrue(imp.format is None) + imp = Importer(ar, buftools.PyBUF_F_CONTIGUOUS) + self.assertEqual(imp.ndim, 2) + self.assertTrue(imp.format is None) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + imp = Importer(ar, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertEqual(imp.ndim, 2) + self.assertTrue(imp.format is None) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_ND) + + ar_sliced = ar[:, ::2] + format = self.bitsize_to_format[s.get_bitsize()] + itemsize = ar_sliced.itemsize + shape = ar_sliced.shape + w, h = shape + strides = ar_sliced.strides + length = w * h * itemsize + imp = Importer(ar_sliced, buftools.PyBUF_STRIDED) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 2) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.format is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertEqual(imp.buf, s._pixels_address) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises( + BufferError, Importer, ar_sliced, buftools.PyBUF_ANY_CONTIGUOUS + ) + + ar_sliced = ar[::2, :] + format = self.bitsize_to_format[s.get_bitsize()] + itemsize = ar_sliced.itemsize + shape = ar_sliced.shape + w, h = shape + strides = ar_sliced.strides + length = w * h * itemsize + imp = Importer(ar_sliced, buftools.PyBUF_STRIDED) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 2) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.format is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertEqual(imp.buf, s._pixels_address) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises( + BufferError, Importer, ar_sliced, buftools.PyBUF_ANY_CONTIGUOUS + ) + + s2 = s.subsurface((2, 3, 5, 7)) + ar = pygame.PixelArray(s2) + format = self.bitsize_to_format[s.get_bitsize()] + itemsize = ar.itemsize + shape = ar.shape + w, h = shape + strides = ar.strides + length = w * h * itemsize + imp = Importer(ar, buftools.PyBUF_STRIDES) + self.assertTrue(imp.obj, ar) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 2) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.format is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertTrue(imp.suboffsets is None) + self.assertEqual(imp.buf, s2._pixels_address) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_FORMAT) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_WRITABLE) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_ANY_CONTIGUOUS) + + def test_newbuf_1D(self): + buftools = self.buftools + Importer = buftools.Importer + + s = pygame.Surface((2, 16), 0, 32) + ar_2D = pygame.PixelArray(s) + x = 0 + ar = ar_2D[x] + format = self.bitsize_to_format[s.get_bitsize()] + itemsize = ar.itemsize + shape = ar.shape + h = shape[0] + strides = ar.strides + length = h * itemsize + buf = s._pixels_address + x * itemsize + imp = Importer(ar, buftools.PyBUF_STRIDES) + self.assertTrue(imp.obj, ar) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 1) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.format is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertTrue(imp.suboffsets is None) + self.assertEqual(imp.buf, buf) + imp = Importer(ar, buftools.PyBUF_FULL) + self.assertEqual(imp.ndim, 1) + self.assertEqual(imp.format, format) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_FORMAT) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_WRITABLE) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_ANY_CONTIGUOUS) + y = 10 + ar = ar_2D[:, y] + shape = ar.shape + w = shape[0] + strides = ar.strides + length = w * itemsize + buf = s._pixels_address + y * s.get_pitch() + imp = Importer(ar, buftools.PyBUF_FULL) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 1) + self.assertEqual(imp.itemsize, itemsize) + self.assertEqual(imp.format, format) + self.assertFalse(imp.readonly) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertEqual(imp.buf, buf) + self.assertTrue(imp.suboffsets is None) + imp = Importer(ar, buftools.PyBUF_SIMPLE) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 0) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.format is None) + self.assertFalse(imp.readonly) + self.assertTrue(imp.shape is None) + self.assertTrue(imp.strides is None) + imp = Importer(ar, buftools.PyBUF_ND) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 1) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.format is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.shape, shape) + self.assertTrue(imp.strides is None) + imp = Importer(ar, buftools.PyBUF_C_CONTIGUOUS) + self.assertEqual(imp.ndim, 1) + imp = Importer(ar, buftools.PyBUF_F_CONTIGUOUS) + self.assertEqual(imp.ndim, 1) + imp = Importer(ar, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertEqual(imp.ndim, 1) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/pixelcopy_test.py b/.venv/Lib/site-packages/pygame/tests/pixelcopy_test.py new file mode 100644 index 00000000..6510fd99 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/pixelcopy_test.py @@ -0,0 +1,710 @@ +import platform +import unittest + +try: + from pygame.tests.test_utils import arrinter +except NameError: + pass +import pygame +from pygame.locals import * +from pygame.pixelcopy import surface_to_array, map_array, array_to_surface, make_surface + +IS_PYPY = "PyPy" == platform.python_implementation() + + +def unsigned32(i): + """cast signed 32 bit integer to an unsigned integer""" + return i & 0xFFFFFFFF + + +@unittest.skipIf(IS_PYPY, "pypy having illegal instruction on mac") +class PixelcopyModuleTest(unittest.TestCase): + bitsizes = [8, 16, 32] + + test_palette = [ + (0, 0, 0, 255), + (10, 30, 60, 255), + (25, 75, 100, 255), + (100, 150, 200, 255), + (0, 100, 200, 255), + ] + + surf_size = (10, 12) + test_points = [ + ((0, 0), 1), + ((4, 5), 1), + ((9, 0), 2), + ((5, 5), 2), + ((0, 11), 3), + ((4, 6), 3), + ((9, 11), 4), + ((5, 6), 4), + ] + + def __init__(self, *args, **kwds): + pygame.display.init() + try: + unittest.TestCase.__init__(self, *args, **kwds) + self.sources = [ + self._make_src_surface(8), + self._make_src_surface(16), + self._make_src_surface(16, srcalpha=True), + self._make_src_surface(24), + self._make_src_surface(32), + self._make_src_surface(32, srcalpha=True), + ] + finally: + pygame.display.quit() + + def _make_surface(self, bitsize, srcalpha=False, palette=None): + if palette is None: + palette = self.test_palette + flags = 0 + if srcalpha: + flags |= SRCALPHA + surf = pygame.Surface(self.surf_size, flags, bitsize) + if bitsize == 8: + surf.set_palette([c[:3] for c in palette]) + return surf + + def _fill_surface(self, surf, palette=None): + if palette is None: + palette = self.test_palette + surf.fill(palette[1], (0, 0, 5, 6)) + surf.fill(palette[2], (5, 0, 5, 6)) + surf.fill(palette[3], (0, 6, 5, 6)) + surf.fill(palette[4], (5, 6, 5, 6)) + + def _make_src_surface(self, bitsize, srcalpha=False, palette=None): + surf = self._make_surface(bitsize, srcalpha, palette) + self._fill_surface(surf, palette) + return surf + + def setUp(self): + pygame.display.init() + + def tearDown(self): + pygame.display.quit() + + def test_surface_to_array_2d(self): + alpha_color = (0, 0, 0, 128) + + for surf in self.sources: + src_bitsize = surf.get_bitsize() + for dst_bitsize in self.bitsizes: + # dst in a surface standing in for a 2 dimensional array + # of unsigned integers. The byte order is system dependent. + dst = pygame.Surface(surf.get_size(), 0, dst_bitsize) + dst.fill((0, 0, 0, 0)) + view = dst.get_view("2") + self.assertFalse(surf.get_locked()) + if dst_bitsize < src_bitsize: + self.assertRaises(ValueError, surface_to_array, view, surf) + self.assertFalse(surf.get_locked()) + continue + surface_to_array(view, surf) + self.assertFalse(surf.get_locked()) + for posn, i in self.test_points: + sp = surf.get_at_mapped(posn) + dp = dst.get_at_mapped(posn) + self.assertEqual( + dp, + sp, + "%s != %s: flags: %i" + ", bpp: %i, posn: %s" + % (dp, sp, surf.get_flags(), surf.get_bitsize(), posn), + ) + del view + + if surf.get_masks()[3]: + dst.fill((0, 0, 0, 0)) + view = dst.get_view("2") + posn = (2, 1) + surf.set_at(posn, alpha_color) + self.assertFalse(surf.get_locked()) + surface_to_array(view, surf) + self.assertFalse(surf.get_locked()) + sp = surf.get_at_mapped(posn) + dp = dst.get_at_mapped(posn) + self.assertEqual( + dp, sp, "%s != %s: bpp: %i" % (dp, sp, surf.get_bitsize()) + ) + + if IS_PYPY: + return + # Swapped endian destination array + pai_flags = arrinter.PAI_ALIGNED | arrinter.PAI_WRITEABLE + for surf in self.sources: + for itemsize in [1, 2, 4, 8]: + if itemsize < surf.get_bytesize(): + continue + a = arrinter.Array(surf.get_size(), "u", itemsize, flags=pai_flags) + surface_to_array(a, surf) + for posn, i in self.test_points: + sp = unsigned32(surf.get_at_mapped(posn)) + dp = a[posn] + self.assertEqual( + dp, + sp, + "%s != %s: itemsize: %i, flags: %i" + ", bpp: %i, posn: %s" + % ( + dp, + sp, + itemsize, + surf.get_flags(), + surf.get_bitsize(), + posn, + ), + ) + + def test_surface_to_array_3d(self): + self.iter_surface_to_array_3d((0xFF, 0xFF00, 0xFF0000, 0)) + self.iter_surface_to_array_3d((0xFF0000, 0xFF00, 0xFF, 0)) + + def iter_surface_to_array_3d(self, rgba_masks): + dst = pygame.Surface(self.surf_size, 0, 24, masks=rgba_masks) + + for surf in self.sources: + dst.fill((0, 0, 0, 0)) + src_bitsize = surf.get_bitsize() + view = dst.get_view("3") + self.assertFalse(surf.get_locked()) + surface_to_array(view, surf) + self.assertFalse(surf.get_locked()) + for posn, i in self.test_points: + sc = surf.get_at(posn)[0:3] + dc = dst.get_at(posn)[0:3] + self.assertEqual( + dc, + sc, + "%s != %s: flags: %i" + ", bpp: %i, posn: %s" + % (dc, sc, surf.get_flags(), surf.get_bitsize(), posn), + ) + view = None + + def test_map_array(self): + targets = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] + source = pygame.Surface( + self.surf_size, 0, 24, masks=[0xFF, 0xFF00, 0xFF0000, 0] + ) + self._fill_surface(source) + source_view = source.get_view("3") # (w, h, 3) + for t in targets: + map_array(t.get_view("2"), source_view, t) + for posn, i in self.test_points: + sc = t.map_rgb(source.get_at(posn)) + dc = t.get_at_mapped(posn) + self.assertEqual( + dc, + sc, + "%s != %s: flags: %i" + ", bpp: %i, posn: %s" + % (dc, sc, t.get_flags(), t.get_bitsize(), posn), + ) + + color = pygame.Color("salmon") + color.set_length(3) + for t in targets: + map_array(t.get_view("2"), color, t) + sc = t.map_rgb(color) + for posn, i in self.test_points: + dc = t.get_at_mapped(posn) + self.assertEqual( + dc, + sc, + "%s != %s: flags: %i" + ", bpp: %i, posn: %s" + % (dc, sc, t.get_flags(), t.get_bitsize(), posn), + ) + + # mismatched shapes + w, h = source.get_size() + target = pygame.Surface((w, h + 1), 0, 32) + self.assertRaises(ValueError, map_array, target, source, target) + target = pygame.Surface((w - 1, h), 0, 32) + self.assertRaises(ValueError, map_array, target, source, target) + + def test_array_to_surface_broadcasting(self): + # target surfaces + targets = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] + + w, h = self.surf_size + + # broadcast column + column = pygame.Surface((1, h), 0, 32) + for target in targets: + source = pygame.Surface((1, h), 0, target) + for y in range(h): + source.set_at((0, y), pygame.Color(y + 1, y + h + 1, y + 2 * h + 1)) + pygame.pixelcopy.surface_to_array(column.get_view("2"), source) + pygame.pixelcopy.array_to_surface(target, column.get_view("2")) + for x in range(w): + for y in range(h): + self.assertEqual( + target.get_at_mapped((x, y)), column.get_at_mapped((0, y)) + ) + + # broadcast row + row = pygame.Surface((w, 1), 0, 32) + for target in targets: + source = pygame.Surface((w, 1), 0, target) + for x in range(w): + source.set_at((x, 0), pygame.Color(x + 1, x + w + 1, x + 2 * w + 1)) + pygame.pixelcopy.surface_to_array(row.get_view("2"), source) + pygame.pixelcopy.array_to_surface(target, row.get_view("2")) + for x in range(w): + for y in range(h): + self.assertEqual( + target.get_at_mapped((x, y)), row.get_at_mapped((x, 0)) + ) + + # broadcast pixel + pixel = pygame.Surface((1, 1), 0, 32) + for target in targets: + source = pygame.Surface((1, 1), 0, target) + source.set_at((0, 0), pygame.Color(13, 47, 101)) + pygame.pixelcopy.surface_to_array(pixel.get_view("2"), source) + pygame.pixelcopy.array_to_surface(target, pixel.get_view("2")) + p = pixel.get_at_mapped((0, 0)) + for x in range(w): + for y in range(h): + self.assertEqual(target.get_at_mapped((x, y)), p) + + +@unittest.skipIf(IS_PYPY, "pypy having illegal instruction on mac") +class PixelCopyTestWithArrayNumpy(unittest.TestCase): + try: + import numpy + except ImportError: + __tags__ = ["ignore", "subprocess_ignore"] + else: + pygame.surfarray.use_arraytype("numpy") + + bitsizes = [8, 16, 32] + + test_palette = [ + (0, 0, 0, 255), + (10, 30, 60, 255), + (25, 75, 100, 255), + (100, 150, 200, 255), + (0, 100, 200, 255), + ] + + surf_size = (10, 12) + test_points = [ + ((0, 0), 1), + ((4, 5), 1), + ((9, 0), 2), + ((5, 5), 2), + ((0, 11), 3), + ((4, 6), 3), + ((9, 11), 4), + ((5, 6), 4), + ] + + pixels2d = {8, 16, 32} + pixels3d = {24, 32} + array2d = {8, 16, 24, 32} + array3d = {24, 32} + + def __init__(self, *args, **kwds): + import numpy + + self.dst_types = [numpy.uint8, numpy.uint16, numpy.uint32] + try: + self.dst_types.append(numpy.uint64) + except AttributeError: + pass + pygame.display.init() + try: + unittest.TestCase.__init__(self, *args, **kwds) + self.sources = [ + self._make_src_surface(8), + self._make_src_surface(16), + self._make_src_surface(16, srcalpha=True), + self._make_src_surface(24), + self._make_src_surface(32), + self._make_src_surface(32, srcalpha=True), + ] + finally: + pygame.display.quit() + + def _make_surface(self, bitsize, srcalpha=False, palette=None): + if palette is None: + palette = self.test_palette + flags = 0 + if srcalpha: + flags |= SRCALPHA + surf = pygame.Surface(self.surf_size, flags, bitsize) + if bitsize == 8: + surf.set_palette([c[:3] for c in palette]) + return surf + + def _fill_surface(self, surf, palette=None): + if palette is None: + palette = self.test_palette + surf.fill(palette[1], (0, 0, 5, 6)) + surf.fill(palette[2], (5, 0, 5, 6)) + surf.fill(palette[3], (0, 6, 5, 6)) + surf.fill(palette[4], (5, 6, 5, 6)) + + def _make_src_surface(self, bitsize, srcalpha=False, palette=None): + surf = self._make_surface(bitsize, srcalpha, palette) + self._fill_surface(surf, palette) + return surf + + def setUp(self): + pygame.display.init() + + def tearDown(self): + pygame.display.quit() + + def test_surface_to_array_2d(self): + try: + from numpy import empty, dtype + except ImportError: + return + + palette = self.test_palette + alpha_color = (0, 0, 0, 128) + + dst_dims = self.surf_size + destinations = [empty(dst_dims, t) for t in self.dst_types] + if pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN: + swapped_dst = empty(dst_dims, dtype(">u4")) + else: + swapped_dst = empty(dst_dims, dtype("u4")) + else: + swapped_dst = empty(dst_dims, dtype("i", + "!i", + "1i", + "=1i", + "@q", + "q", + "4x", + "8x", + ]: + surface.fill((255, 254, 253)) + exp = Exporter(shape, format=format) + exp._buf[:] = [42] * exp.buflen + array_to_surface(surface, exp) + for x in range(w): + for y in range(h): + self.assertEqual(surface.get_at((x, y)), (42, 42, 42, 255)) + # Some unsupported formats for array_to_surface and a 32 bit surface + for format in ["f", "d", "?", "x", "1x", "2x", "3x", "5x", "6x", "7x", "9x"]: + exp = Exporter(shape, format=format) + self.assertRaises(ValueError, array_to_surface, surface, exp) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/rect_test.py b/.venv/Lib/site-packages/pygame/tests/rect_test.py new file mode 100644 index 00000000..a17afbdf --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/rect_test.py @@ -0,0 +1,3313 @@ +import math +import unittest +from collections.abc import Collection, Sequence +import platform +import random +import unittest + +from pygame import Rect, Vector2 +from pygame.tests import test_utils + +IS_PYPY = "PyPy" == platform.python_implementation() + +# todo can they be different on different platforms? +_int_min = -2147483647 - 1 # min value of int in C +_int_max = 2147483647 # max value of int in C + + +def _random_int(): + return random.randint(_int_min, _int_max) + + +class RectTypeTest(unittest.TestCase): + def _assertCountEqual(self, *args, **kwargs): + self.assertCountEqual(*args, **kwargs) + + def testConstructionXYWidthHeight(self): + r = Rect(1, 2, 3, 4) + self.assertEqual(1, r.left) + self.assertEqual(2, r.top) + self.assertEqual(3, r.width) + self.assertEqual(4, r.height) + + def testConstructionTopLeftSize(self): + r = Rect((1, 2), (3, 4)) + self.assertEqual(1, r.left) + self.assertEqual(2, r.top) + self.assertEqual(3, r.width) + self.assertEqual(4, r.height) + + def testCalculatedAttributes(self): + r = Rect(1, 2, 3, 4) + + self.assertEqual(r.left + r.width, r.right) + self.assertEqual(r.top + r.height, r.bottom) + self.assertEqual((r.width, r.height), r.size) + self.assertEqual((r.left, r.top), r.topleft) + self.assertEqual((r.right, r.top), r.topright) + self.assertEqual((r.left, r.bottom), r.bottomleft) + self.assertEqual((r.right, r.bottom), r.bottomright) + + midx = r.left + r.width // 2 + midy = r.top + r.height // 2 + + self.assertEqual(midx, r.centerx) + self.assertEqual(midy, r.centery) + self.assertEqual((r.centerx, r.centery), r.center) + self.assertEqual((r.centerx, r.top), r.midtop) + self.assertEqual((r.centerx, r.bottom), r.midbottom) + self.assertEqual((r.left, r.centery), r.midleft) + self.assertEqual((r.right, r.centery), r.midright) + + def test_rect_iter(self): + rect = Rect(50, 100, 150, 200) + + # call __iter__ explicitly to test that it is defined + rect_iterator = rect.__iter__() + for i, val in enumerate(rect_iterator): + self.assertEqual(rect[i], val) + + def test_normalize(self): + """Ensures normalize works when width and height are both negative.""" + test_rect = Rect((1, 2), (-3, -6)) + expected_normalized_rect = ( + (test_rect.x + test_rect.w, test_rect.y + test_rect.h), + (-test_rect.w, -test_rect.h), + ) + + test_rect.normalize() + + self.assertEqual(test_rect, expected_normalized_rect) + + def test_normalize__positive_height(self): + """Ensures normalize works with a negative width and a positive height.""" + test_rect = Rect((1, 2), (-3, 6)) + expected_normalized_rect = ( + (test_rect.x + test_rect.w, test_rect.y), + (-test_rect.w, test_rect.h), + ) + + test_rect.normalize() + + self.assertEqual(test_rect, expected_normalized_rect) + + def test_normalize__positive_width(self): + """Ensures normalize works with a positive width and a negative height.""" + test_rect = Rect((1, 2), (3, -6)) + expected_normalized_rect = ( + (test_rect.x, test_rect.y + test_rect.h), + (test_rect.w, -test_rect.h), + ) + + test_rect.normalize() + + self.assertEqual(test_rect, expected_normalized_rect) + + def test_normalize__zero_height(self): + """Ensures normalize works with a negative width and a zero height.""" + test_rect = Rect((1, 2), (-3, 0)) + expected_normalized_rect = ( + (test_rect.x + test_rect.w, test_rect.y), + (-test_rect.w, test_rect.h), + ) + + test_rect.normalize() + + self.assertEqual(test_rect, expected_normalized_rect) + + def test_normalize__zero_width(self): + """Ensures normalize works with a zero width and a negative height.""" + test_rect = Rect((1, 2), (0, -6)) + expected_normalized_rect = ( + (test_rect.x, test_rect.y + test_rect.h), + (test_rect.w, -test_rect.h), + ) + + test_rect.normalize() + + self.assertEqual(test_rect, expected_normalized_rect) + + def test_normalize__non_negative(self): + """Ensures normalize works when width and height are both non-negative. + + Tests combinations of positive and zero values for width and height. + The normalize method has no impact when both width and height are + non-negative. + """ + for size in ((3, 6), (3, 0), (0, 6), (0, 0)): + test_rect = Rect((1, 2), size) + expected_normalized_rect = Rect(test_rect) + + test_rect.normalize() + + self.assertEqual(test_rect, expected_normalized_rect) + + def test_x(self): + """Ensures changing the x attribute moves the rect and does not change + the rect's size. + """ + expected_x = 10 + expected_y = 2 + expected_size = (3, 4) + r = Rect((1, expected_y), expected_size) + + r.x = expected_x + + self.assertEqual(r.x, expected_x) + self.assertEqual(r.x, r.left) + self.assertEqual(r.y, expected_y) + self.assertEqual(r.size, expected_size) + + def test_x__invalid_value(self): + """Ensures the x attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.x = value + + def test_x__del(self): + """Ensures the x attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.x + + def test_y(self): + """Ensures changing the y attribute moves the rect and does not change + the rect's size. + """ + expected_x = 1 + expected_y = 20 + expected_size = (3, 4) + r = Rect((expected_x, 2), expected_size) + + r.y = expected_y + + self.assertEqual(r.y, expected_y) + self.assertEqual(r.y, r.top) + self.assertEqual(r.x, expected_x) + self.assertEqual(r.size, expected_size) + + def test_y__invalid_value(self): + """Ensures the y attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.y = value + + def test_y__del(self): + """Ensures the y attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.y + + def test_left(self): + """Changing the left attribute moves the rect and does not change + the rect's width + """ + r = Rect(1, 2, 3, 4) + new_left = 10 + + r.left = new_left + self.assertEqual(new_left, r.left) + self.assertEqual(Rect(new_left, 2, 3, 4), r) + + def test_left__invalid_value(self): + """Ensures the left attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.left = value + + def test_left__del(self): + """Ensures the left attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.left + + def test_right(self): + """Changing the right attribute moves the rect and does not change + the rect's width + """ + r = Rect(1, 2, 3, 4) + new_right = r.right + 20 + expected_left = r.left + 20 + old_width = r.width + + r.right = new_right + self.assertEqual(new_right, r.right) + self.assertEqual(expected_left, r.left) + self.assertEqual(old_width, r.width) + + def test_right__invalid_value(self): + """Ensures the right attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.right = value + + def test_right__del(self): + """Ensures the right attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.right + + def test_top(self): + """Changing the top attribute moves the rect and does not change + the rect's width + """ + r = Rect(1, 2, 3, 4) + new_top = 10 + + r.top = new_top + self.assertEqual(Rect(1, new_top, 3, 4), r) + self.assertEqual(new_top, r.top) + + def test_top__invalid_value(self): + """Ensures the top attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.top = value + + def test_top__del(self): + """Ensures the top attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.top + + def test_bottom(self): + """Changing the bottom attribute moves the rect and does not change + the rect's height + """ + r = Rect(1, 2, 3, 4) + new_bottom = r.bottom + 20 + expected_top = r.top + 20 + old_height = r.height + + r.bottom = new_bottom + self.assertEqual(new_bottom, r.bottom) + self.assertEqual(expected_top, r.top) + self.assertEqual(old_height, r.height) + + def test_bottom__invalid_value(self): + """Ensures the bottom attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.bottom = value + + def test_bottom__del(self): + """Ensures the bottom attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.bottom + + def test_centerx(self): + """Changing the centerx attribute moves the rect and does not change + the rect's width + """ + r = Rect(1, 2, 3, 4) + new_centerx = r.centerx + 20 + expected_left = r.left + 20 + old_width = r.width + + r.centerx = new_centerx + self.assertEqual(new_centerx, r.centerx) + self.assertEqual(expected_left, r.left) + self.assertEqual(old_width, r.width) + + def test_centerx__invalid_value(self): + """Ensures the centerx attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.centerx = value + + def test_centerx__del(self): + """Ensures the centerx attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.centerx + + def test_centery(self): + """Changing the centery attribute moves the rect and does not change + the rect's width + """ + r = Rect(1, 2, 3, 4) + new_centery = r.centery + 20 + expected_top = r.top + 20 + old_height = r.height + + r.centery = new_centery + self.assertEqual(new_centery, r.centery) + self.assertEqual(expected_top, r.top) + self.assertEqual(old_height, r.height) + + def test_centery__invalid_value(self): + """Ensures the centery attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.centery = value + + def test_centery__del(self): + """Ensures the centery attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.centery + + def test_topleft(self): + """Changing the topleft attribute moves the rect and does not change + the rect's size + """ + r = Rect(1, 2, 3, 4) + new_topleft = (r.left + 20, r.top + 30) + old_size = r.size + + r.topleft = new_topleft + self.assertEqual(new_topleft, r.topleft) + self.assertEqual(old_size, r.size) + + def test_topleft__invalid_value(self): + """Ensures the topleft attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.topleft = value + + def test_topleft__del(self): + """Ensures the topleft attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.topleft + + def test_bottomleft(self): + """Changing the bottomleft attribute moves the rect and does not change + the rect's size + """ + r = Rect(1, 2, 3, 4) + new_bottomleft = (r.left + 20, r.bottom + 30) + expected_topleft = (r.left + 20, r.top + 30) + old_size = r.size + + r.bottomleft = new_bottomleft + self.assertEqual(new_bottomleft, r.bottomleft) + self.assertEqual(expected_topleft, r.topleft) + self.assertEqual(old_size, r.size) + + def test_bottomleft__invalid_value(self): + """Ensures the bottomleft attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.bottomleft = value + + def test_bottomleft__del(self): + """Ensures the bottomleft attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.bottomleft + + def test_topright(self): + """Changing the topright attribute moves the rect and does not change + the rect's size + """ + r = Rect(1, 2, 3, 4) + new_topright = (r.right + 20, r.top + 30) + expected_topleft = (r.left + 20, r.top + 30) + old_size = r.size + + r.topright = new_topright + self.assertEqual(new_topright, r.topright) + self.assertEqual(expected_topleft, r.topleft) + self.assertEqual(old_size, r.size) + + def test_topright__invalid_value(self): + """Ensures the topright attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.topright = value + + def test_topright__del(self): + """Ensures the topright attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.topright + + def test_bottomright(self): + """Changing the bottomright attribute moves the rect and does not change + the rect's size + """ + r = Rect(1, 2, 3, 4) + new_bottomright = (r.right + 20, r.bottom + 30) + expected_topleft = (r.left + 20, r.top + 30) + old_size = r.size + + r.bottomright = new_bottomright + self.assertEqual(new_bottomright, r.bottomright) + self.assertEqual(expected_topleft, r.topleft) + self.assertEqual(old_size, r.size) + + def test_bottomright__invalid_value(self): + """Ensures the bottomright attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.bottomright = value + + def test_bottomright__del(self): + """Ensures the bottomright attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.bottomright + + def test_center(self): + """Changing the center attribute moves the rect and does not change + the rect's size + """ + r = Rect(1, 2, 3, 4) + new_center = (r.centerx + 20, r.centery + 30) + expected_topleft = (r.left + 20, r.top + 30) + old_size = r.size + + r.center = new_center + self.assertEqual(new_center, r.center) + self.assertEqual(expected_topleft, r.topleft) + self.assertEqual(old_size, r.size) + + def test_center__invalid_value(self): + """Ensures the center attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.center = value + + def test_center__del(self): + """Ensures the center attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.center + + def test_midleft(self): + """Changing the midleft attribute moves the rect and does not change + the rect's size + """ + r = Rect(1, 2, 3, 4) + new_midleft = (r.left + 20, r.centery + 30) + expected_topleft = (r.left + 20, r.top + 30) + old_size = r.size + + r.midleft = new_midleft + self.assertEqual(new_midleft, r.midleft) + self.assertEqual(expected_topleft, r.topleft) + self.assertEqual(old_size, r.size) + + def test_midleft__invalid_value(self): + """Ensures the midleft attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.midleft = value + + def test_midleft__del(self): + """Ensures the midleft attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.midleft + + def test_midright(self): + """Changing the midright attribute moves the rect and does not change + the rect's size + """ + r = Rect(1, 2, 3, 4) + new_midright = (r.right + 20, r.centery + 30) + expected_topleft = (r.left + 20, r.top + 30) + old_size = r.size + + r.midright = new_midright + self.assertEqual(new_midright, r.midright) + self.assertEqual(expected_topleft, r.topleft) + self.assertEqual(old_size, r.size) + + def test_midright__invalid_value(self): + """Ensures the midright attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.midright = value + + def test_midright__del(self): + """Ensures the midright attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.midright + + def test_midtop(self): + """Changing the midtop attribute moves the rect and does not change + the rect's size + """ + r = Rect(1, 2, 3, 4) + new_midtop = (r.centerx + 20, r.top + 30) + expected_topleft = (r.left + 20, r.top + 30) + old_size = r.size + + r.midtop = new_midtop + self.assertEqual(new_midtop, r.midtop) + self.assertEqual(expected_topleft, r.topleft) + self.assertEqual(old_size, r.size) + + def test_midtop__invalid_value(self): + """Ensures the midtop attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.midtop = value + + def test_midtop__del(self): + """Ensures the midtop attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.midtop + + def test_midbottom(self): + """Changing the midbottom attribute moves the rect and does not change + the rect's size + """ + r = Rect(1, 2, 3, 4) + new_midbottom = (r.centerx + 20, r.bottom + 30) + expected_topleft = (r.left + 20, r.top + 30) + old_size = r.size + + r.midbottom = new_midbottom + self.assertEqual(new_midbottom, r.midbottom) + self.assertEqual(expected_topleft, r.topleft) + self.assertEqual(old_size, r.size) + + def test_midbottom__invalid_value(self): + """Ensures the midbottom attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.midbottom = value + + def test_midbottom__del(self): + """Ensures the midbottom attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.midbottom + + def test_width(self): + """Changing the width resizes the rect from the top-left corner""" + r = Rect(1, 2, 3, 4) + new_width = 10 + old_topleft = r.topleft + old_height = r.height + + r.width = new_width + self.assertEqual(new_width, r.width) + self.assertEqual(old_height, r.height) + self.assertEqual(old_topleft, r.topleft) + + def test_width__invalid_value(self): + """Ensures the width attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.width = value + + def test_width__del(self): + """Ensures the width attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.width + + def test_height(self): + """Changing the height resizes the rect from the top-left corner""" + r = Rect(1, 2, 3, 4) + new_height = 10 + old_topleft = r.topleft + old_width = r.width + + r.height = new_height + self.assertEqual(new_height, r.height) + self.assertEqual(old_width, r.width) + self.assertEqual(old_topleft, r.topleft) + + def test_height__invalid_value(self): + """Ensures the height attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.height = value + + def test_height__del(self): + """Ensures the height attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.height + + def test_size(self): + """Changing the size resizes the rect from the top-left corner""" + r = Rect(1, 2, 3, 4) + new_size = (10, 20) + old_topleft = r.topleft + + r.size = new_size + self.assertEqual(new_size, r.size) + self.assertEqual(old_topleft, r.topleft) + + def test_size__invalid_value(self): + """Ensures the size attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.size = value + + def test_size__del(self): + """Ensures the size attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.size + + def test_contains(self): + r = Rect(1, 2, 3, 4) + + self.assertTrue( + r.contains(Rect(2, 3, 1, 1)), "r does not contain Rect(2, 3, 1, 1)" + ) + self.assertTrue(Rect(2, 3, 1, 1) in r, "r does not contain Rect(2, 3, 1, 1) 2") + self.assertTrue( + r.contains(Rect(r)), "r does not contain the same rect as itself" + ) + self.assertTrue(r in Rect(r), "r does not contain the same rect as itself") + self.assertTrue( + r.contains(Rect(2, 3, 0, 0)), + "r does not contain an empty rect within its bounds", + ) + self.assertTrue( + Rect(2, 3, 0, 0) in r, + "r does not contain an empty rect within its bounds", + ) + self.assertFalse(r.contains(Rect(0, 0, 1, 2)), "r contains Rect(0, 0, 1, 2)") + self.assertFalse(r.contains(Rect(4, 6, 1, 1)), "r contains Rect(4, 6, 1, 1)") + self.assertFalse(r.contains(Rect(4, 6, 0, 0)), "r contains Rect(4, 6, 0, 0)") + self.assertFalse(Rect(0, 0, 1, 2) in r, "r contains Rect(0, 0, 1, 2)") + self.assertFalse(Rect(4, 6, 1, 1) in r, "r contains Rect(4, 6, 1, 1)") + self.assertFalse(Rect(4, 6, 0, 0) in r, "r contains Rect(4, 6, 0, 0)") + self.assertTrue(2 in Rect(0, 0, 1, 2), "r does not contain 2") + self.assertFalse(3 in Rect(0, 0, 1, 2), "r contains 3") + + self.assertRaises(TypeError, lambda: "string" in Rect(0, 0, 1, 2)) + self.assertRaises(TypeError, lambda: 4 + 3j in Rect(0, 0, 1, 2)) + + def test_collidepoint(self): + r = Rect(1, 2, 3, 4) + + self.assertTrue( + r.collidepoint(r.left, r.top), "r does not collide with point (left, top)" + ) + self.assertFalse( + r.collidepoint(r.left - 1, r.top), "r collides with point (left - 1, top)" + ) + self.assertFalse( + r.collidepoint(r.left, r.top - 1), "r collides with point (left, top - 1)" + ) + self.assertFalse( + r.collidepoint(r.left - 1, r.top - 1), + "r collides with point (left - 1, top - 1)", + ) + + self.assertTrue( + r.collidepoint(r.right - 1, r.bottom - 1), + "r does not collide with point (right - 1, bottom - 1)", + ) + self.assertFalse( + r.collidepoint(r.right, r.bottom), "r collides with point (right, bottom)" + ) + self.assertFalse( + r.collidepoint(r.right - 1, r.bottom), + "r collides with point (right - 1, bottom)", + ) + self.assertFalse( + r.collidepoint(r.right, r.bottom - 1), + "r collides with point (right, bottom - 1)", + ) + + def test_inflate__larger(self): + """The inflate method inflates around the center of the rectangle""" + r = Rect(2, 4, 6, 8) + r2 = r.inflate(4, 6) + + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left - 2, r2.left) + self.assertEqual(r.top - 3, r2.top) + self.assertEqual(r.right + 2, r2.right) + self.assertEqual(r.bottom + 3, r2.bottom) + self.assertEqual(r.width + 4, r2.width) + self.assertEqual(r.height + 6, r2.height) + + def test_inflate__smaller(self): + """The inflate method inflates around the center of the rectangle""" + r = Rect(2, 4, 6, 8) + r2 = r.inflate(-4, -6) + + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left + 2, r2.left) + self.assertEqual(r.top + 3, r2.top) + self.assertEqual(r.right - 2, r2.right) + self.assertEqual(r.bottom - 3, r2.bottom) + self.assertEqual(r.width - 4, r2.width) + self.assertEqual(r.height - 6, r2.height) + + def test_inflate_ip__larger(self): + """The inflate_ip method inflates around the center of the rectangle""" + r = Rect(2, 4, 6, 8) + r2 = Rect(r) + r2.inflate_ip(-4, -6) + + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left + 2, r2.left) + self.assertEqual(r.top + 3, r2.top) + self.assertEqual(r.right - 2, r2.right) + self.assertEqual(r.bottom - 3, r2.bottom) + self.assertEqual(r.width - 4, r2.width) + self.assertEqual(r.height - 6, r2.height) + + def test_inflate_ip__smaller(self): + """The inflate method inflates around the center of the rectangle""" + r = Rect(2, 4, 6, 8) + r2 = Rect(r) + r2.inflate_ip(-4, -6) + + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left + 2, r2.left) + self.assertEqual(r.top + 3, r2.top) + self.assertEqual(r.right - 2, r2.right) + self.assertEqual(r.bottom - 3, r2.bottom) + self.assertEqual(r.width - 4, r2.width) + self.assertEqual(r.height - 6, r2.height) + + def test_scale_by__larger_single_argument(self): + """The scale method scales around the center of the rectangle""" + r = Rect(2, 4, 6, 8) + r2 = r.scale_by(2) + + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left - 3, r2.left) + self.assertEqual(r.top - 4, r2.top) + self.assertEqual(r.right + 3, r2.right) + self.assertEqual(r.bottom + 4, r2.bottom) + self.assertEqual(r.width * 2, r2.width) + self.assertEqual(r.height * 2, r2.height) + + def test_scale_by__larger_single_argument_kwarg(self): + """The scale method scales around the center of the rectangle using + keyword arguments 'x' and 'y'""" + r = Rect(2, 4, 6, 8) + r2 = r.scale_by(x=2) + + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left - 3, r2.left) + self.assertEqual(r.top - 4, r2.top) + self.assertEqual(r.right + 3, r2.right) + self.assertEqual(r.bottom + 4, r2.bottom) + self.assertEqual(r.width * 2, r2.width) + self.assertEqual(r.height * 2, r2.height) + + def test_scale_by__smaller_single_argument(self): + """The scale method scales around the center of the rectangle""" + r = Rect(2, 4, 8, 8) + r2 = r.scale_by(0.5) + + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left + 2, r2.left) + self.assertEqual(r.top + 2, r2.top) + self.assertEqual(r.right - 2, r2.right) + self.assertEqual(r.bottom - 2, r2.bottom) + self.assertEqual(r.width - 4, r2.width) + self.assertEqual(r.height - 4, r2.height) + + def test_scale_by__larger(self): + """The scale method scales around the center of the rectangle""" + # arrange + r = Rect(2, 4, 6, 8) + # act + r2 = r.scale_by(2, 4) + # assert + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left - 3, r2.left) + self.assertEqual(r.centery - r.h * 4 / 2, r2.top) + self.assertEqual(r.right + 3, r2.right) + self.assertEqual(r.centery + r.h * 4 / 2, r2.bottom) + self.assertEqual(r.width * 2, r2.width) + self.assertEqual(r.height * 4, r2.height) + + def test_scale_by__larger_kwargs_scale_by(self): + """ + The scale method scales around the center of the rectangle + Uses 'scale_by' kwarg. + """ + # arrange + r = Rect(2, 4, 6, 8) + # act + r2 = r.scale_by(scale_by=(2, 4)) + # assert + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left - 3, r2.left) + self.assertEqual(r.centery - r.h * 4 / 2, r2.top) + self.assertEqual(r.right + 3, r2.right) + self.assertEqual(r.centery + r.h * 4 / 2, r2.bottom) + self.assertEqual(r.width * 2, r2.width) + self.assertEqual(r.height * 4, r2.height) + + def test_scale_by__larger_kwargs(self): + """ + The scale method scales around the center of the rectangle + Uses 'x' and 'y' kwargs. + """ + # arrange + r = Rect(2, 4, 6, 8) + # act + r2 = r.scale_by(x=2, y=4) + # assert + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left - 3, r2.left) + self.assertEqual(r.centery - r.h * 4 / 2, r2.top) + self.assertEqual(r.right + 3, r2.right) + self.assertEqual(r.centery + r.h * 4 / 2, r2.bottom) + self.assertEqual(r.width * 2, r2.width) + self.assertEqual(r.height * 4, r2.height) + + def test_scale_by__smaller(self): + """The scale method scales around the center of the rectangle""" + # arrange + r = Rect(2, 4, 8, 8) + # act + r2 = r.scale_by(0.5, 0.25) + # assert + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left + 2, r2.left) + self.assertEqual(r.centery - r.h / 4 / 2, r2.top) + self.assertEqual(r.right - 2, r2.right) + self.assertEqual(r.centery + r.h / 4 / 2, r2.bottom) + self.assertEqual(r.width - 4, r2.width) + self.assertEqual(r.height // 4, r2.height) + + def test_scale_by__subzero(self): + """The scale method scales around the center of the rectangle""" + r = Rect(2, 4, 6, 8) + r.scale_by(0) + r.scale_by(-1) + r.scale_by(-0.000001) + r.scale_by(0.00001) + + rx1 = r.scale_by(10, 1) + self.assertEqual(r.centerx - r.w * 10 / 2, rx1.x) + self.assertEqual(r.y, rx1.y) + self.assertEqual(r.w * 10, rx1.w) + self.assertEqual(r.h, rx1.h) + rx2 = r.scale_by(-10, 1) + self.assertEqual(rx1.x, rx2.x) + self.assertEqual(rx1.y, rx2.y) + self.assertEqual(rx1.w, rx2.w) + self.assertEqual(rx1.h, rx2.h) + + ry1 = r.scale_by(1, 10) + self.assertEqual(r.x, ry1.x) + self.assertEqual(r.centery - r.h * 10 / 2, ry1.y) + self.assertEqual(r.w, ry1.w) + self.assertEqual(r.h * 10, ry1.h) + ry2 = r.scale_by(1, -10) + self.assertEqual(ry1.x, ry2.x) + self.assertEqual(ry1.y, ry2.y) + self.assertEqual(ry1.w, ry2.w) + self.assertEqual(ry1.h, ry2.h) + + r1 = r.scale_by(10) + self.assertEqual(r.centerx - r.w * 10 / 2, r1.x) + self.assertEqual(r.centery - r.h * 10 / 2, r1.y) + self.assertEqual(r.w * 10, r1.w) + self.assertEqual(r.h * 10, r1.h) + + def test_scale_by_identity(self): + """The scale method scales around the center of the rectangle""" + # arrange + r = Rect(2, 4, 6, 8) + # act + actual = r.scale_by(1, 1) + # assert + self.assertEqual(r.x, actual.x) + self.assertEqual(r.y, actual.y) + self.assertEqual(r.w, actual.w) + self.assertEqual(r.h, actual.h) + + def test_scale_by_negative_identity(self): + """The scale method scales around the center of the rectangle""" + # arrange + r = Rect(2, 4, 6, 8) + # act + actual = r.scale_by(-1, -1) + # assert + self.assertEqual(r.x, actual.x) + self.assertEqual(r.y, actual.y) + self.assertEqual(r.w, actual.w) + self.assertEqual(r.h, actual.h) + + def test_scale_by_identity_single_argument(self): + """The scale method scales around the center of the rectangle""" + # arrange + r = Rect(2, 4, 6, 8) + # act + actual = r.scale_by(1) + # assert + self.assertEqual(r.x, actual.x) + self.assertEqual(r.y, actual.y) + self.assertEqual(r.w, actual.w) + self.assertEqual(r.h, actual.h) + + def test_scale_by_negative_identity_single_argment(self): + """The scale method scales around the center of the rectangle""" + # arrange + r = Rect(2, 4, 6, 8) + # act + actual = r.scale_by(-1) + # assert + self.assertEqual(r.x, actual.x) + self.assertEqual(r.y, actual.y) + self.assertEqual(r.w, actual.w) + self.assertEqual(r.h, actual.h) + + def test_scale_by_ip__larger(self): + """The scale method scales around the center of the rectangle""" + r = Rect(2, 4, 6, 8) + r2 = Rect(r) + r2.scale_by_ip(2) + + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left - 3, r2.left) + self.assertEqual(r.top - 4, r2.top) + self.assertEqual(r.right + 3, r2.right) + self.assertEqual(r.bottom + 4, r2.bottom) + self.assertEqual(r.width * 2, r2.width) + self.assertEqual(r.height * 2, r2.height) + + def test_scale_by_ip__smaller(self): + """The scale method scales around the center of the rectangle""" + r = Rect(2, 4, 8, 8) + r2 = Rect(r) + r2.scale_by_ip(0.5) + + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left + 2, r2.left) + self.assertEqual(r.top + 2, r2.top) + self.assertEqual(r.right - 2, r2.right) + self.assertEqual(r.bottom - 2, r2.bottom) + self.assertEqual(r.width / 2, r2.width) + self.assertEqual(r.height / 2, r2.height) + + def test_scale_by_ip__subzero(self): + """The scale method scales around the center of the rectangle""" + r = Rect(2, 4, 6, 8) + r.scale_by_ip(0) + r.scale_by_ip(-1) + r.scale_by_ip(-0.000001) + r.scale_by_ip(0.00001) + + def test_scale_by_ip__kwargs(self): + """The scale method scales around the center of the rectangle""" + r = Rect(2, 4, 6, 8) + r2 = Rect(r) + r2.scale_by_ip(x=2, y=4) + + # assert + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left - 3, r2.left) + self.assertEqual(r.centery - r.h * 4 / 2, r2.top) + self.assertEqual(r.right + 3, r2.right) + self.assertEqual(r.centery + r.h * 4 / 2, r2.bottom) + self.assertEqual(r.width * 2, r2.width) + self.assertEqual(r.height * 4, r2.height) + + def test_scale_by_ip__kwarg_exceptions(self): + """The scale method scales around the center of the rectangle using + keyword argument 'scale_by'. Tests for incorrect keyword args""" + r = Rect(2, 4, 6, 8) + + with self.assertRaises(TypeError): + r.scale_by_ip(scale_by=2) + + with self.assertRaises(TypeError): + r.scale_by_ip(scale_by=(1, 2), y=1) + + def test_clamp(self): + r = Rect(10, 10, 10, 10) + c = Rect(19, 12, 5, 5).clamp(r) + self.assertEqual(c.right, r.right) + self.assertEqual(c.top, 12) + c = Rect(1, 2, 3, 4).clamp(r) + self.assertEqual(c.topleft, r.topleft) + c = Rect(5, 500, 22, 33).clamp(r) + self.assertEqual(c.center, r.center) + + def test_clamp_ip(self): + r = Rect(10, 10, 10, 10) + c = Rect(19, 12, 5, 5) + c.clamp_ip(r) + self.assertEqual(c.right, r.right) + self.assertEqual(c.top, 12) + c = Rect(1, 2, 3, 4) + c.clamp_ip(r) + self.assertEqual(c.topleft, r.topleft) + c = Rect(5, 500, 22, 33) + c.clamp_ip(r) + self.assertEqual(c.center, r.center) + + def test_clip(self): + r1 = Rect(1, 2, 3, 4) + self.assertEqual(Rect(1, 2, 2, 2), r1.clip(Rect(0, 0, 3, 4))) + self.assertEqual(Rect(2, 2, 2, 4), r1.clip(Rect(2, 2, 10, 20))) + self.assertEqual(Rect(2, 3, 1, 2), r1.clip(Rect(2, 3, 1, 2))) + self.assertEqual((0, 0), r1.clip(20, 30, 5, 6).size) + self.assertEqual( + r1, r1.clip(Rect(r1)), "r1 does not clip an identical rect to itself" + ) + + def test_clipline(self): + """Ensures clipline handles four int parameters. + + Tests the clipline(x1, y1, x2, y2) format. + """ + rect = Rect((1, 2), (35, 40)) + x1 = 5 + y1 = 6 + x2 = 11 + y2 = 19 + expected_line = ((x1, y1), (x2, y2)) + + clipped_line = rect.clipline(x1, y1, x2, y2) + + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__two_sequences(self): + """Ensures clipline handles a sequence of two sequences. + + Tests the clipline((x1, y1), (x2, y2)) format. + Tests the sequences as different types. + """ + rect = Rect((1, 2), (35, 40)) + pt1 = (5, 6) + pt2 = (11, 19) + + INNER_SEQUENCES = (list, tuple, Vector2) + expected_line = (pt1, pt2) + + for inner_seq1 in INNER_SEQUENCES: + endpt1 = inner_seq1(pt1) + + for inner_seq2 in INNER_SEQUENCES: + clipped_line = rect.clipline((endpt1, inner_seq2(pt2))) + + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__two_sequences_kwarg(self): + """Ensures clipline handles a sequence of two sequences using kwargs. + + Tests the clipline((x1, y1), (x2, y2)) format. + Tests the sequences as different types. + """ + rect = Rect((1, 2), (35, 40)) + pt1 = (5, 6) + pt2 = (11, 19) + + INNER_SEQUENCES = (list, tuple, Vector2) + expected_line = (pt1, pt2) + + for inner_seq1 in INNER_SEQUENCES: + endpt1 = inner_seq1(pt1) + + for inner_seq2 in INNER_SEQUENCES: + clipped_line = rect.clipline( + first_coordinate=endpt1, second_coordinate=inner_seq2(pt2) + ) + + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__sequence_of_four_ints(self): + """Ensures clipline handles a sequence of four ints. + + Tests the clipline((x1, y1, x2, y2)) format. + Tests the sequence as different types. + """ + rect = Rect((1, 2), (35, 40)) + line = (5, 6, 11, 19) + expected_line = ((line[0], line[1]), (line[2], line[3])) + + for outer_seq in (list, tuple): + clipped_line = rect.clipline(outer_seq(line)) + + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__sequence_of_four_ints_kwargs(self): + """Ensures clipline handles a sequence of four ints using kwargs. + + Tests the clipline((x1, y1, x2, y2)) format. + Tests the sequence as different types. + """ + rect = Rect((1, 2), (35, 40)) + line = (5, 6, 11, 19) + expected_line = ((line[0], line[1]), (line[2], line[3])) + + for outer_seq in (list, tuple): + clipped_line = rect.clipline(rect_arg=outer_seq(line)) + + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__sequence_of_two_sequences(self): + """Ensures clipline handles a sequence of two sequences. + + Tests the clipline(((x1, y1), (x2, y2))) format. + Tests the sequences as different types. + """ + rect = Rect((1, 2), (35, 40)) + pt1 = (5, 6) + pt2 = (11, 19) + + INNER_SEQUENCES = (list, tuple, Vector2) + expected_line = (pt1, pt2) + + for inner_seq1 in INNER_SEQUENCES: + endpt1 = inner_seq1(pt1) + + for inner_seq2 in INNER_SEQUENCES: + endpt2 = inner_seq2(pt2) + + for outer_seq in (list, tuple): + clipped_line = rect.clipline(outer_seq((endpt1, endpt2))) + + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__sequence_of_two_sequences_kwargs(self): + """Ensures clipline handles a sequence of two sequences using kwargs. + + Tests the clipline(((x1, y1), (x2, y2))) format. + Tests the sequences as different types. + """ + rect = Rect((1, 2), (35, 40)) + pt1 = (5, 6) + pt2 = (11, 19) + + INNER_SEQUENCES = (list, tuple, Vector2) + expected_line = (pt1, pt2) + + for inner_seq1 in INNER_SEQUENCES: + endpt1 = inner_seq1(pt1) + + for inner_seq2 in INNER_SEQUENCES: + endpt2 = inner_seq2(pt2) + + for outer_seq in (list, tuple): + clipped_line = rect.clipline(x1=outer_seq((endpt1, endpt2))) + + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__floats(self): + """Ensures clipline handles float parameters.""" + rect = Rect((1, 2), (35, 40)) + x1 = 5.9 + y1 = 6.9 + x2 = 11.9 + y2 = 19.9 + + # Floats are truncated. + expected_line = ( + (math.floor(x1), math.floor(y1)), + (math.floor(x2), math.floor(y2)), + ) + + clipped_line = rect.clipline(x1, y1, x2, y2) + + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__floats_kwargs(self): + """Ensures clipline handles four float parameters. + + Tests the clipline(x1, y1, x2, y2) format. + """ + rect = Rect((1, 2), (35, 40)) + x1 = 5.9 + y1 = 6.9 + x2 = 11.9 + y2 = 19.9 + + # Floats are truncated. + expected_line = ( + (math.floor(x1), math.floor(y1)), + (math.floor(x2), math.floor(y2)), + ) + + clipped_line = rect.clipline(x1=x1, x2=y1, x3=x2, x4=y2) + + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__kwarg_exceptions(self): + """Ensure clipline handles incorrect keyword arguments""" + r = Rect(2, 4, 6, 8) + + with self.assertRaises(TypeError): + r.clipline(x1=0) + + with self.assertRaises(TypeError): + r.clipline(first_coordinate=(1, 3, 5, 4), second_coordinate=(1, 2)) + + with self.assertRaises(TypeError): + r.clipline(first_coordinate=(1, 3), second_coordinate=(2, 2), x1=1) + + with self.assertRaises(TypeError): + r.clipline(rect_arg=(1, 3, 5)) + + with self.assertRaises(TypeError): + r.clipline(rect_arg=(1, 3, 5, 4), second_coordinate=(2, 2)) + + def test_clipline__no_overlap(self): + """Ensures lines that do not overlap the rect are not clipped.""" + rect = Rect((10, 25), (15, 20)) + # Use a bigger rect to help create test lines. + big_rect = rect.inflate(2, 2) + lines = ( + (big_rect.bottomleft, big_rect.topleft), # Left edge. + (big_rect.topleft, big_rect.topright), # Top edge. + (big_rect.topright, big_rect.bottomright), # Right edge. + (big_rect.bottomright, big_rect.bottomleft), + ) # Bottom edge. + expected_line = () + + # Test lines outside rect. + for line in lines: + clipped_line = rect.clipline(line) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__both_endpoints_outside(self): + """Ensures lines that overlap the rect are clipped. + + Testing lines with both endpoints outside the rect. + """ + rect = Rect((0, 0), (20, 20)) + # Use a bigger rect to help create test lines. + big_rect = rect.inflate(2, 2) + + # Create a dict of lines and expected results. + line_dict = { + (big_rect.midleft, big_rect.midright): ( + rect.midleft, + (rect.midright[0] - 1, rect.midright[1]), + ), + (big_rect.midtop, big_rect.midbottom): ( + rect.midtop, + (rect.midbottom[0], rect.midbottom[1] - 1), + ), + # Diagonals. + (big_rect.topleft, big_rect.bottomright): ( + rect.topleft, + (rect.bottomright[0] - 1, rect.bottomright[1] - 1), + ), + # This line needs a small adjustment to make sure it intersects + # the rect correctly. + ( + (big_rect.topright[0] - 1, big_rect.topright[1]), + (big_rect.bottomleft[0], big_rect.bottomleft[1] - 1), + ): ( + (rect.topright[0] - 1, rect.topright[1]), + (rect.bottomleft[0], rect.bottomleft[1] - 1), + ), + } + + for line, expected_line in line_dict.items(): + clipped_line = rect.clipline(line) + + self.assertTupleEqual(clipped_line, expected_line) + + # Swap endpoints to test for symmetry. + expected_line = (expected_line[1], expected_line[0]) + + clipped_line = rect.clipline((line[1], line[0])) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__both_endpoints_inside(self): + """Ensures lines that overlap the rect are clipped. + + Testing lines with both endpoints inside the rect. + """ + rect = Rect((-10, -5), (20, 20)) + # Use a smaller rect to help create test lines. + small_rect = rect.inflate(-2, -2) + + lines = ( + (small_rect.midleft, small_rect.midright), + (small_rect.midtop, small_rect.midbottom), + # Diagonals. + (small_rect.topleft, small_rect.bottomright), + (small_rect.topright, small_rect.bottomleft), + ) + + for line in lines: + expected_line = line + + clipped_line = rect.clipline(line) + + self.assertTupleEqual(clipped_line, expected_line) + + # Swap endpoints to test for symmetry. + expected_line = (expected_line[1], expected_line[0]) + + clipped_line = rect.clipline((line[1], line[0])) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__endpoints_inside_and_outside(self): + """Ensures lines that overlap the rect are clipped. + + Testing lines with one endpoint outside the rect and the other is + inside the rect. + """ + rect = Rect((0, 0), (21, 21)) + # Use a bigger rect to help create test lines. + big_rect = rect.inflate(2, 2) + + # Create a dict of lines and expected results. + line_dict = { + (big_rect.midleft, rect.center): (rect.midleft, rect.center), + (big_rect.midtop, rect.center): (rect.midtop, rect.center), + (big_rect.midright, rect.center): ( + (rect.midright[0] - 1, rect.midright[1]), + rect.center, + ), + (big_rect.midbottom, rect.center): ( + (rect.midbottom[0], rect.midbottom[1] - 1), + rect.center, + ), + # Diagonals. + (big_rect.topleft, rect.center): (rect.topleft, rect.center), + (big_rect.topright, rect.center): ( + (rect.topright[0] - 1, rect.topright[1]), + rect.center, + ), + (big_rect.bottomright, rect.center): ( + (rect.bottomright[0] - 1, rect.bottomright[1] - 1), + rect.center, + ), + # This line needs a small adjustment to make sure it intersects + # the rect correctly. + ((big_rect.bottomleft[0], big_rect.bottomleft[1] - 1), rect.center): ( + (rect.bottomleft[0], rect.bottomleft[1] - 1), + rect.center, + ), + } + + for line, expected_line in line_dict.items(): + clipped_line = rect.clipline(line) + + self.assertTupleEqual(clipped_line, expected_line) + + # Swap endpoints to test for symmetry. + expected_line = (expected_line[1], expected_line[0]) + + clipped_line = rect.clipline((line[1], line[0])) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__edges(self): + """Ensures clipline properly clips line that are along the rect edges.""" + rect = Rect((10, 25), (15, 20)) + + # Create a dict of edges and expected results. + edge_dict = { + # Left edge. + (rect.bottomleft, rect.topleft): ( + (rect.bottomleft[0], rect.bottomleft[1] - 1), + rect.topleft, + ), + # Top edge. + (rect.topleft, rect.topright): ( + rect.topleft, + (rect.topright[0] - 1, rect.topright[1]), + ), + # Right edge. + (rect.topright, rect.bottomright): (), + # Bottom edge. + (rect.bottomright, rect.bottomleft): (), + } + + for edge, expected_line in edge_dict.items(): + clipped_line = rect.clipline(edge) + + self.assertTupleEqual(clipped_line, expected_line) + + # Swap endpoints to test for symmetry. + if expected_line: + expected_line = (expected_line[1], expected_line[0]) + + clipped_line = rect.clipline((edge[1], edge[0])) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__equal_endpoints_with_overlap(self): + """Ensures clipline handles lines with both endpoints the same. + + Testing lines that overlap the rect. + """ + rect = Rect((10, 25), (15, 20)) + + # Test all the points in and on a rect. + pts = ( + (x, y) + for x in range(rect.left, rect.right) + for y in range(rect.top, rect.bottom) + ) + + for pt in pts: + expected_line = (pt, pt) + + clipped_line = rect.clipline((pt, pt)) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__equal_endpoints_no_overlap(self): + """Ensures clipline handles lines with both endpoints the same. + + Testing lines that do not overlap the rect. + """ + expected_line = () + rect = Rect((10, 25), (15, 20)) + + # Test points outside rect. + for pt in test_utils.rect_perimeter_pts(rect.inflate(2, 2)): + clipped_line = rect.clipline((pt, pt)) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__zero_size_rect(self): + """Ensures clipline handles zero sized rects correctly.""" + expected_line = () + + for size in ((0, 15), (15, 0), (0, 0)): + rect = Rect((10, 25), size) + + clipped_line = rect.clipline(rect.topleft, rect.topleft) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__negative_size_rect(self): + """Ensures clipline handles negative sized rects correctly.""" + expected_line = () + + for size in ((-15, 20), (15, -20), (-15, -20)): + rect = Rect((10, 25), size) + norm_rect = rect.copy() + norm_rect.normalize() + # Use a bigger rect to help create test lines. + big_rect = norm_rect.inflate(2, 2) + + # Create a dict of lines and expected results. Some line have both + # endpoints outside the rect and some have one inside and one + # outside. + line_dict = { + (big_rect.midleft, big_rect.midright): ( + norm_rect.midleft, + (norm_rect.midright[0] - 1, norm_rect.midright[1]), + ), + (big_rect.midtop, big_rect.midbottom): ( + norm_rect.midtop, + (norm_rect.midbottom[0], norm_rect.midbottom[1] - 1), + ), + (big_rect.midleft, norm_rect.center): ( + norm_rect.midleft, + norm_rect.center, + ), + (big_rect.midtop, norm_rect.center): ( + norm_rect.midtop, + norm_rect.center, + ), + (big_rect.midright, norm_rect.center): ( + (norm_rect.midright[0] - 1, norm_rect.midright[1]), + norm_rect.center, + ), + (big_rect.midbottom, norm_rect.center): ( + (norm_rect.midbottom[0], norm_rect.midbottom[1] - 1), + norm_rect.center, + ), + } + + for line, expected_line in line_dict.items(): + clipped_line = rect.clipline(line) + + # Make sure rect wasn't normalized. + self.assertNotEqual(rect, norm_rect) + self.assertTupleEqual(clipped_line, expected_line) + + # Swap endpoints to test for symmetry. + expected_line = (expected_line[1], expected_line[0]) + + clipped_line = rect.clipline((line[1], line[0])) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__invalid_line(self): + """Ensures clipline handles invalid lines correctly.""" + rect = Rect((0, 0), (10, 20)) + invalid_lines = ( + (), + (1,), + (1, 2), + (1, 2, 3), + (1, 2, 3, 4, 5), + ((1, 2),), + ((1, 2), (3,)), + ((1, 2), 3), + ((1, 2, 5), (3, 4)), + ((1, 2), (3, 4, 5)), + ((1, 2), (3, 4), (5, 6)), + ) + + for line in invalid_lines: + with self.assertRaises(TypeError): + clipped_line = rect.clipline(line) + + with self.assertRaises(TypeError): + clipped_line = rect.clipline(*line) + + def test_move(self): + r = Rect(1, 2, 3, 4) + move_x = 10 + move_y = 20 + r2 = r.move(move_x, move_y) + expected_r2 = Rect(r.left + move_x, r.top + move_y, r.width, r.height) + self.assertEqual(expected_r2, r2) + + def test_move_ip(self): + r = Rect(1, 2, 3, 4) + r2 = Rect(r) + move_x = 10 + move_y = 20 + r2.move_ip(move_x, move_y) + expected_r2 = Rect(r.left + move_x, r.top + move_y, r.width, r.height) + self.assertEqual(expected_r2, r2) + + @unittest.skipIf( + IS_PYPY, "fails on pypy (but only for: bottom, right, centerx, centery)" + ) + def test_set_float_values(self): + zero = 0 + pos = 124 + neg = -432 + # (initial, increment, expected, other) + data_rows = [ + (zero, 0.1, zero, _random_int()), + (zero, 0.4, zero, _random_int()), + (zero, 0.5, zero + 1, _random_int()), + (zero, 1.1, zero + 1, _random_int()), + (zero, 1.5, zero + 2, _random_int()), # >0f + (zero, -0.1, zero, _random_int()), + (zero, -0.4, zero, _random_int()), + (zero, -0.5, zero - 1, _random_int()), + (zero, -0.6, zero - 1, _random_int()), + (zero, -1.6, zero - 2, _random_int()), # <0f + (zero, 1, zero + 1, _random_int()), + (zero, 4, zero + 4, _random_int()), # >0i + (zero, -1, zero - 1, _random_int()), + (zero, -4, zero - 4, _random_int()), # <0i + (pos, 0.1, pos, _random_int()), + (pos, 0.4, pos, _random_int()), + (pos, 0.5, pos + 1, _random_int()), + (pos, 1.1, pos + 1, _random_int()), + (pos, 1.5, pos + 2, _random_int()), # >0f + (pos, -0.1, pos, _random_int()), + (pos, -0.4, pos, _random_int()), + (pos, -0.5, pos, _random_int()), + (pos, -0.6, pos - 1, _random_int()), + (pos, -1.6, pos - 2, _random_int()), # <0f + (pos, 1, pos + 1, _random_int()), + (pos, 4, pos + 4, _random_int()), # >0i + (pos, -1, pos - 1, _random_int()), + (pos, -4, pos - 4, _random_int()), # <0i + (neg, 0.1, neg, _random_int()), + (neg, 0.4, neg, _random_int()), + (neg, 0.5, neg, _random_int()), + (neg, 1.1, neg + 1, _random_int()), + (neg, 1.5, neg + 1, _random_int()), # >0f + (neg, -0.1, neg, _random_int()), + (neg, -0.4, neg, _random_int()), + (neg, -0.5, neg - 1, _random_int()), + (neg, -0.6, neg - 1, _random_int()), + (neg, -1.6, neg - 2, _random_int()), # <0f + (neg, 1, neg + 1, _random_int()), + (neg, 4, neg + 4, _random_int()), # >0i + (neg, -1, neg - 1, _random_int()), + (neg, -4, neg - 4, _random_int()), # <0i + ] + + single_value_attribute_names = [ + "x", + "y", + "w", + "h", + "width", + "height", + "top", + "left", + "bottom", + "right", + "centerx", + "centery", + ] + + tuple_value_attribute_names = [ + "topleft", + "topright", + "bottomleft", + "bottomright", + "midtop", + "midleft", + "midbottom", + "midright", + "size", + "center", + ] + + for row in data_rows: + initial, inc, expected, other = row + new_value = initial + inc + for attribute_name in single_value_attribute_names: + with self.subTest(row=row, name=f"r.{attribute_name}"): + actual = Rect( + _random_int(), _random_int(), _random_int(), _random_int() + ) + # act + setattr(actual, attribute_name, new_value) + # assert + self.assertEqual(expected, getattr(actual, attribute_name)) + + for attribute_name in tuple_value_attribute_names: + with self.subTest(row=row, name=f"r.{attribute_name}[0]"): + actual = Rect( + _random_int(), _random_int(), _random_int(), _random_int() + ) + # act + setattr(actual, attribute_name, (new_value, other)) + # assert + self.assertEqual((expected, other), getattr(actual, attribute_name)) + + with self.subTest(row=row, name=f"r.{attribute_name}[1]"): + actual = Rect( + _random_int(), _random_int(), _random_int(), _random_int() + ) + # act + setattr(actual, attribute_name, (other, new_value)) + # assert + self.assertEqual((other, expected), getattr(actual, attribute_name)) + + def test_set_out_of_range_number_raises_exception(self): + i = 0 + # (initial, expected) + data_rows = [ + (_int_max + 1, TypeError), + (_int_max + 0.00001, TypeError), + (_int_max, None), + (_int_max - 1, None), + (_int_max - 2, None), + (_int_max - 10, None), + (_int_max - 63, None), + (_int_max - 64, None), + (_int_max - 65, None), + (_int_min - 1, TypeError), + (_int_min - 0.00001, TypeError), + (_int_min, None), + (_int_min + 1, None), + (_int_min + 2, None), + (_int_min + 10, None), + (_int_min + 62, None), + (_int_min + 63, None), + (_int_min + 64, None), + (0, None), + (100000, None), + (-100000, None), + ] + + single_attribute_names = [ + "x", + "y", + "w", + "h", + "width", + "height", + "top", + "left", + "bottom", + "right", + "centerx", + "centery", + ] + + tuple_value_attribute_names = [ + "topleft", + "topright", + "bottomleft", + "bottomright", + "midtop", + "midleft", + "midbottom", + "midright", + "size", + "center", + ] + + for row in data_rows: + for attribute_name in single_attribute_names: + value, expected = row + with self.subTest(row=row, name=f"r.{attribute_name}"): + actual = Rect(0, 0, 0, 0) + if expected: + # act/ assert + self.assertRaises( + TypeError, setattr, actual, attribute_name, value + ) + else: + # act + setattr(actual, attribute_name, value) + # assert + self.assertEqual(value, getattr(actual, attribute_name)) + other = _random_int() + + for attribute_name in tuple_value_attribute_names: + value, expected = row + with self.subTest(row=row, name=f"r.{attribute_name}[0]"): + actual = Rect(0, 0, 0, 0) + # act/ assert + if expected: + # act/ assert + self.assertRaises( + TypeError, setattr, actual, attribute_name, (value, other) + ) + else: + # act + setattr(actual, attribute_name, (value, other)) + # assert + self.assertEqual( + (value, other), getattr(actual, attribute_name) + ) + with self.subTest(row=row, name=f"r.{attribute_name}[1]"): + actual = Rect(0, 0, 0, 0) + # act/ assert + if expected: + # act/ assert + self.assertRaises( + TypeError, setattr, actual, attribute_name, (other, value) + ) + else: + # act + setattr(actual, attribute_name, (other, value)) + # assert + self.assertEqual( + (other, value), getattr(actual, attribute_name) + ) + + def test_update_XYWidthHeight(self): + """Test update with 4 int values(x, y, w, h)""" + rect = Rect(0, 0, 1, 1) + rect.update(1, 2, 3, 4) + + self.assertEqual(1, rect.left) + self.assertEqual(2, rect.top) + self.assertEqual(3, rect.width) + self.assertEqual(4, rect.height) + + def test_update__TopLeftSize(self): + """Test update with 2 tuples((x, y), (w, h))""" + rect = Rect(0, 0, 1, 1) + rect.update((1, 2), (3, 4)) + + self.assertEqual(1, rect.left) + self.assertEqual(2, rect.top) + self.assertEqual(3, rect.width) + self.assertEqual(4, rect.height) + + def test_update__List(self): + """Test update with list""" + rect = Rect(0, 0, 1, 1) + rect2 = [1, 2, 3, 4] + rect.update(rect2) + + self.assertEqual(1, rect.left) + self.assertEqual(2, rect.top) + self.assertEqual(3, rect.width) + self.assertEqual(4, rect.height) + + def test_update__RectObject(self): + """Test update with other rect object""" + rect = Rect(0, 0, 1, 1) + rect2 = Rect(1, 2, 3, 4) + rect.update(rect2) + + self.assertEqual(1, rect.left) + self.assertEqual(2, rect.top) + self.assertEqual(3, rect.width) + self.assertEqual(4, rect.height) + + def test_union(self): + r1 = Rect(1, 1, 1, 2) + r2 = Rect(-2, -2, 1, 2) + self.assertEqual(Rect(-2, -2, 4, 5), r1.union(r2)) + + def test_union__with_identical_Rect(self): + r1 = Rect(1, 2, 3, 4) + self.assertEqual(r1, r1.union(Rect(r1))) + + def test_union_ip(self): + r1 = Rect(1, 1, 1, 2) + r2 = Rect(-2, -2, 1, 2) + r1.union_ip(r2) + self.assertEqual(Rect(-2, -2, 4, 5), r1) + + def test_unionall(self): + r1 = Rect(0, 0, 1, 1) + r2 = Rect(-2, -2, 1, 1) + r3 = Rect(2, 2, 1, 1) + + r4 = r1.unionall([r2, r3]) + self.assertEqual(Rect(-2, -2, 5, 5), r4) + + def test_unionall__invalid_rect_format(self): + """Ensures unionall correctly handles invalid rect parameters.""" + numbers = [0, 1.2, 2, 3.3] + strs = ["a", "b", "c"] + nones = [None, None] + + for invalid_rects in (numbers, strs, nones): + with self.assertRaises(TypeError): + Rect(0, 0, 1, 1).unionall(invalid_rects) + + def test_unionall__kwargs(self): + r1 = Rect(0, 0, 1, 1) + r2 = Rect(-2, -2, 1, 1) + r3 = Rect(2, 2, 1, 1) + + r4 = r1.unionall(rect=[r2, r3]) + self.assertEqual(Rect(-2, -2, 5, 5), r4) + + def test_unionall_ip(self): + r1 = Rect(0, 0, 1, 1) + r2 = Rect(-2, -2, 1, 1) + r3 = Rect(2, 2, 1, 1) + + r1.unionall_ip([r2, r3]) + self.assertEqual(Rect(-2, -2, 5, 5), r1) + + # Bug for an empty list. Would return a Rect instead of None. + self.assertTrue(r1.unionall_ip([]) is None) + + def test_unionall_ip__kwargs(self): + r1 = Rect(0, 0, 1, 1) + r2 = Rect(-2, -2, 1, 1) + r3 = Rect(2, 2, 1, 1) + + r1.unionall_ip(rects=[r2, r3]) + self.assertEqual(Rect(-2, -2, 5, 5), r1) + + # Bug for an empty list. Would return a Rect instead of None. + self.assertTrue(r1.unionall_ip([]) is None) + + def test_colliderect(self): + r1 = Rect(1, 2, 3, 4) + self.assertTrue( + r1.colliderect(Rect(0, 0, 2, 3)), + "r1 does not collide with Rect(0, 0, 2, 3)", + ) + self.assertFalse( + r1.colliderect(Rect(0, 0, 1, 2)), "r1 collides with Rect(0, 0, 1, 2)" + ) + self.assertFalse( + r1.colliderect(Rect(r1.right, r1.bottom, 2, 2)), + "r1 collides with Rect(r1.right, r1.bottom, 2, 2)", + ) + self.assertTrue( + r1.colliderect(Rect(r1.left + 1, r1.top + 1, r1.width - 2, r1.height - 2)), + "r1 does not collide with Rect(r1.left + 1, r1.top + 1, " + + "r1.width - 2, r1.height - 2)", + ) + self.assertTrue( + r1.colliderect(Rect(r1.left - 1, r1.top - 1, r1.width + 2, r1.height + 2)), + "r1 does not collide with Rect(r1.left - 1, r1.top - 1, " + + "r1.width + 2, r1.height + 2)", + ) + self.assertTrue( + r1.colliderect(Rect(r1)), "r1 does not collide with an identical rect" + ) + self.assertFalse( + r1.colliderect(Rect(r1.right, r1.bottom, 0, 0)), + "r1 collides with Rect(r1.right, r1.bottom, 0, 0)", + ) + self.assertFalse( + r1.colliderect(Rect(r1.right, r1.bottom, 1, 1)), + "r1 collides with Rect(r1.right, r1.bottom, 1, 1)", + ) + + def testEquals(self): + """check to see how the rect uses __eq__""" + r1 = Rect(1, 2, 3, 4) + r2 = Rect(10, 20, 30, 40) + r3 = (10, 20, 30, 40) + r4 = Rect(10, 20, 30, 40) + + class foo(Rect): + def __eq__(self, other): + return id(self) == id(other) + + def __ne__(self, other): + return id(self) != id(other) + + class foo2(Rect): + pass + + r5 = foo(10, 20, 30, 40) + r6 = foo2(10, 20, 30, 40) + + self.assertNotEqual(r5, r2) + + # because we define equality differently for this subclass. + self.assertEqual(r6, r2) + + rect_list = [r1, r2, r3, r4, r6] + + # see if we can remove 4 of these. + rect_list.remove(r2) + rect_list.remove(r2) + rect_list.remove(r2) + rect_list.remove(r2) + self.assertRaises(ValueError, rect_list.remove, r2) + + def test_collidedict(self): + """Ensures collidedict detects collisions.""" + rect = Rect(1, 1, 10, 10) + + collide_item1 = ("collide 1", rect.copy()) + collide_item2 = ("collide 2", Rect(5, 5, 10, 10)) + no_collide_item1 = ("no collide 1", Rect(60, 60, 10, 10)) + no_collide_item2 = ("no collide 2", Rect(70, 70, 10, 10)) + + # Dict to check collisions with values. + rect_values = dict( + (collide_item1, collide_item2, no_collide_item1, no_collide_item2) + ) + value_collide_items = (collide_item1, collide_item2) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + key_collide_items = tuple((tuple(v), k) for k, v in value_collide_items) + + for use_values in (True, False): + if use_values: + expected_items = value_collide_items + d = rect_values + else: + expected_items = key_collide_items + d = rect_keys + + collide_item = rect.collidedict(d, use_values) + + # The detected collision could be any of the possible items. + self.assertIn(collide_item, expected_items) + + def test_collidedict__no_collision(self): + """Ensures collidedict returns None when no collisions.""" + rect = Rect(1, 1, 10, 10) + + no_collide_item1 = ("no collide 1", Rect(50, 50, 10, 10)) + no_collide_item2 = ("no collide 2", Rect(60, 60, 10, 10)) + no_collide_item3 = ("no collide 3", Rect(70, 70, 10, 10)) + + # Dict to check collisions with values. + rect_values = dict((no_collide_item1, no_collide_item2, no_collide_item3)) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + + for use_values in (True, False): + d = rect_values if use_values else rect_keys + + collide_item = rect.collidedict(d, use_values) + + self.assertIsNone(collide_item) + + def test_collidedict__barely_touching(self): + """Ensures collidedict works correctly for rects that barely touch.""" + rect = Rect(1, 1, 10, 10) + # Small rect to test barely touching collisions. + collide_rect = Rect(0, 0, 1, 1) + + collide_item1 = ("collide 1", collide_rect) + no_collide_item1 = ("no collide 1", Rect(50, 50, 10, 10)) + no_collide_item2 = ("no collide 2", Rect(60, 60, 10, 10)) + no_collide_item3 = ("no collide 3", Rect(70, 70, 10, 10)) + + # Dict to check collisions with values. + no_collide_rect_values = dict( + (no_collide_item1, no_collide_item2, no_collide_item3) + ) + + # Dict to check collisions with keys. + no_collide_rect_keys = {tuple(v): k for k, v in no_collide_rect_values.items()} + + # Tests the collide_rect on each of the rect's corners. + for attr in ("topleft", "topright", "bottomright", "bottomleft"): + setattr(collide_rect, attr, getattr(rect, attr)) + + for use_values in (True, False): + if use_values: + expected_item = collide_item1 + d = dict(no_collide_rect_values) + else: + expected_item = (tuple(collide_item1[1]), collide_item1[0]) + d = dict(no_collide_rect_keys) + + d.update((expected_item,)) # Add in the expected item. + + collide_item = rect.collidedict(d, use_values) + + self.assertTupleEqual(collide_item, expected_item) + + def test_collidedict__zero_sized_rects(self): + """Ensures collidedict works correctly with zero sized rects. + + There should be no collisions with zero sized rects. + """ + zero_rect1 = Rect(1, 1, 0, 0) + zero_rect2 = Rect(1, 1, 1, 0) + zero_rect3 = Rect(1, 1, 0, 1) + zero_rect4 = Rect(1, 1, -1, 0) + zero_rect5 = Rect(1, 1, 0, -1) + + no_collide_item1 = ("no collide 1", zero_rect1.copy()) + no_collide_item2 = ("no collide 2", zero_rect2.copy()) + no_collide_item3 = ("no collide 3", zero_rect3.copy()) + no_collide_item4 = ("no collide 4", zero_rect4.copy()) + no_collide_item5 = ("no collide 5", zero_rect5.copy()) + no_collide_item6 = ("no collide 6", Rect(0, 0, 10, 10)) + no_collide_item7 = ("no collide 7", Rect(0, 0, 2, 2)) + + # Dict to check collisions with values. + rect_values = dict( + ( + no_collide_item1, + no_collide_item2, + no_collide_item3, + no_collide_item4, + no_collide_item5, + no_collide_item6, + no_collide_item7, + ) + ) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + + for use_values in (True, False): + d = rect_values if use_values else rect_keys + + for zero_rect in ( + zero_rect1, + zero_rect2, + zero_rect3, + zero_rect4, + zero_rect5, + ): + collide_item = zero_rect.collidedict(d, use_values) + + self.assertIsNone(collide_item) + + def test_collidedict__zero_sized_rects_as_args(self): + """Ensures collidedict works correctly with zero sized rects as args. + + There should be no collisions with zero sized rects. + """ + rect = Rect(0, 0, 10, 10) + + no_collide_item1 = ("no collide 1", Rect(1, 1, 0, 0)) + no_collide_item2 = ("no collide 2", Rect(1, 1, 1, 0)) + no_collide_item3 = ("no collide 3", Rect(1, 1, 0, 1)) + + # Dict to check collisions with values. + rect_values = dict((no_collide_item1, no_collide_item2, no_collide_item3)) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + + for use_values in (True, False): + d = rect_values if use_values else rect_keys + + collide_item = rect.collidedict(d, use_values) + + self.assertIsNone(collide_item) + + def test_collidedict__negative_sized_rects(self): + """Ensures collidedict works correctly with negative sized rects.""" + neg_rect = Rect(1, 1, -1, -1) + + collide_item1 = ("collide 1", neg_rect.copy()) + collide_item2 = ("collide 2", Rect(0, 0, 10, 10)) + no_collide_item1 = ("no collide 1", Rect(1, 1, 10, 10)) + + # Dict to check collisions with values. + rect_values = dict((collide_item1, collide_item2, no_collide_item1)) + value_collide_items = (collide_item1, collide_item2) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + key_collide_items = tuple((tuple(v), k) for k, v in value_collide_items) + + for use_values in (True, False): + if use_values: + collide_items = value_collide_items + d = rect_values + else: + collide_items = key_collide_items + d = rect_keys + + collide_item = neg_rect.collidedict(d, use_values) + + # The detected collision could be any of the possible items. + self.assertIn(collide_item, collide_items) + + def test_collidedict__negative_sized_rects_as_args(self): + """Ensures collidedict works correctly with negative sized rect args.""" + rect = Rect(0, 0, 10, 10) + + collide_item1 = ("collide 1", Rect(1, 1, -1, -1)) + no_collide_item1 = ("no collide 1", Rect(1, 1, -1, 0)) + no_collide_item2 = ("no collide 2", Rect(1, 1, 0, -1)) + + # Dict to check collisions with values. + rect_values = dict((collide_item1, no_collide_item1, no_collide_item2)) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + + for use_values in (True, False): + if use_values: + expected_item = collide_item1 + d = rect_values + else: + expected_item = (tuple(collide_item1[1]), collide_item1[0]) + d = rect_keys + + collide_item = rect.collidedict(d, use_values) + + self.assertTupleEqual(collide_item, expected_item) + + def test_collidedict__invalid_dict_format(self): + """Ensures collidedict correctly handles invalid dict parameters.""" + rect = Rect(0, 0, 10, 10) + + invalid_value_dict = ("collide", rect.copy()) + invalid_key_dict = tuple(invalid_value_dict[1]), invalid_value_dict[0] + + for use_values in (True, False): + d = invalid_value_dict if use_values else invalid_key_dict + + with self.assertRaises(TypeError): + collide_item = rect.collidedict(d, use_values) + + def test_collidedict__invalid_dict_value_format(self): + """Ensures collidedict correctly handles dicts with invalid values.""" + rect = Rect(0, 0, 10, 10) + rect_keys = {tuple(rect): "collide"} + + with self.assertRaises(TypeError): + collide_item = rect.collidedict(rect_keys, 1) + + def test_collidedict__invalid_dict_key_format(self): + """Ensures collidedict correctly handles dicts with invalid keys.""" + rect = Rect(0, 0, 10, 10) + rect_values = {"collide": rect.copy()} + + with self.assertRaises(TypeError): + collide_item = rect.collidedict(rect_values) + + def test_collidedict__invalid_use_values_format(self): + """Ensures collidedict correctly handles invalid use_values parameters.""" + rect = Rect(0, 0, 1, 1) + d = {} + + for invalid_param in (None, d, 1.1): + with self.assertRaises(TypeError): + collide_item = rect.collidedict(d, invalid_param) + + def test_collidedict__kwargs(self): + """Ensures collidedict detects collisions via keyword arguments.""" + rect = Rect(1, 1, 10, 10) + + collide_item1 = ("collide 1", rect.copy()) + collide_item2 = ("collide 2", Rect(5, 5, 10, 10)) + no_collide_item1 = ("no collide 1", Rect(60, 60, 10, 10)) + no_collide_item2 = ("no collide 2", Rect(70, 70, 10, 10)) + + # Dict to check collisions with values. + rect_values = dict( + (collide_item1, collide_item2, no_collide_item1, no_collide_item2) + ) + value_collide_items = (collide_item1, collide_item2) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + key_collide_items = tuple((tuple(v), k) for k, v in value_collide_items) + + for use_values in (True, False): + if use_values: + expected_items = value_collide_items + d = rect_values + else: + expected_items = key_collide_items + d = rect_keys + + collide_item = rect.collidedict(rect_dict=d, values=use_values) + + # The detected collision could be any of the possible items. + self.assertIn(collide_item, expected_items) + + def test_collidedictall(self): + """Ensures collidedictall detects collisions.""" + rect = Rect(1, 1, 10, 10) + + collide_item1 = ("collide 1", rect.copy()) + collide_item2 = ("collide 2", Rect(5, 5, 10, 10)) + no_collide_item1 = ("no collide 1", Rect(60, 60, 20, 20)) + no_collide_item2 = ("no collide 2", Rect(70, 70, 20, 20)) + + # Dict to check collisions with values. + rect_values = dict( + (collide_item1, collide_item2, no_collide_item1, no_collide_item2) + ) + value_collide_items = [collide_item1, collide_item2] + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + key_collide_items = [(tuple(v), k) for k, v in value_collide_items] + + for use_values in (True, False): + if use_values: + expected_items = value_collide_items + d = rect_values + else: + expected_items = key_collide_items + d = rect_keys + + collide_items = rect.collidedictall(d, use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidedictall__no_collision(self): + """Ensures collidedictall returns an empty list when no collisions.""" + rect = Rect(1, 1, 10, 10) + + no_collide_item1 = ("no collide 1", Rect(50, 50, 20, 20)) + no_collide_item2 = ("no collide 2", Rect(60, 60, 20, 20)) + no_collide_item3 = ("no collide 3", Rect(70, 70, 20, 20)) + + # Dict to check collisions with values. + rect_values = dict((no_collide_item1, no_collide_item2, no_collide_item3)) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + + expected_items = [] + + for use_values in (True, False): + d = rect_values if use_values else rect_keys + + collide_items = rect.collidedictall(d, use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidedictall__barely_touching(self): + """Ensures collidedictall works correctly for rects that barely touch.""" + rect = Rect(1, 1, 10, 10) + # Small rect to test barely touching collisions. + collide_rect = Rect(0, 0, 1, 1) + + collide_item1 = ("collide 1", collide_rect) + no_collide_item1 = ("no collide 1", Rect(50, 50, 20, 20)) + no_collide_item2 = ("no collide 2", Rect(60, 60, 20, 20)) + no_collide_item3 = ("no collide 3", Rect(70, 70, 20, 20)) + + # Dict to check collisions with values. + no_collide_rect_values = dict( + (no_collide_item1, no_collide_item2, no_collide_item3) + ) + + # Dict to check collisions with keys. + no_collide_rect_keys = {tuple(v): k for k, v in no_collide_rect_values.items()} + + # Tests the collide_rect on each of the rect's corners. + for attr in ("topleft", "topright", "bottomright", "bottomleft"): + setattr(collide_rect, attr, getattr(rect, attr)) + + for use_values in (True, False): + if use_values: + expected_items = [collide_item1] + d = dict(no_collide_rect_values) + else: + expected_items = [(tuple(collide_item1[1]), collide_item1[0])] + d = dict(no_collide_rect_keys) + + d.update(expected_items) # Add in the expected items. + + collide_items = rect.collidedictall(d, use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidedictall__zero_sized_rects(self): + """Ensures collidedictall works correctly with zero sized rects. + + There should be no collisions with zero sized rects. + """ + zero_rect1 = Rect(2, 2, 0, 0) + zero_rect2 = Rect(2, 2, 2, 0) + zero_rect3 = Rect(2, 2, 0, 2) + zero_rect4 = Rect(2, 2, -2, 0) + zero_rect5 = Rect(2, 2, 0, -2) + + no_collide_item1 = ("no collide 1", zero_rect1.copy()) + no_collide_item2 = ("no collide 2", zero_rect2.copy()) + no_collide_item3 = ("no collide 3", zero_rect3.copy()) + no_collide_item4 = ("no collide 4", zero_rect4.copy()) + no_collide_item5 = ("no collide 5", zero_rect5.copy()) + no_collide_item6 = ("no collide 6", Rect(0, 0, 10, 10)) + no_collide_item7 = ("no collide 7", Rect(0, 0, 2, 2)) + + # Dict to check collisions with values. + rect_values = dict( + ( + no_collide_item1, + no_collide_item2, + no_collide_item3, + no_collide_item4, + no_collide_item5, + no_collide_item6, + no_collide_item7, + ) + ) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + + expected_items = [] + + for use_values in (True, False): + d = rect_values if use_values else rect_keys + + for zero_rect in ( + zero_rect1, + zero_rect2, + zero_rect3, + zero_rect4, + zero_rect5, + ): + collide_items = zero_rect.collidedictall(d, use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidedictall__zero_sized_rects_as_args(self): + """Ensures collidedictall works correctly with zero sized rects + as args. + + There should be no collisions with zero sized rects. + """ + rect = Rect(0, 0, 20, 20) + + no_collide_item1 = ("no collide 1", Rect(2, 2, 0, 0)) + no_collide_item2 = ("no collide 2", Rect(2, 2, 2, 0)) + no_collide_item3 = ("no collide 3", Rect(2, 2, 0, 2)) + + # Dict to check collisions with values. + rect_values = dict((no_collide_item1, no_collide_item2, no_collide_item3)) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + + expected_items = [] + + for use_values in (True, False): + d = rect_values if use_values else rect_keys + + collide_items = rect.collidedictall(d, use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidedictall__negative_sized_rects(self): + """Ensures collidedictall works correctly with negative sized rects.""" + neg_rect = Rect(2, 2, -2, -2) + + collide_item1 = ("collide 1", neg_rect.copy()) + collide_item2 = ("collide 2", Rect(0, 0, 20, 20)) + no_collide_item1 = ("no collide 1", Rect(2, 2, 20, 20)) + + # Dict to check collisions with values. + rect_values = dict((collide_item1, collide_item2, no_collide_item1)) + value_collide_items = [collide_item1, collide_item2] + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + key_collide_items = [(tuple(v), k) for k, v in value_collide_items] + + for use_values in (True, False): + if use_values: + expected_items = value_collide_items + d = rect_values + else: + expected_items = key_collide_items + d = rect_keys + + collide_items = neg_rect.collidedictall(d, use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidedictall__negative_sized_rects_as_args(self): + """Ensures collidedictall works correctly with negative sized rect + args. + """ + rect = Rect(0, 0, 10, 10) + + collide_item1 = ("collide 1", Rect(1, 1, -1, -1)) + no_collide_item1 = ("no collide 1", Rect(1, 1, -1, 0)) + no_collide_item2 = ("no collide 2", Rect(1, 1, 0, -1)) + + # Dict to check collisions with values. + rect_values = dict((collide_item1, no_collide_item1, no_collide_item2)) + value_collide_items = [collide_item1] + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + key_collide_items = [(tuple(v), k) for k, v in value_collide_items] + + for use_values in (True, False): + if use_values: + expected_items = value_collide_items + d = rect_values + else: + expected_items = key_collide_items + d = rect_keys + + collide_items = rect.collidedictall(d, use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidedictall__invalid_dict_format(self): + """Ensures collidedictall correctly handles invalid dict parameters.""" + rect = Rect(0, 0, 10, 10) + + invalid_value_dict = ("collide", rect.copy()) + invalid_key_dict = tuple(invalid_value_dict[1]), invalid_value_dict[0] + + for use_values in (True, False): + d = invalid_value_dict if use_values else invalid_key_dict + + with self.assertRaises(TypeError): + collide_item = rect.collidedictall(d, use_values) + + def test_collidedictall__invalid_dict_value_format(self): + """Ensures collidedictall correctly handles dicts with invalid values.""" + rect = Rect(0, 0, 10, 10) + rect_keys = {tuple(rect): "collide"} + + with self.assertRaises(TypeError): + collide_items = rect.collidedictall(rect_keys, 1) + + def test_collidedictall__invalid_dict_key_format(self): + """Ensures collidedictall correctly handles dicts with invalid keys.""" + rect = Rect(0, 0, 10, 10) + rect_values = {"collide": rect.copy()} + + with self.assertRaises(TypeError): + collide_items = rect.collidedictall(rect_values) + + def test_collidedictall__invalid_use_values_format(self): + """Ensures collidedictall correctly handles invalid use_values + parameters. + """ + rect = Rect(0, 0, 1, 1) + d = {} + + for invalid_param in (None, d, 1.1): + with self.assertRaises(TypeError): + collide_items = rect.collidedictall(d, invalid_param) + + def test_collidedictall__kwargs(self): + """Ensures collidedictall detects collisions via keyword arguments.""" + rect = Rect(1, 1, 10, 10) + + collide_item1 = ("collide 1", rect.copy()) + collide_item2 = ("collide 2", Rect(5, 5, 10, 10)) + no_collide_item1 = ("no collide 1", Rect(60, 60, 20, 20)) + no_collide_item2 = ("no collide 2", Rect(70, 70, 20, 20)) + + # Dict to check collisions with values. + rect_values = dict( + (collide_item1, collide_item2, no_collide_item1, no_collide_item2) + ) + value_collide_items = [collide_item1, collide_item2] + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + key_collide_items = [(tuple(v), k) for k, v in value_collide_items] + + for use_values in (True, False): + if use_values: + expected_items = value_collide_items + d = rect_values + else: + expected_items = key_collide_items + d = rect_keys + + collide_items = rect.collidedictall(rect_dict=d, values=use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidelist(self): + # __doc__ (as of 2008-08-02) for pygame.rect.Rect.collidelist: + + # Rect.collidelist(list): return index + # test if one rectangle in a list intersects + # + # Test whether the rectangle collides with any in a sequence of + # rectangles. The index of the first collision found is returned. If + # no collisions are found an index of -1 is returned. + + r = Rect(1, 1, 10, 10) + l = [Rect(50, 50, 1, 1), Rect(5, 5, 10, 10), Rect(15, 15, 1, 1)] + + self.assertEqual(r.collidelist(l), 1) + + f = [Rect(50, 50, 1, 1), (100, 100, 4, 4)] + self.assertEqual(r.collidelist(f), -1) + + def test_collidelist__kwargs(self): + # Rect.collidelist(list): return index + # test if one rectangle in a list intersects + # + # Test whether the rectangle collides with any in a sequence of + # rectangles using keyword arguments. The index of the first collision + # found is returned. If no collisions are found an index + # of -1 is returned. + + r = Rect(1, 1, 10, 10) + l = [Rect(50, 50, 1, 1), Rect(5, 5, 10, 10), Rect(15, 15, 1, 1)] + + self.assertEqual(r.collidelist(l), 1) + + f = [Rect(50, 50, 1, 1), (100, 100, 4, 4)] + self.assertEqual(r.collidelist(rects=f), -1) + + def test_collidelistall(self): + # __doc__ (as of 2008-08-02) for pygame.rect.Rect.collidelistall: + + # Rect.collidelistall(list): return indices + # test if all rectangles in a list intersect + # + # Returns a list of all the indices that contain rectangles that + # collide with the Rect. If no intersecting rectangles are found, an + # empty list is returned. + + r = Rect(1, 1, 10, 10) + + l = [ + Rect(1, 1, 10, 10), + Rect(5, 5, 10, 10), + Rect(15, 15, 1, 1), + Rect(2, 2, 1, 1), + ] + self.assertEqual(r.collidelistall(l), [0, 1, 3]) + + f = [Rect(50, 50, 1, 1), Rect(20, 20, 5, 5)] + self.assertFalse(r.collidelistall(f)) + + def test_collidelistall_returns_empty_list(self): + r = Rect(1, 1, 10, 10) + + l = [ + Rect(112, 1, 10, 10), + Rect(50, 5, 10, 10), + Rect(15, 15, 1, 1), + Rect(-20, 2, 1, 1), + ] + self.assertEqual(r.collidelistall(l), []) + + def test_collidelistall_list_of_tuples(self): + r = Rect(1, 1, 10, 10) + + l = [ + (1, 1, 10, 10), + (5, 5, 10, 10), + (15, 15, 1, 1), + (2, 2, 1, 1), + ] + self.assertEqual(r.collidelistall(l), [0, 1, 3]) + + f = [(50, 50, 1, 1), (20, 20, 5, 5)] + self.assertFalse(r.collidelistall(f)) + + def test_collidelistall_list_of_two_tuples(self): + r = Rect(1, 1, 10, 10) + + l = [ + ((1, 1), (10, 10)), + ((5, 5), (10, 10)), + ((15, 15), (1, 1)), + ((2, 2), (1, 1)), + ] + self.assertEqual(r.collidelistall(l), [0, 1, 3]) + + f = [((50, 50), (1, 1)), ((20, 20), (5, 5))] + self.assertFalse(r.collidelistall(f)) + + def test_collidelistall_list_of_lists(self): + r = Rect(1, 1, 10, 10) + + l = [ + [1, 1, 10, 10], + [5, 5, 10, 10], + [15, 15, 1, 1], + [2, 2, 1, 1], + ] + self.assertEqual(r.collidelistall(l), [0, 1, 3]) + + f = [[50, 50, 1, 1], [20, 20, 5, 5]] + self.assertFalse(r.collidelistall(f)) + + class _ObjectWithRectAttribute: + def __init__(self, r): + self.rect = r + + class _ObjectWithCallableRectAttribute: + def __init__(self, r): + self._rect = r + + def rect(self): + return self._rect + + class _ObjectWithRectProperty: + def __init__(self, r): + self._rect = r + + @property + def rect(self): + return self._rect + + class _ObjectWithMultipleRectAttribute: + def __init__(self, r1, r2, r3): + self.rect1 = r1 + self.rect2 = r2 + self.rect3 = r3 + + def test_collidelistall_list_of_object_with_rect_attribute(self): + r = Rect(1, 1, 10, 10) + + l = [ + self._ObjectWithRectAttribute(Rect(1, 1, 10, 10)), + self._ObjectWithRectAttribute(Rect(5, 5, 10, 10)), + self._ObjectWithRectAttribute(Rect(15, 15, 1, 1)), + self._ObjectWithRectAttribute(Rect(2, 2, 1, 1)), + ] + self.assertEqual(r.collidelistall(l), [0, 1, 3]) + + f = [ + self._ObjectWithRectAttribute(Rect(50, 50, 1, 1)), + self._ObjectWithRectAttribute(Rect(20, 20, 5, 5)), + ] + self.assertFalse(r.collidelistall(f)) + + def test_collidelistall_list_of_object_with_callable_rect_attribute(self): + r = Rect(1, 1, 10, 10) + + l = [ + self._ObjectWithCallableRectAttribute(Rect(1, 1, 10, 10)), + self._ObjectWithCallableRectAttribute(Rect(5, 5, 10, 10)), + self._ObjectWithCallableRectAttribute(Rect(15, 15, 1, 1)), + self._ObjectWithCallableRectAttribute(Rect(2, 2, 1, 1)), + ] + self.assertEqual(r.collidelistall(l), [0, 1, 3]) + + f = [ + self._ObjectWithCallableRectAttribute(Rect(50, 50, 1, 1)), + self._ObjectWithCallableRectAttribute(Rect(20, 20, 5, 5)), + ] + self.assertFalse(r.collidelistall(f)) + + def test_collidelistall_list_of_object_with_callable_rect_returning_object_with_rect_attribute( + self, + ): + r = Rect(1, 1, 10, 10) + + l = [ + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(1, 1, 10, 10)) + ), + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(5, 5, 10, 10)) + ), + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(15, 15, 1, 1)) + ), + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(2, 2, 1, 1)) + ), + ] + self.assertEqual(r.collidelistall(l), [0, 1, 3]) + + f = [ + self._ObjectWithCallableRectAttribute(Rect(50, 50, 1, 1)), + self._ObjectWithCallableRectAttribute(Rect(20, 20, 5, 5)), + ] + self.assertFalse(r.collidelistall(f)) + + def test_collidelistall_list_of_object_with_rect_property(self): + r = Rect(1, 1, 10, 10) + + l = [ + self._ObjectWithRectProperty(Rect(1, 1, 10, 10)), + self._ObjectWithRectProperty(Rect(5, 5, 10, 10)), + self._ObjectWithRectProperty(Rect(15, 15, 1, 1)), + self._ObjectWithRectProperty(Rect(2, 2, 1, 1)), + ] + self.assertEqual(r.collidelistall(l), [0, 1, 3]) + + f = [ + self._ObjectWithRectProperty(Rect(50, 50, 1, 1)), + self._ObjectWithRectProperty(Rect(20, 20, 5, 5)), + ] + self.assertFalse(r.collidelistall(f)) + + def test_collidelistall__kwargs(self): + # Rect.collidelistall(list): return indices + # test if all rectangles in a list intersect using keyword arguments. + # + # Returns a list of all the indices that contain rectangles that + # collide with the Rect. If no intersecting rectangles are found, an + # empty list is returned. + + r = Rect(1, 1, 10, 10) + + l = [ + Rect(1, 1, 10, 10), + Rect(5, 5, 10, 10), + Rect(15, 15, 1, 1), + Rect(2, 2, 1, 1), + ] + self.assertEqual(r.collidelistall(l), [0, 1, 3]) + + f = [Rect(50, 50, 1, 1), Rect(20, 20, 5, 5)] + self.assertFalse(r.collidelistall(rects=f)) + + def test_collideobjects_call_variants(self): + # arrange + r = Rect(1, 1, 10, 10) + rects = [Rect(1, 2, 3, 4), Rect(10, 20, 30, 40)] + objects = [ + self._ObjectWithMultipleRectAttribute( + Rect(1, 2, 3, 4), Rect(10, 20, 30, 40), Rect(100, 200, 300, 400) + ), + self._ObjectWithMultipleRectAttribute( + Rect(1, 2, 3, 4), Rect(10, 20, 30, 40), Rect(100, 200, 300, 400) + ), + ] + + # act / verify + r.collideobjects(rects) + r.collideobjects(rects, key=None) + r.collideobjects(objects, key=lambda o: o.rect1) + self.assertRaises(TypeError, r.collideobjects, objects) + + def test_collideobjects_without_key(self): + r = Rect(1, 1, 10, 10) + types_to_test = [ + [Rect(50, 50, 1, 1), Rect(5, 5, 10, 10), Rect(4, 4, 1, 1)], + [ + self._ObjectWithRectAttribute(Rect(50, 50, 1, 1)), + self._ObjectWithRectAttribute(Rect(5, 5, 10, 10)), + self._ObjectWithRectAttribute(Rect(4, 4, 1, 1)), + ], + [ + self._ObjectWithRectProperty(Rect(50, 50, 1, 1)), + self._ObjectWithRectProperty(Rect(5, 5, 10, 10)), + self._ObjectWithRectProperty(Rect(4, 4, 1, 1)), + ], + [ + self._ObjectWithCallableRectAttribute(Rect(50, 50, 1, 1)), + self._ObjectWithCallableRectAttribute(Rect(5, 5, 10, 10)), + self._ObjectWithCallableRectAttribute(Rect(4, 4, 1, 1)), + ], + [ + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(50, 50, 1, 1)) + ), + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(5, 5, 10, 10)) + ), + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(4, 4, 1, 1)) + ), + ], + [(50, 50, 1, 1), (5, 5, 10, 10), (4, 4, 1, 1)], + [((50, 50), (1, 1)), ((5, 5), (10, 10)), ((4, 4), (1, 1))], + [[50, 50, 1, 1], [5, 5, 10, 10], [4, 4, 1, 1]], + [ + Rect(50, 50, 1, 1), + self._ObjectWithRectAttribute(Rect(5, 5, 10, 10)), + (4, 4, 1, 1), + ], # mix + ] + + for l in types_to_test: + with self.subTest(type=l[0].__class__.__name__): + # act + actual = r.collideobjects(l) + # assert + self.assertEqual(actual, l[1]) + + types_to_test = [ + [Rect(50, 50, 1, 1), Rect(100, 100, 4, 4)], + [ + self._ObjectWithRectAttribute(Rect(50, 50, 1, 1)), + self._ObjectWithRectAttribute(Rect(100, 100, 4, 4)), + ], + [ + self._ObjectWithRectProperty(Rect(50, 50, 1, 1)), + self._ObjectWithRectProperty(Rect(100, 100, 4, 4)), + ], + [ + self._ObjectWithCallableRectAttribute(Rect(50, 50, 1, 1)), + self._ObjectWithCallableRectAttribute(Rect(100, 100, 4, 4)), + ], + [ + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(50, 50, 1, 1)) + ), + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(100, 100, 4, 4)) + ), + ], + [(50, 50, 1, 1), (100, 100, 4, 4)], + [((50, 50), (1, 1)), ((100, 100), (4, 4))], + [[50, 50, 1, 1], [100, 100, 4, 4]], + [Rect(50, 50, 1, 1), [100, 100, 4, 4]], # mix + ] + + for f in types_to_test: + with self.subTest(type=f[0].__class__.__name__, expected=None): + # act + actual = r.collideobjects(f) + # assert + self.assertEqual(actual, None) + + def test_collideobjects_list_of_object_with_multiple_rect_attribute(self): + r = Rect(1, 1, 10, 10) + + things = [ + self._ObjectWithMultipleRectAttribute( + Rect(1, 1, 10, 10), Rect(5, 5, 1, 1), Rect(-73, 3, 3, 3) + ), + self._ObjectWithMultipleRectAttribute( + Rect(5, 5, 10, 10), Rect(-5, -5, 10, 10), Rect(3, 3, 3, 3) + ), + self._ObjectWithMultipleRectAttribute( + Rect(15, 15, 1, 1), Rect(100, 1, 1, 1), Rect(3, 83, 3, 3) + ), + self._ObjectWithMultipleRectAttribute( + Rect(2, 2, 1, 1), Rect(1, -81, 10, 10), Rect(3, 8, 3, 3) + ), + ] + self.assertEqual(r.collideobjects(things, key=lambda o: o.rect1), things[0]) + self.assertEqual(r.collideobjects(things, key=lambda o: o.rect2), things[0]) + self.assertEqual(r.collideobjects(things, key=lambda o: o.rect3), things[1]) + + f = [ + self._ObjectWithMultipleRectAttribute( + Rect(50, 50, 1, 1), Rect(11, 1, 1, 1), Rect(2, -32, 2, 2) + ), + self._ObjectWithMultipleRectAttribute( + Rect(20, 20, 5, 5), Rect(1, 11, 1, 1), Rect(-20, 2, 2, 2) + ), + ] + self.assertFalse(r.collideobjectsall(f, key=lambda o: o.rect1)) + self.assertFalse(r.collideobjectsall(f, key=lambda o: o.rect2)) + self.assertFalse(r.collideobjectsall(f, key=lambda o: o.rect3)) + + def test_collideobjectsall_call_variants(self): + # arrange + r = Rect(1, 1, 10, 10) + rects = [Rect(1, 2, 3, 4), Rect(10, 20, 30, 40)] + objects = [ + self._ObjectWithMultipleRectAttribute( + Rect(1, 2, 3, 4), Rect(10, 20, 30, 40), Rect(100, 200, 300, 400) + ), + self._ObjectWithMultipleRectAttribute( + Rect(1, 2, 3, 4), Rect(10, 20, 30, 40), Rect(100, 200, 300, 400) + ), + ] + + # act / verify + r.collideobjectsall(rects) + r.collideobjectsall(rects, key=None) + r.collideobjectsall(objects, key=lambda o: o.rect1) + self.assertRaises(TypeError, r.collideobjectsall, objects) + + def test_collideobjectsall(self): + r = Rect(1, 1, 10, 10) + + types_to_test = [ + [ + Rect(1, 1, 10, 10), + Rect(5, 5, 10, 10), + Rect(15, 15, 1, 1), + Rect(2, 2, 1, 1), + ], + [ + (1, 1, 10, 10), + (5, 5, 10, 10), + (15, 15, 1, 1), + (2, 2, 1, 1), + ], + [ + ((1, 1), (10, 10)), + ((5, 5), (10, 10)), + ((15, 15), (1, 1)), + ((2, 2), (1, 1)), + ], + [ + [1, 1, 10, 10], + [5, 5, 10, 10], + [15, 15, 1, 1], + [2, 2, 1, 1], + ], + [ + self._ObjectWithRectAttribute(Rect(1, 1, 10, 10)), + self._ObjectWithRectAttribute(Rect(5, 5, 10, 10)), + self._ObjectWithRectAttribute(Rect(15, 15, 1, 1)), + self._ObjectWithRectAttribute(Rect(2, 2, 1, 1)), + ], + [ + self._ObjectWithCallableRectAttribute(Rect(1, 1, 10, 10)), + self._ObjectWithCallableRectAttribute(Rect(5, 5, 10, 10)), + self._ObjectWithCallableRectAttribute(Rect(15, 15, 1, 1)), + self._ObjectWithCallableRectAttribute(Rect(2, 2, 1, 1)), + ], + [ + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(1, 1, 10, 10)) + ), + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(5, 5, 10, 10)) + ), + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(15, 15, 1, 1)) + ), + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(2, 2, 1, 1)) + ), + ], + [ + self._ObjectWithRectProperty(Rect(1, 1, 10, 10)), + self._ObjectWithRectProperty(Rect(5, 5, 10, 10)), + self._ObjectWithRectProperty(Rect(15, 15, 1, 1)), + self._ObjectWithRectProperty(Rect(2, 2, 1, 1)), + ], + ] + for things in types_to_test: + with self.subTest(type=things[0].__class__.__name__): + # act + actual = r.collideobjectsall(things, key=None) + # assert + self.assertEqual(actual, [things[0], things[1], things[3]]) + + types_to_test = [ + [Rect(50, 50, 1, 1), Rect(20, 20, 5, 5)], + [(50, 50, 1, 1), (20, 20, 5, 5)], + [((50, 50), (1, 1)), ((20, 20), (5, 5))], + [[50, 50, 1, 1], [20, 20, 5, 5]], + [ + self._ObjectWithRectAttribute(Rect(50, 50, 1, 1)), + self._ObjectWithRectAttribute(Rect(20, 20, 5, 5)), + ], + [ + self._ObjectWithCallableRectAttribute(Rect(50, 50, 1, 1)), + self._ObjectWithCallableRectAttribute(Rect(20, 20, 5, 5)), + ], + [ + self._ObjectWithCallableRectAttribute(Rect(50, 50, 1, 1)), + self._ObjectWithCallableRectAttribute(Rect(20, 20, 5, 5)), + ], + [ + self._ObjectWithRectProperty(Rect(50, 50, 1, 1)), + self._ObjectWithRectProperty(Rect(20, 20, 5, 5)), + ], + ] + for f in types_to_test: + with self.subTest(type=f[0].__class__.__name__, expected=None): + # act + actual = r.collideobjectsall(f) + # assert + self.assertFalse(actual) + + def test_collideobjectsall_list_of_object_with_multiple_rect_attribute(self): + r = Rect(1, 1, 10, 10) + + things = [ + self._ObjectWithMultipleRectAttribute( + Rect(1, 1, 10, 10), Rect(5, 5, 1, 1), Rect(-73, 3, 3, 3) + ), + self._ObjectWithMultipleRectAttribute( + Rect(5, 5, 10, 10), Rect(-5, -5, 10, 10), Rect(3, 3, 3, 3) + ), + self._ObjectWithMultipleRectAttribute( + Rect(15, 15, 1, 1), Rect(100, 1, 1, 1), Rect(3, 83, 3, 3) + ), + self._ObjectWithMultipleRectAttribute( + Rect(2, 2, 1, 1), Rect(1, -81, 10, 10), Rect(3, 8, 3, 3) + ), + ] + self.assertEqual( + r.collideobjectsall(things, key=lambda o: o.rect1), + [things[0], things[1], things[3]], + ) + self.assertEqual( + r.collideobjectsall(things, key=lambda o: o.rect2), [things[0], things[1]] + ) + self.assertEqual( + r.collideobjectsall(things, key=lambda o: o.rect3), [things[1], things[3]] + ) + + f = [ + self._ObjectWithMultipleRectAttribute( + Rect(50, 50, 1, 1), Rect(11, 1, 1, 1), Rect(2, -32, 2, 2) + ), + self._ObjectWithMultipleRectAttribute( + Rect(20, 20, 5, 5), Rect(1, 11, 1, 1), Rect(-20, 2, 2, 2) + ), + ] + self.assertFalse(r.collideobjectsall(f, key=lambda o: o.rect1)) + self.assertFalse(r.collideobjectsall(f, key=lambda o: o.rect2)) + self.assertFalse(r.collideobjectsall(f, key=lambda o: o.rect3)) + + def test_fit(self): + # __doc__ (as of 2008-08-02) for pygame.rect.Rect.fit: + + # Rect.fit(Rect): return Rect + # resize and move a rectangle with aspect ratio + # + # Returns a new rectangle that is moved and resized to fit another. + # The aspect ratio of the original Rect is preserved, so the new + # rectangle may be smaller than the target in either width or height. + + r = Rect(10, 10, 30, 30) + + r2 = Rect(30, 30, 15, 10) + + f = r.fit(r2) + self.assertTrue(r2.contains(f)) + + f2 = r2.fit(r) + self.assertTrue(r.contains(f2)) + + def test_copy(self): + r = Rect(1, 2, 10, 20) + c = r.copy() + self.assertEqual(c, r) + + def test_subscript(self): + r = Rect(1, 2, 3, 4) + self.assertEqual(r[0], 1) + self.assertEqual(r[1], 2) + self.assertEqual(r[2], 3) + self.assertEqual(r[3], 4) + self.assertEqual(r[-1], 4) + self.assertEqual(r[-2], 3) + self.assertEqual(r[-4], 1) + self.assertRaises(IndexError, r.__getitem__, 5) + self.assertRaises(IndexError, r.__getitem__, -5) + self.assertEqual(r[0:2], [1, 2]) + self.assertEqual(r[0:4], [1, 2, 3, 4]) + self.assertEqual(r[0:-1], [1, 2, 3]) + self.assertEqual(r[:], [1, 2, 3, 4]) + self.assertEqual(r[...], [1, 2, 3, 4]) + self.assertEqual(r[0:4:2], [1, 3]) + self.assertEqual(r[0:4:3], [1, 4]) + self.assertEqual(r[3::-1], [4, 3, 2, 1]) + self.assertRaises(TypeError, r.__getitem__, None) + + def test_ass_subscript(self): + r = Rect(0, 0, 0, 0) + r[...] = 1, 2, 3, 4 + self.assertEqual(r, [1, 2, 3, 4]) + self.assertRaises(TypeError, r.__setitem__, None, 0) + self.assertEqual(r, [1, 2, 3, 4]) + self.assertRaises(TypeError, r.__setitem__, 0, "") + self.assertEqual(r, [1, 2, 3, 4]) + self.assertRaises(IndexError, r.__setitem__, 4, 0) + self.assertEqual(r, [1, 2, 3, 4]) + self.assertRaises(IndexError, r.__setitem__, -5, 0) + self.assertEqual(r, [1, 2, 3, 4]) + r[0] = 10 + self.assertEqual(r, [10, 2, 3, 4]) + r[3] = 40 + self.assertEqual(r, [10, 2, 3, 40]) + r[-1] = 400 + self.assertEqual(r, [10, 2, 3, 400]) + r[-4] = 100 + self.assertEqual(r, [100, 2, 3, 400]) + r[1:3] = 0 + self.assertEqual(r, [100, 0, 0, 400]) + r[...] = 0 + self.assertEqual(r, [0, 0, 0, 0]) + r[:] = 9 + self.assertEqual(r, [9, 9, 9, 9]) + r[:] = 11, 12, 13, 14 + self.assertEqual(r, [11, 12, 13, 14]) + r[::-1] = r + self.assertEqual(r, [14, 13, 12, 11]) + + def test_ass_subscript_deletion(self): + r = Rect(0, 0, 0, 0) + with self.assertRaises(TypeError): + del r[0] + + with self.assertRaises(TypeError): + del r[0:2] + + with self.assertRaises(TypeError): + del r[...] + + def test_collection_abc(self): + r = Rect(64, 70, 75, 30) + self.assertTrue(isinstance(r, Collection)) + self.assertFalse(isinstance(r, Sequence)) + + +class SubclassTest(unittest.TestCase): + class MyRect(Rect): + def __init__(self, *args, **kwds): + super(SubclassTest.MyRect, self).__init__(*args, **kwds) + self.an_attribute = True + + def test_copy(self): + mr1 = self.MyRect(1, 2, 10, 20) + self.assertTrue(mr1.an_attribute) + mr2 = mr1.copy() + self.assertTrue(isinstance(mr2, self.MyRect)) + self.assertRaises(AttributeError, getattr, mr2, "an_attribute") + + def test_move(self): + mr1 = self.MyRect(1, 2, 10, 20) + self.assertTrue(mr1.an_attribute) + mr2 = mr1.move(1, 2) + self.assertTrue(isinstance(mr2, self.MyRect)) + self.assertRaises(AttributeError, getattr, mr2, "an_attribute") + + def test_inflate(self): + mr1 = self.MyRect(1, 2, 10, 20) + self.assertTrue(mr1.an_attribute) + mr2 = mr1.inflate(2, 4) + self.assertTrue(isinstance(mr2, self.MyRect)) + self.assertRaises(AttributeError, getattr, mr2, "an_attribute") + + def test_clamp(self): + mr1 = self.MyRect(19, 12, 5, 5) + self.assertTrue(mr1.an_attribute) + mr2 = mr1.clamp(Rect(10, 10, 10, 10)) + self.assertTrue(isinstance(mr2, self.MyRect)) + self.assertRaises(AttributeError, getattr, mr2, "an_attribute") + + def test_clip(self): + mr1 = self.MyRect(1, 2, 3, 4) + self.assertTrue(mr1.an_attribute) + mr2 = mr1.clip(Rect(0, 0, 3, 4)) + self.assertTrue(isinstance(mr2, self.MyRect)) + self.assertRaises(AttributeError, getattr, mr2, "an_attribute") + + def test_union(self): + mr1 = self.MyRect(1, 1, 1, 2) + self.assertTrue(mr1.an_attribute) + mr2 = mr1.union(Rect(-2, -2, 1, 2)) + self.assertTrue(isinstance(mr2, self.MyRect)) + self.assertRaises(AttributeError, getattr, mr2, "an_attribute") + + def test_unionall(self): + mr1 = self.MyRect(0, 0, 1, 1) + self.assertTrue(mr1.an_attribute) + mr2 = mr1.unionall([Rect(-2, -2, 1, 1), Rect(2, 2, 1, 1)]) + self.assertTrue(isinstance(mr2, self.MyRect)) + self.assertRaises(AttributeError, getattr, mr2, "an_attribute") + + def test_fit(self): + mr1 = self.MyRect(10, 10, 30, 30) + self.assertTrue(mr1.an_attribute) + mr2 = mr1.fit(Rect(30, 30, 15, 10)) + self.assertTrue(isinstance(mr2, self.MyRect)) + self.assertRaises(AttributeError, getattr, mr2, "an_attribute") + + def test_collection_abc(self): + mr1 = self.MyRect(64, 70, 75, 30) + self.assertTrue(isinstance(mr1, Collection)) + self.assertFalse(isinstance(mr1, Sequence)) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/__init__.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/__init__.py new file mode 100644 index 00000000..1bb8bf6d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 00000000..6ba9ab58 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/__pycache__/run_tests__test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/__pycache__/run_tests__test.cpython-311.pyc new file mode 100644 index 00000000..68af4345 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/__pycache__/run_tests__test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__init__.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__init__.py new file mode 100644 index 00000000..1bb8bf6d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 00000000..a3e5f26e Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/fake_2_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/fake_2_test.cpython-311.pyc new file mode 100644 index 00000000..3706dab1 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/fake_2_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/fake_3_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/fake_3_test.cpython-311.pyc new file mode 100644 index 00000000..d66e756f Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/fake_3_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/fake_4_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/fake_4_test.cpython-311.pyc new file mode 100644 index 00000000..2ff7eff9 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/fake_4_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/fake_5_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/fake_5_test.cpython-311.pyc new file mode 100644 index 00000000..781b1f48 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/fake_5_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/fake_6_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/fake_6_test.cpython-311.pyc new file mode 100644 index 00000000..cad4406f Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/fake_6_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/no_assertions__ret_code_of_1__test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/no_assertions__ret_code_of_1__test.cpython-311.pyc new file mode 100644 index 00000000..7bd9ccaa Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/no_assertions__ret_code_of_1__test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/zero_tests_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/zero_tests_test.cpython-311.pyc new file mode 100644 index 00000000..742d5189 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/__pycache__/zero_tests_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_2_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_2_test.py new file mode 100644 index 00000000..3be92e1a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_2_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_3_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_3_test.py new file mode 100644 index 00000000..3be92e1a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_3_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_4_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_4_test.py new file mode 100644 index 00000000..3be92e1a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_4_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_5_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_5_test.py new file mode 100644 index 00000000..3be92e1a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_5_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_6_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_6_test.py new file mode 100644 index 00000000..3be92e1a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_6_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/no_assertions__ret_code_of_1__test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/no_assertions__ret_code_of_1__test.py new file mode 100644 index 00000000..0ba0e94d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/no_assertions__ret_code_of_1__test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + pass + + def test_get_mods(self): + pass + + def test_get_pressed(self): + pass + + def test_name(self): + pass + + def test_set_mods(self): + pass + + def test_set_repeat(self): + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/zero_tests_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/zero_tests_test.py new file mode 100644 index 00000000..649055a6 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/zero_tests_test.py @@ -0,0 +1,23 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/__init__.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/__init__.py new file mode 100644 index 00000000..1bb8bf6d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 00000000..81684653 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/__pycache__/fake_2_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/__pycache__/fake_2_test.cpython-311.pyc new file mode 100644 index 00000000..8420db7a Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/__pycache__/fake_2_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/__pycache__/incomplete_todo_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/__pycache__/incomplete_todo_test.cpython-311.pyc new file mode 100644 index 00000000..ba021a84 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/__pycache__/incomplete_todo_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/__pycache__/magic_tag_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/__pycache__/magic_tag_test.cpython-311.pyc new file mode 100644 index 00000000..c7bb2f5d Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/__pycache__/magic_tag_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/__pycache__/sleep_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/__pycache__/sleep_test.cpython-311.pyc new file mode 100644 index 00000000..fd544794 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/__pycache__/sleep_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/fake_2_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/fake_2_test.py new file mode 100644 index 00000000..3be92e1a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/fake_2_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/incomplete_todo_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/incomplete_todo_test.py new file mode 100644 index 00000000..bdd8a3b7 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/incomplete_todo_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def todo_test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def todo_test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/magic_tag_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/magic_tag_test.py new file mode 100644 index 00000000..126bc2b8 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/magic_tag_test.py @@ -0,0 +1,38 @@ +__tags__ = ["magic"] + +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/sleep_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/sleep_test.py new file mode 100644 index 00000000..468c75fb --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/sleep_test.py @@ -0,0 +1,29 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + +import time + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + stop_time = time.time() + 10.0 + while time.time() < stop_time: + time.sleep(1) + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/__init__.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/__init__.py new file mode 100644 index 00000000..1bb8bf6d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 00000000..9e358570 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/__pycache__/fake_2_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/__pycache__/fake_2_test.cpython-311.pyc new file mode 100644 index 00000000..f189fd15 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/__pycache__/fake_2_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/__pycache__/invisible_tag_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/__pycache__/invisible_tag_test.cpython-311.pyc new file mode 100644 index 00000000..c5c7c36f Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/__pycache__/invisible_tag_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/__pycache__/magic_tag_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/__pycache__/magic_tag_test.cpython-311.pyc new file mode 100644 index 00000000..1f098f03 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/__pycache__/magic_tag_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/fake_2_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/fake_2_test.py new file mode 100644 index 00000000..3be92e1a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/fake_2_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/invisible_tag_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/invisible_tag_test.py new file mode 100644 index 00000000..3ef959a0 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/invisible_tag_test.py @@ -0,0 +1,41 @@ +__tags__ = ["invisible"] + +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/magic_tag_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/magic_tag_test.py new file mode 100644 index 00000000..126bc2b8 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/magic_tag_test.py @@ -0,0 +1,38 @@ +__tags__ = ["magic"] + +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/__init__.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/__init__.py new file mode 100644 index 00000000..1bb8bf6d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 00000000..6822dfa7 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/__pycache__/fake_2_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/__pycache__/fake_2_test.cpython-311.pyc new file mode 100644 index 00000000..36928ae6 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/__pycache__/fake_2_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/__pycache__/fake_3_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/__pycache__/fake_3_test.cpython-311.pyc new file mode 100644 index 00000000..0a32e14d Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/__pycache__/fake_3_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/__pycache__/fake_4_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/__pycache__/fake_4_test.cpython-311.pyc new file mode 100644 index 00000000..8d18ac7a Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/__pycache__/fake_4_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/fake_2_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/fake_2_test.py new file mode 100644 index 00000000..3be92e1a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/fake_2_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/fake_3_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/fake_3_test.py new file mode 100644 index 00000000..3be92e1a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/fake_3_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/fake_4_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/fake_4_test.py new file mode 100644 index 00000000..1e75feaf --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/fake_4_test.py @@ -0,0 +1,41 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(False, "Some Jibberish") + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + if 1: + if 1: + assert False + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/__init__.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/__init__.py new file mode 100644 index 00000000..1bb8bf6d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 00000000..9629f2f3 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/__pycache__/fake_2_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/__pycache__/fake_2_test.cpython-311.pyc new file mode 100644 index 00000000..4e1a5506 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/__pycache__/fake_2_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/__pycache__/fake_3_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/__pycache__/fake_3_test.cpython-311.pyc new file mode 100644 index 00000000..69990268 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/__pycache__/fake_3_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/fake_2_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/fake_2_test.py new file mode 100644 index 00000000..b88f1aeb --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/fake_2_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def todo_test_get_pressed(self): + self.fail() + + def test_name(self): + self.assertTrue(True) + + def todo_test_set_mods(self): + self.fail() + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/fake_3_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/fake_3_test.py new file mode 100644 index 00000000..3be92e1a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/fake_3_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/__init__.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/__init__.py new file mode 100644 index 00000000..1bb8bf6d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 00000000..5bfd8673 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/__pycache__/fake_2_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/__pycache__/fake_2_test.cpython-311.pyc new file mode 100644 index 00000000..9a1d974d Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/__pycache__/fake_2_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/__pycache__/fake_3_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/__pycache__/fake_3_test.cpython-311.pyc new file mode 100644 index 00000000..d7085855 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/__pycache__/fake_3_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/fake_2_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/fake_2_test.py new file mode 100644 index 00000000..bdd8a3b7 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/fake_2_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def todo_test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def todo_test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/fake_3_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/fake_3_test.py new file mode 100644 index 00000000..3be92e1a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/fake_3_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/__init__.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/__init__.py new file mode 100644 index 00000000..1bb8bf6d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 00000000..30d17fa5 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/__pycache__/fake_1_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/__pycache__/fake_1_test.cpython-311.pyc new file mode 100644 index 00000000..baf78827 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/__pycache__/fake_1_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/__pycache__/fake_2_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/__pycache__/fake_2_test.cpython-311.pyc new file mode 100644 index 00000000..d51053f9 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/__pycache__/fake_2_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/fake_1_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/fake_1_test.py new file mode 100644 index 00000000..3e9e9367 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/fake_1_test.py @@ -0,0 +1,40 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + while True: + pass + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/fake_2_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/fake_2_test.py new file mode 100644 index 00000000..3be92e1a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/fake_2_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/__init__.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/__init__.py new file mode 100644 index 00000000..1bb8bf6d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 00000000..e3ae8b76 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/__pycache__/fake_2_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/__pycache__/fake_2_test.cpython-311.pyc new file mode 100644 index 00000000..cdfebbbc Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/__pycache__/fake_2_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/__pycache__/fake_3_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/__pycache__/fake_3_test.cpython-311.pyc new file mode 100644 index 00000000..6c28af17 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/__pycache__/fake_3_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/__pycache__/fake_4_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/__pycache__/fake_4_test.cpython-311.pyc new file mode 100644 index 00000000..495fb8e9 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/__pycache__/fake_4_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_2_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_2_test.py new file mode 100644 index 00000000..3be92e1a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_2_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_3_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_3_test.py new file mode 100644 index 00000000..f59ad40a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_3_test.py @@ -0,0 +1,41 @@ +import sys + +if __name__ == "__main__": + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + sys.stderr.write("jibberish messes things up\n") + self.assertTrue(False) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_4_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_4_test.py new file mode 100644 index 00000000..1e75feaf --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_4_test.py @@ -0,0 +1,41 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(False, "Some Jibberish") + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + if 1: + if 1: + assert False + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/__init__.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/__init__.py new file mode 100644 index 00000000..1bb8bf6d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 00000000..7092a25a Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/__pycache__/fake_2_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/__pycache__/fake_2_test.cpython-311.pyc new file mode 100644 index 00000000..46d1a319 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/__pycache__/fake_2_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/__pycache__/fake_3_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/__pycache__/fake_3_test.cpython-311.pyc new file mode 100644 index 00000000..16b81227 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/__pycache__/fake_3_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/__pycache__/fake_4_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/__pycache__/fake_4_test.cpython-311.pyc new file mode 100644 index 00000000..0fd47e10 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/__pycache__/fake_4_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_2_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_2_test.py new file mode 100644 index 00000000..3be92e1a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_2_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_3_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_3_test.py new file mode 100644 index 00000000..467c7254 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_3_test.py @@ -0,0 +1,42 @@ +import sys + +if __name__ == "__main__": + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + sys.stdout.write("jibberish ruins everything\n") + self.assertTrue(False) + + def test_name(self): + sys.stdout.write("forgot to remove debug crap\n") + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_4_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_4_test.py new file mode 100644 index 00000000..1e75feaf --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_4_test.py @@ -0,0 +1,41 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(False, "Some Jibberish") + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + if 1: + if 1: + assert False + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/run_tests__test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/run_tests__test.py new file mode 100644 index 00000000..a1eadd10 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/run_tests__test.py @@ -0,0 +1,145 @@ +################################################################################ + +import subprocess, os, sys, re, difflib + +################################################################################ + +IGNORE = (".svn", "infinite_loop") +NORMALIZERS = ( + (r"Ran (\d+) tests in (\d+\.\d+)s", "Ran \\1 tests in X.XXXs"), + (r'File ".*?([^/\\.]+\.py)"', 'File "\\1"'), +) + +################################################################################ + + +def norm_result(result): + "normalize differences, such as timing between output" + for normalizer, replacement in NORMALIZERS: + if hasattr(normalizer, "__call__"): + result = normalizer(result) + else: + result = re.sub(normalizer, replacement, result) + + return result + + +def call_proc(cmd, cd=None): + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + cwd=cd, + universal_newlines=True, + ) + if proc.wait(): + print(f"{cmd} {proc.wait()}") + raise Exception(proc.stdout.read()) + + return proc.stdout.read() + + +################################################################################ + +unnormed_diff = "-u" in sys.argv +verbose = "-v" in sys.argv or unnormed_diff +if "-h" in sys.argv or "--help" in sys.argv: + sys.exit( + "\nCOMPARES OUTPUT OF SINGLE VS SUBPROCESS MODE OF RUN_TESTS.PY\n\n" + "-v, to output diffs even on success\n" + "-u, to output diffs of unnormalized tests\n\n" + "Each line of a Differ delta begins with a two-letter code:\n\n" + " '- ' line unique to sequence 1\n" + " '+ ' line unique to sequence 2\n" + " ' ' line common to both sequences\n" + " '? ' line not present in either input sequence\n" + ) + +main_dir = os.path.split(os.path.abspath(sys.argv[0]))[0] +trunk_dir = os.path.normpath(os.path.join(main_dir, "../../")) + +test_suite_dirs = [ + x + for x in os.listdir(main_dir) + if os.path.isdir(os.path.join(main_dir, x)) and x not in IGNORE +] + + +################################################################################ + + +def assert_on_results(suite, single, sub): + test = globals().get(f"{suite}_test") + if hasattr(test, "__call_"): + test(suite, single, sub) + print(f"assertions on {suite} OK") + + +# Don't modify tests in suites below. These assertions are in place to make sure +# that tests are actually being ran + + +def all_ok_test(suite, *args): + for results in args: + assert "Ran 36 tests" in results # some tests are running + assert "OK" in results # OK + + +def failures1_test(suite, *args): + for results in args: + assert "FAILED (failures=2)" in results + assert "Ran 18 tests" in results + + +################################################################################ +# Test that output is the same in single process and subprocess modes +# + +base_cmd = [sys.executable, "run_tests.py", "-i"] + +cmd = base_cmd + ["-n", "-f"] +sub_cmd = base_cmd + ["-f"] +time_out_cmd = base_cmd + ["-t", "4", "-f", "infinite_loop"] + +passes = 0 +failed = False + +for suite in test_suite_dirs: + single = call_proc(cmd + [suite], trunk_dir) + subs = call_proc(sub_cmd + [suite], trunk_dir) + + normed_single, normed_subs = map(norm_result, (single, subs)) + + failed = normed_single != normed_subs + if failed: + print(f"{suite} suite comparison FAILED\n") + else: + passes += 1 + print(f"{suite} suite comparison OK") + + assert_on_results(suite, single, subs) + + if verbose or failed: + print("difflib.Differ().compare(single, suprocessed):\n") + print( + "".join( + list( + difflib.Differ().compare( + (unnormed_diff and single or normed_single).splitlines(1), + (unnormed_diff and subs or normed_subs).splitlines(1), + ) + ) + ) + ) + +sys.stdout.write("infinite_loop suite (subprocess mode timeout) ") +loop_test = call_proc(time_out_cmd, trunk_dir) +assert "successfully terminated" in loop_test +passes += 1 +print("OK") + +print(f"\n{passes}/{len(test_suite_dirs) + 1} suites pass") + +print("\n-h for help") + +################################################################################ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/__init__.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/__init__.py new file mode 100644 index 00000000..1bb8bf6d --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/__init__.py @@ -0,0 +1 @@ +# empty diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 00000000..9b5bdf5b Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/__pycache__/fake_2_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/__pycache__/fake_2_test.cpython-311.pyc new file mode 100644 index 00000000..52ead74c Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/__pycache__/fake_2_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/__pycache__/sleep_test.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/__pycache__/sleep_test.cpython-311.pyc new file mode 100644 index 00000000..4d0391ae Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/__pycache__/sleep_test.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/fake_2_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/fake_2_test.py new file mode 100644 index 00000000..3be92e1a --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/fake_2_test.py @@ -0,0 +1,39 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + self.assertTrue(True) + + def test_get_mods(self): + self.assertTrue(True) + + def test_get_pressed(self): + self.assertTrue(True) + + def test_name(self): + self.assertTrue(True) + + def test_set_mods(self): + self.assertTrue(True) + + def test_set_repeat(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/sleep_test.py b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/sleep_test.py new file mode 100644 index 00000000..bab528a9 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/sleep_test.py @@ -0,0 +1,30 @@ +if __name__ == "__main__": + import sys + import os + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import unittest + +import time + + +class KeyModuleTest(unittest.TestCase): + def test_get_focused(self): + stop_time = time.time() + 10.0 + while time.time() < stop_time: + time.sleep(1) + + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/rwobject_test.py b/.venv/Lib/site-packages/pygame/tests/rwobject_test.py new file mode 100644 index 00000000..31723aee --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/rwobject_test.py @@ -0,0 +1,139 @@ +import pathlib +import unittest + +from pygame import encode_string, encode_file_path + + +class RWopsEncodeStringTest(unittest.TestCase): + global getrefcount + + def test_obj_None(self): + encoded_string = encode_string(None) + + self.assertIsNone(encoded_string) + + def test_returns_bytes(self): + u = "Hello" + encoded_string = encode_string(u) + + self.assertIsInstance(encoded_string, bytes) + + def test_obj_bytes(self): + b = b"encyclop\xE6dia" + encoded_string = encode_string(b, "ascii", "strict") + + self.assertIs(encoded_string, b) + + def test_encode_unicode(self): + u = "\u00DEe Olde Komp\u00FCter Shoppe" + b = u.encode("utf-8") + self.assertEqual(encode_string(u, "utf-8"), b) + + def test_error_fowarding(self): + self.assertRaises(SyntaxError, encode_string) + + def test_errors(self): + u = "abc\u0109defg\u011Dh\u0125ij\u0135klmnoprs\u015Dtu\u016Dvz" + b = u.encode("ascii", "ignore") + self.assertEqual(encode_string(u, "ascii", "ignore"), b) + + def test_encoding_error(self): + u = "a\x80b" + encoded_string = encode_string(u, "ascii", "strict") + + self.assertIsNone(encoded_string) + + def test_check_defaults(self): + u = "a\u01F7b" + b = u.encode("unicode_escape", "backslashreplace") + encoded_string = encode_string(u) + + self.assertEqual(encoded_string, b) + + def test_etype(self): + u = "a\x80b" + self.assertRaises(SyntaxError, encode_string, u, "ascii", "strict", SyntaxError) + + def test_etype__invalid(self): + """Ensures invalid etypes are properly handled.""" + + for etype in ("SyntaxError", self): + self.assertRaises(TypeError, encode_string, "test", etype=etype) + + def test_string_with_null_bytes(self): + b = b"a\x00b\x00c" + encoded_string = encode_string(b, etype=SyntaxError) + encoded_decode_string = encode_string(b.decode(), "ascii", "strict") + + self.assertIs(encoded_string, b) + self.assertEqual(encoded_decode_string, b) + + try: + from sys import getrefcount as _g + + getrefcount = _g # This nonsense is for Python 3.x + except ImportError: + pass + else: + + def test_refcount(self): + bpath = b" This is a string that is not cached."[1:] + upath = bpath.decode("ascii") + before = getrefcount(bpath) + bpath = encode_string(bpath) + self.assertEqual(getrefcount(bpath), before) + bpath = encode_string(upath) + self.assertEqual(getrefcount(bpath), before) + + def test_smp(self): + utf_8 = b"a\xF0\x93\x82\xA7b" + u = "a\U000130A7b" + b = encode_string(u, "utf-8", "strict", AssertionError) + self.assertEqual(b, utf_8) + + def test_pathlib_obj(self): + """Test loading string representation of pathlib object""" + """ + We do this because pygame functions internally use pg_EncodeString + to decode the filenames passed to them. So if we test that here, we + can safely assume that all those functions do not have any issues + with pathlib objects + """ + encoded = encode_string(pathlib.PurePath("foo"), "utf-8") + self.assertEqual(encoded, b"foo") + + encoded = encode_string(pathlib.Path("baz")) + self.assertEqual(encoded, b"baz") + + +class RWopsEncodeFilePathTest(unittest.TestCase): + # Most tests can be skipped since RWopsEncodeFilePath wraps + # RWopsEncodeString + def test_encoding(self): + u = "Hello" + encoded_file_path = encode_file_path(u) + + self.assertIsInstance(encoded_file_path, bytes) + + def test_error_fowarding(self): + self.assertRaises(SyntaxError, encode_file_path) + + def test_path_with_null_bytes(self): + b = b"a\x00b\x00c" + encoded_file_path = encode_file_path(b) + + self.assertIsNone(encoded_file_path) + + def test_etype(self): + b = b"a\x00b\x00c" + self.assertRaises(TypeError, encode_file_path, b, TypeError) + + def test_etype__invalid(self): + """Ensures invalid etypes are properly handled.""" + + for etype in ("SyntaxError", self): + self.assertRaises(TypeError, encode_file_path, "test", etype) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/scrap_tags.py b/.venv/Lib/site-packages/pygame/tests/scrap_tags.py new file mode 100644 index 00000000..17a82ffd --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/scrap_tags.py @@ -0,0 +1,26 @@ +__tags__ = ["ignore", "subprocess_ignore"] + +# TODO: make scrap_test.py work +# This test used to work only on linux and windows. +# Currently it only work in windows, and in linux it throws: +# `pygame.error: content could not be placed in clipboard.` +# The old test and tags kept here for reference when fixing. + +# import sys +# +# exclude = False +# +# if sys.platform == "win32" or sys.platform.startswith("linux"): +# try: +# import pygame +# +# pygame.scrap._NOT_IMPLEMENTED_ +# except AttributeError: +# pass +# else: +# exclude = True +# else: +# exclude = True +# +# if exclude: +# __tags__.extend(["ignore", "subprocess_ignore"]) diff --git a/.venv/Lib/site-packages/pygame/tests/scrap_test.py b/.venv/Lib/site-packages/pygame/tests/scrap_test.py new file mode 100644 index 00000000..6b7f6faa --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/scrap_test.py @@ -0,0 +1,301 @@ +import os +import sys + +if os.environ.get("SDL_VIDEODRIVER") == "dummy": + __tags__ = ("ignore", "subprocess_ignore") +import unittest +from pygame.tests.test_utils import trunk_relative_path + +import pygame +from pygame import scrap + + +class ScrapModuleTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + pygame.display.init() + pygame.display.set_mode((1, 1)) + scrap.init() + + @classmethod + def tearDownClass(cls): + # scrap.quit() # Does not exist! + pygame.display.quit() + + def test_init(self): + """Ensures scrap module still initialized after multiple init calls.""" + scrap.init() + scrap.init() + + self.assertTrue(scrap.get_init()) + + def test_init__reinit(self): + """Ensures reinitializing the scrap module doesn't clear its data.""" + data_type = pygame.SCRAP_TEXT + expected_data = b"test_init__reinit" + scrap.put(data_type, expected_data) + + scrap.init() + + self.assertEqual(scrap.get(data_type), expected_data) + + def test_get_init(self): + """Ensures get_init gets the init state.""" + self.assertTrue(scrap.get_init()) + + def todo_test_contains(self): + """Ensures contains works as expected.""" + self.fail() + + def todo_test_get(self): + """Ensures get works as expected.""" + self.fail() + + def test_get__owned_empty_type(self): + """Ensures get works when there is no data of the requested type + in the clipboard and the clipboard is owned by the pygame application. + """ + # Use a unique data type identifier to ensure there is no preexisting + # data. + DATA_TYPE = "test_get__owned_empty_type" + + if scrap.lost(): + # Try to acquire the clipboard. + scrap.put(pygame.SCRAP_TEXT, b"text to clipboard") + + if scrap.lost(): + self.skipTest("requires the pygame application to own the clipboard") + + data = scrap.get(DATA_TYPE) + + self.assertIsNone(data) + + def todo_test_get_types(self): + """Ensures get_types works as expected.""" + self.fail() + + def todo_test_lost(self): + """Ensures lost works as expected.""" + self.fail() + + def test_set_mode(self): + """Ensures set_mode works as expected.""" + scrap.set_mode(pygame.SCRAP_SELECTION) + scrap.set_mode(pygame.SCRAP_CLIPBOARD) + + self.assertRaises(ValueError, scrap.set_mode, 1099) + + def test_put__text(self): + """Ensures put can place text into the clipboard.""" + scrap.put(pygame.SCRAP_TEXT, b"Hello world") + + self.assertEqual(scrap.get(pygame.SCRAP_TEXT), b"Hello world") + + scrap.put(pygame.SCRAP_TEXT, b"Another String") + + self.assertEqual(scrap.get(pygame.SCRAP_TEXT), b"Another String") + + @unittest.skipIf("pygame.image" not in sys.modules, "requires pygame.image module") + def test_put__bmp_image(self): + """Ensures put can place a BMP image into the clipboard.""" + sf = pygame.image.load(trunk_relative_path("examples/data/asprite.bmp")) + expected_string = pygame.image.tostring(sf, "RGBA") + scrap.put(pygame.SCRAP_BMP, expected_string) + + self.assertEqual(scrap.get(pygame.SCRAP_BMP), expected_string) + + def test_put(self): + """Ensures put can place data into the clipboard + when using a user defined type identifier. + """ + DATA_TYPE = "arbitrary buffer" + + scrap.put(DATA_TYPE, b"buf") + r = scrap.get(DATA_TYPE) + + self.assertEqual(r, b"buf") + + +class ScrapModuleClipboardNotOwnedTest(unittest.TestCase): + """Test the scrap module's functionality when the pygame application is + not the current owner of the clipboard. + + A separate class is used to prevent tests that acquire the clipboard from + interfering with these tests. + """ + + @classmethod + def setUpClass(cls): + pygame.display.init() + pygame.display.set_mode((1, 1)) + scrap.init() + + @classmethod + def tearDownClass(cls): + # scrap.quit() # Does not exist! + pygame.quit() + pygame.display.quit() + + def _skip_if_clipboard_owned(self): + # Skip test if the pygame application owns the clipboard. Currently, + # there is no way to give up ownership. + if not scrap.lost(): + self.skipTest("requires the pygame application to not own the clipboard") + + def test_get__not_owned(self): + """Ensures get works when there is no data of the requested type + in the clipboard and the clipboard is not owned by the pygame + application. + """ + self._skip_if_clipboard_owned() + + # Use a unique data type identifier to ensure there is no preexisting + # data. + DATA_TYPE = "test_get__not_owned" + + data = scrap.get(DATA_TYPE) + + self.assertIsNone(data) + + def test_get_types__not_owned(self): + """Ensures get_types works when the clipboard is not owned + by the pygame application. + """ + self._skip_if_clipboard_owned() + + data_types = scrap.get_types() + + self.assertIsInstance(data_types, list) + + def test_contains__not_owned(self): + """Ensures contains works when the clipboard is not owned + by the pygame application. + """ + self._skip_if_clipboard_owned() + + # Use a unique data type identifier to ensure there is no preexisting + # data. + DATA_TYPE = "test_contains__not_owned" + + contains = scrap.contains(DATA_TYPE) + + self.assertFalse(contains) + + def test_lost__not_owned(self): + """Ensures lost works when the clipboard is not owned + by the pygame application. + """ + self._skip_if_clipboard_owned() + + lost = scrap.lost() + + self.assertTrue(lost) + + +class X11InteractiveTest(unittest.TestCase): + __tags__ = ["ignore", "subprocess_ignore"] + try: + pygame.display.init() + except Exception: + pass + else: + if pygame.display.get_driver() == "x11": + __tags__ = ["interactive"] + pygame.display.quit() + + def test_issue_208(self): + """PATCH: pygame.scrap on X11, fix copying into PRIMARY selection + + Copying into theX11 PRIMARY selection (mouse copy/paste) would not + work due to a confusion between content type and clipboard type. + + """ + + from pygame import display, event, freetype + from pygame.locals import SCRAP_SELECTION, SCRAP_TEXT + from pygame.locals import KEYDOWN, K_y, QUIT + + success = False + freetype.init() + font = freetype.Font(None, 24) + display.init() + display.set_caption("Interactive X11 Paste Test") + screen = display.set_mode((600, 200)) + screen.fill(pygame.Color("white")) + text = "Scrap put() succeeded." + msg = ( + "Some text has been placed into the X11 clipboard." + " Please click the center mouse button in an open" + " text window to retrieve it." + '\n\nDid you get "{}"? (y/n)' + ).format(text) + word_wrap(screen, msg, font, 6) + display.flip() + event.pump() + scrap.init() + scrap.set_mode(SCRAP_SELECTION) + scrap.put(SCRAP_TEXT, text.encode("UTF-8")) + while True: + e = event.wait() + if e.type == QUIT: + break + if e.type == KEYDOWN: + success = e.key == K_y + break + pygame.display.quit() + self.assertTrue(success) + + +def word_wrap(surf, text, font, margin=0, color=(0, 0, 0)): + font.origin = True + surf_width, surf_height = surf.get_size() + width = surf_width - 2 * margin + height = surf_height - 2 * margin + line_spacing = int(1.25 * font.get_sized_height()) + x, y = margin, margin + line_spacing + space = font.get_rect(" ") + for word in iwords(text): + if word == "\n": + x, y = margin, y + line_spacing + else: + bounds = font.get_rect(word) + if x + bounds.width + bounds.x >= width: + x, y = margin, y + line_spacing + if x + bounds.width + bounds.x >= width: + raise ValueError("word too wide for the surface") + if y + bounds.height - bounds.y >= height: + raise ValueError("text to long for the surface") + font.render_to(surf, (x, y), None, color) + x += bounds.width + space.width + return x, y + + +def iwords(text): + # r"\n|[^ ]+" + # + head = 0 + tail = head + end = len(text) + while head < end: + if text[head] == " ": + head += 1 + tail = head + 1 + elif text[head] == "\n": + head += 1 + yield "\n" + tail = head + 1 + elif tail == end: + yield text[head:] + head = end + elif text[tail] == "\n": + yield text[head:tail] + head = tail + elif text[tail] == " ": + yield text[head:tail] + head = tail + else: + tail += 1 + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/sndarray_tags.py b/.venv/Lib/site-packages/pygame/tests/sndarray_tags.py new file mode 100644 index 00000000..68fa7a5b --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/sndarray_tags.py @@ -0,0 +1,12 @@ +__tags__ = ["array"] + +exclude = False + +try: + import pygame.mixer + import numpy +except ImportError: + exclude = True + +if exclude: + __tags__.extend(("ignore", "subprocess_ignore")) diff --git a/.venv/Lib/site-packages/pygame/tests/sndarray_test.py b/.venv/Lib/site-packages/pygame/tests/sndarray_test.py new file mode 100644 index 00000000..5b624caf --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/sndarray_test.py @@ -0,0 +1,154 @@ +import unittest + +from numpy import int8, int16, uint8, uint16, float32, array, alltrue + +import pygame +import pygame.sndarray + + +class SndarrayTest(unittest.TestCase): + array_dtypes = {8: uint8, -8: int8, 16: uint16, -16: int16, 32: float32} + + def _assert_compatible(self, arr, size): + dtype = self.array_dtypes[size] + self.assertEqual(arr.dtype, dtype) + + def test_array(self): + def check_array(size, channels, test_data): + try: + pygame.mixer.init(22050, size, channels, allowedchanges=0) + except pygame.error: + # Not all sizes are supported on all systems. + return + try: + __, sz, __ = pygame.mixer.get_init() + if sz == size: + srcarr = array(test_data, self.array_dtypes[size]) + snd = pygame.sndarray.make_sound(srcarr) + arr = pygame.sndarray.array(snd) + self._assert_compatible(arr, size) + self.assertTrue( + alltrue(arr == srcarr), + "size: %i\n%s\n%s" % (size, arr, test_data), + ) + finally: + pygame.mixer.quit() + + check_array(8, 1, [0, 0x0F, 0xF0, 0xFF]) + check_array(8, 2, [[0, 0x80], [0x2D, 0x41], [0x64, 0xA1], [0xFF, 0x40]]) + check_array(16, 1, [0, 0x00FF, 0xFF00, 0xFFFF]) + check_array( + 16, 2, [[0, 0xFFFF], [0xFFFF, 0], [0x00FF, 0xFF00], [0x0F0F, 0xF0F0]] + ) + check_array(-8, 1, [0, -0x80, 0x7F, 0x64]) + check_array(-8, 2, [[0, -0x80], [-0x64, 0x64], [0x25, -0x50], [0xFF, 0]]) + check_array(-16, 1, [0, 0x7FFF, -0x7FFF, -1]) + check_array(-16, 2, [[0, -0x7FFF], [-0x7FFF, 0], [0x7FFF, 0], [0, 0x7FFF]]) + + def test_get_arraytype(self): + array_type = pygame.sndarray.get_arraytype() + + self.assertEqual(array_type, "numpy", f"unknown array type {array_type}") + + def test_get_arraytypes(self): + arraytypes = pygame.sndarray.get_arraytypes() + self.assertIn("numpy", arraytypes) + + for atype in arraytypes: + self.assertEqual(atype, "numpy", f"unknown array type {atype}") + + def test_make_sound(self): + def check_sound(size, channels, test_data): + try: + pygame.mixer.init(22050, size, channels, allowedchanges=0) + except pygame.error: + # Not all sizes are supported on all systems. + return + try: + __, sz, __ = pygame.mixer.get_init() + if sz == size: + srcarr = array(test_data, self.array_dtypes[size]) + snd = pygame.sndarray.make_sound(srcarr) + arr = pygame.sndarray.samples(snd) + self.assertTrue( + alltrue(arr == srcarr), + "size: %i\n%s\n%s" % (size, arr, test_data), + ) + finally: + pygame.mixer.quit() + + check_sound(8, 1, [0, 0x0F, 0xF0, 0xFF]) + check_sound(8, 2, [[0, 0x80], [0x2D, 0x41], [0x64, 0xA1], [0xFF, 0x40]]) + check_sound(16, 1, [0, 0x00FF, 0xFF00, 0xFFFF]) + check_sound( + 16, 2, [[0, 0xFFFF], [0xFFFF, 0], [0x00FF, 0xFF00], [0x0F0F, 0xF0F0]] + ) + check_sound(-8, 1, [0, -0x80, 0x7F, 0x64]) + check_sound(-8, 2, [[0, -0x80], [-0x64, 0x64], [0x25, -0x50], [0xFF, 0]]) + check_sound(-16, 1, [0, 0x7FFF, -0x7FFF, -1]) + check_sound(-16, 2, [[0, -0x7FFF], [-0x7FFF, 0], [0x7FFF, 0], [0, 0x7FFF]]) + check_sound(32, 2, [[0.0, -1.0], [-1.0, 0], [1.0, 0], [0, 1.0]]) + + def test_samples(self): + null_byte = b"\x00" + + def check_sample(size, channels, test_data): + try: + pygame.mixer.init(22050, size, channels, allowedchanges=0) + except pygame.error: + # Not all sizes are supported on all systems. + return + try: + __, sz, __ = pygame.mixer.get_init() + if sz == size: + zeroed = null_byte * ((abs(size) // 8) * len(test_data) * channels) + snd = pygame.mixer.Sound(buffer=zeroed) + samples = pygame.sndarray.samples(snd) + self._assert_compatible(samples, size) + ##print('X %s' % (samples.shape,)) + ##print('Y %s' % (test_data,)) + samples[...] = test_data + arr = pygame.sndarray.array(snd) + self.assertTrue( + alltrue(samples == arr), + "size: %i\n%s\n%s" % (size, arr, test_data), + ) + finally: + pygame.mixer.quit() + + check_sample(8, 1, [0, 0x0F, 0xF0, 0xFF]) + check_sample(8, 2, [[0, 0x80], [0x2D, 0x41], [0x64, 0xA1], [0xFF, 0x40]]) + check_sample(16, 1, [0, 0x00FF, 0xFF00, 0xFFFF]) + check_sample( + 16, 2, [[0, 0xFFFF], [0xFFFF, 0], [0x00FF, 0xFF00], [0x0F0F, 0xF0F0]] + ) + check_sample(-8, 1, [0, -0x80, 0x7F, 0x64]) + check_sample(-8, 2, [[0, -0x80], [-0x64, 0x64], [0x25, -0x50], [0xFF, 0]]) + check_sample(-16, 1, [0, 0x7FFF, -0x7FFF, -1]) + check_sample(-16, 2, [[0, -0x7FFF], [-0x7FFF, 0], [0x7FFF, 0], [0, 0x7FFF]]) + check_sample(32, 2, [[0.0, -1.0], [-1.0, 0], [1.0, 0], [0, 1.0]]) + + def test_use_arraytype(self): + def do_use_arraytype(atype): + pygame.sndarray.use_arraytype(atype) + + pygame.sndarray.use_arraytype("numpy") + self.assertEqual(pygame.sndarray.get_arraytype(), "numpy") + + self.assertRaises(ValueError, do_use_arraytype, "not an option") + + def test_float32(self): + """sized arrays work with Sounds and 32bit float arrays.""" + try: + pygame.mixer.init(22050, 32, 2, allowedchanges=0) + except pygame.error: + # Not all sizes are supported on all systems. + self.skipTest("unsupported mixer configuration") + + arr = array([[0.0, -1.0], [-1.0, 0], [1.0, 0], [0, 1.0]], float32) + newsound = pygame.mixer.Sound(array=arr) + pygame.mixer.quit() + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/sprite_test.py b/.venv/Lib/site-packages/pygame/tests/sprite_test.py new file mode 100644 index 00000000..531263c0 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/sprite_test.py @@ -0,0 +1,1412 @@ +#################################### IMPORTS ################################### + + +import unittest + +import pygame +from pygame import sprite + + +################################# MODULE LEVEL ################################# + + +class SpriteModuleTest(unittest.TestCase): + pass + + +######################### SPRITECOLLIDE FUNCTIONS TEST ######################### + + +class SpriteCollideTest(unittest.TestCase): + def setUp(self): + self.ag = sprite.AbstractGroup() + self.ag2 = sprite.AbstractGroup() + self.s1 = sprite.Sprite(self.ag) + self.s2 = sprite.Sprite(self.ag2) + self.s3 = sprite.Sprite(self.ag2) + + self.s1.image = pygame.Surface((50, 10), pygame.SRCALPHA, 32) + self.s2.image = pygame.Surface((10, 10), pygame.SRCALPHA, 32) + self.s3.image = pygame.Surface((10, 10), pygame.SRCALPHA, 32) + + self.s1.rect = self.s1.image.get_rect() + self.s2.rect = self.s2.image.get_rect() + self.s3.rect = self.s3.image.get_rect() + self.s2.rect.move_ip(40, 0) + self.s3.rect.move_ip(100, 100) + + def test_spritecollide__works_if_collided_cb_is_None(self): + # Test that sprites collide without collided function. + self.assertEqual( + sprite.spritecollide(self.s1, self.ag2, dokill=False, collided=None), + [self.s2], + ) + + def test_spritecollide__works_if_collided_cb_not_passed(self): + # Should also work when collided function isn't passed at all. + self.assertEqual( + sprite.spritecollide(self.s1, self.ag2, dokill=False), [self.s2] + ) + + def test_spritecollide__collided_must_be_a_callable(self): + # Need to pass a callable. + self.assertRaises( + TypeError, sprite.spritecollide, self.s1, self.ag2, dokill=False, collided=1 + ) + + def test_spritecollide__collided_defaults_to_collide_rect(self): + # collide_rect should behave the same as default. + self.assertEqual( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=sprite.collide_rect + ), + [self.s2], + ) + + def test_collide_rect_ratio__ratio_of_one_like_default(self): + # collide_rect_ratio should behave the same as default at a 1.0 ratio. + self.assertEqual( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=sprite.collide_rect_ratio(1.0) + ), + [self.s2], + ) + + def test_collide_rect_ratio__collides_all_at_ratio_of_twenty(self): + # collide_rect_ratio should collide all at a 20.0 ratio. + collided_func = sprite.collide_rect_ratio(20.0) + expected_sprites = sorted(self.ag2.sprites(), key=id) + + collided_sprites = sorted( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=collided_func + ), + key=id, + ) + + self.assertListEqual(collided_sprites, expected_sprites) + + def test_collide_circle__no_radius_set(self): + # collide_circle with no radius set. + self.assertEqual( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=sprite.collide_circle + ), + [self.s2], + ) + + def test_collide_circle_ratio__no_radius_and_ratio_of_one(self): + # collide_circle_ratio with no radius set, at a 1.0 ratio. + self.assertEqual( + sprite.spritecollide( + self.s1, + self.ag2, + dokill=False, + collided=sprite.collide_circle_ratio(1.0), + ), + [self.s2], + ) + + def test_collide_circle_ratio__no_radius_and_ratio_of_twenty(self): + # collide_circle_ratio with no radius set, at a 20.0 ratio. + collided_func = sprite.collide_circle_ratio(20.0) + expected_sprites = sorted(self.ag2.sprites(), key=id) + + collided_sprites = sorted( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=collided_func + ), + key=id, + ) + + self.assertListEqual(expected_sprites, collided_sprites) + + def test_collide_circle__radius_set_by_collide_circle_ratio(self): + # Call collide_circle_ratio with no radius set, at a 20.0 ratio. + # That should return group ag2 AND set the radius attribute of the + # sprites in such a way that collide_circle would give same result as + # if it had been called without the radius being set. + collided_func = sprite.collide_circle_ratio(20.0) + + sprite.spritecollide(self.s1, self.ag2, dokill=False, collided=collided_func) + + self.assertEqual( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=sprite.collide_circle + ), + [self.s2], + ) + + def test_collide_circle_ratio__no_radius_and_ratio_of_two_twice(self): + # collide_circle_ratio with no radius set, at a 2.0 ratio, + # called twice to check if the bug where the calculated radius + # is not stored correctly in the radius attribute of each sprite. + collided_func = sprite.collide_circle_ratio(2.0) + + # Calling collide_circle_ratio will set the radius attribute of the + # sprites. If an incorrect value is stored then we will not get the + # same result next time it is called: + expected_sprites = sorted( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=collided_func + ), + key=id, + ) + collided_sprites = sorted( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=collided_func + ), + key=id, + ) + + self.assertListEqual(expected_sprites, collided_sprites) + + def test_collide_circle__with_radii_set(self): + # collide_circle with a radius set. + self.s1.radius = 50 + self.s2.radius = 10 + self.s3.radius = 400 + collided_func = sprite.collide_circle + expected_sprites = sorted(self.ag2.sprites(), key=id) + + collided_sprites = sorted( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=collided_func + ), + key=id, + ) + + self.assertListEqual(expected_sprites, collided_sprites) + + def test_collide_circle_ratio__with_radii_set(self): + # collide_circle_ratio with a radius set. + self.s1.radius = 50 + self.s2.radius = 10 + self.s3.radius = 400 + collided_func = sprite.collide_circle_ratio(0.5) + expected_sprites = sorted(self.ag2.sprites(), key=id) + + collided_sprites = sorted( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=collided_func + ), + key=id, + ) + + self.assertListEqual(expected_sprites, collided_sprites) + + def test_collide_mask__opaque(self): + # make some fully opaque sprites that will collide with masks. + self.s1.image.fill((255, 255, 255, 255)) + self.s2.image.fill((255, 255, 255, 255)) + self.s3.image.fill((255, 255, 255, 255)) + + # masks should be autogenerated from image if they don't exist. + self.assertEqual( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=sprite.collide_mask + ), + [self.s2], + ) + + self.s1.mask = pygame.mask.from_surface(self.s1.image) + self.s2.mask = pygame.mask.from_surface(self.s2.image) + self.s3.mask = pygame.mask.from_surface(self.s3.image) + + # with set masks. + self.assertEqual( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=sprite.collide_mask + ), + [self.s2], + ) + + def test_collide_mask__transparent(self): + # make some sprites that are fully transparent, so they won't collide. + self.s1.image.fill((255, 255, 255, 0)) + self.s2.image.fill((255, 255, 255, 0)) + self.s3.image.fill((255, 255, 255, 0)) + + self.s1.mask = pygame.mask.from_surface(self.s1.image, 255) + self.s2.mask = pygame.mask.from_surface(self.s2.image, 255) + self.s3.mask = pygame.mask.from_surface(self.s3.image, 255) + + self.assertFalse( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=sprite.collide_mask + ) + ) + + def test_spritecollideany__without_collided_callback(self): + # pygame.sprite.spritecollideany(sprite, group) -> sprite + # finds any sprites that collide + + # if collided is not passed, all + # sprites must have a "rect" value, which is a + # rectangle of the sprite area, which will be used + # to calculate the collision. + + # s2 in, s3 out + expected_sprite = self.s2 + collided_sprite = sprite.spritecollideany(self.s1, self.ag2) + + self.assertEqual(collided_sprite, expected_sprite) + + # s2 and s3 out + self.s2.rect.move_ip(0, 10) + collided_sprite = sprite.spritecollideany(self.s1, self.ag2) + + self.assertIsNone(collided_sprite) + + # s2 out, s3 in + self.s3.rect.move_ip(-105, -105) + expected_sprite = self.s3 + collided_sprite = sprite.spritecollideany(self.s1, self.ag2) + + self.assertEqual(collided_sprite, expected_sprite) + + # s2 and s3 in + self.s2.rect.move_ip(0, -10) + expected_sprite_choices = self.ag2.sprites() + collided_sprite = sprite.spritecollideany(self.s1, self.ag2) + + self.assertIn(collided_sprite, expected_sprite_choices) + + def test_spritecollideany__with_collided_callback(self): + # pygame.sprite.spritecollideany(sprite, group) -> sprite + # finds any sprites that collide + + # collided is a callback function used to calculate if + # two sprites are colliding. it should take two sprites + # as values, and return a bool value indicating if + # they are colliding. + + # This collision test can be faster than pygame.sprite.spritecollide() + # since it has less work to do. + + arg_dict_a = {} + arg_dict_b = {} + return_container = [True] + + # This function is configurable using the mutable default arguments! + def collided_callback( + spr_a, + spr_b, + arg_dict_a=arg_dict_a, + arg_dict_b=arg_dict_b, + return_container=return_container, + ): + count = arg_dict_a.get(spr_a, 0) + arg_dict_a[spr_a] = 1 + count + + count = arg_dict_b.get(spr_b, 0) + arg_dict_b[spr_b] = 1 + count + + return return_container[0] + + # This should return a sprite from self.ag2 because the callback + # function (collided_callback()) currently returns True. + expected_sprite_choices = self.ag2.sprites() + collided_sprite = sprite.spritecollideany(self.s1, self.ag2, collided_callback) + + self.assertIn(collided_sprite, expected_sprite_choices) + + # The callback function should have been called only once, so self.s1 + # should have only been passed as an argument once + self.assertEqual(len(arg_dict_a), 1) + self.assertEqual(arg_dict_a[self.s1], 1) + + # The callback function should have been called only once, so self.s2 + # exclusive-or self.s3 should have only been passed as an argument + # once + self.assertEqual(len(arg_dict_b), 1) + self.assertEqual(list(arg_dict_b.values())[0], 1) + self.assertTrue(self.s2 in arg_dict_b or self.s3 in arg_dict_b) + + arg_dict_a.clear() + arg_dict_b.clear() + return_container[0] = False + + # This should return None because the callback function + # (collided_callback()) currently returns False. + collided_sprite = sprite.spritecollideany(self.s1, self.ag2, collided_callback) + + self.assertIsNone(collided_sprite) + + # The callback function should have been called as many times as + # there are sprites in self.ag2 + self.assertEqual(len(arg_dict_a), 1) + self.assertEqual(arg_dict_a[self.s1], len(self.ag2)) + self.assertEqual(len(arg_dict_b), len(self.ag2)) + + # Each sprite in self.ag2 should be called once. + for s in self.ag2: + self.assertEqual(arg_dict_b[s], 1) + + def test_groupcollide__without_collided_callback(self): + # pygame.sprite.groupcollide(groupa, groupb, dokilla, dokillb) -> dict + # collision detection between group and group + + # test no kill + expected_dict = {self.s1: [self.s2]} + crashed = pygame.sprite.groupcollide(self.ag, self.ag2, False, False) + + self.assertDictEqual(expected_dict, crashed) + + crashed = pygame.sprite.groupcollide(self.ag, self.ag2, False, False) + + self.assertDictEqual(expected_dict, crashed) + + # Test dokill2=True (kill colliding sprites in second group). + crashed = pygame.sprite.groupcollide(self.ag, self.ag2, False, True) + + self.assertDictEqual(expected_dict, crashed) + + expected_dict = {} + crashed = pygame.sprite.groupcollide(self.ag, self.ag2, False, False) + + self.assertDictEqual(expected_dict, crashed) + + # Test dokill1=True (kill colliding sprites in first group). + self.s3.rect.move_ip(-100, -100) + expected_dict = {self.s1: [self.s3]} + crashed = pygame.sprite.groupcollide(self.ag, self.ag2, True, False) + + self.assertDictEqual(expected_dict, crashed) + + expected_dict = {} + crashed = pygame.sprite.groupcollide(self.ag, self.ag2, False, False) + + self.assertDictEqual(expected_dict, crashed) + + def test_groupcollide__with_collided_callback(self): + collided_callback_true = lambda spr_a, spr_b: True + collided_callback_false = lambda spr_a, spr_b: False + + # test no kill + expected_dict = {} + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, False, False, collided_callback_false + ) + + self.assertDictEqual(expected_dict, crashed) + + expected_dict = {self.s1: sorted(self.ag2.sprites(), key=id)} + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, False, False, collided_callback_true + ) + for value in crashed.values(): + value.sort(key=id) + + self.assertDictEqual(expected_dict, crashed) + + # expected_dict is the same again for this collide + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, False, False, collided_callback_true + ) + for value in crashed.values(): + value.sort(key=id) + + self.assertDictEqual(expected_dict, crashed) + + # Test dokill2=True (kill colliding sprites in second group). + expected_dict = {} + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, False, True, collided_callback_false + ) + + self.assertDictEqual(expected_dict, crashed) + + expected_dict = {self.s1: sorted(self.ag2.sprites(), key=id)} + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, False, True, collided_callback_true + ) + for value in crashed.values(): + value.sort(key=id) + + self.assertDictEqual(expected_dict, crashed) + + expected_dict = {} + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, False, True, collided_callback_true + ) + + self.assertDictEqual(expected_dict, crashed) + + # Test dokill1=True (kill colliding sprites in first group). + self.ag.add(self.s2) + self.ag2.add(self.s3) + expected_dict = {} + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, True, False, collided_callback_false + ) + + self.assertDictEqual(expected_dict, crashed) + + expected_dict = {self.s1: [self.s3], self.s2: [self.s3]} + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, True, False, collided_callback_true + ) + + self.assertDictEqual(expected_dict, crashed) + + expected_dict = {} + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, True, False, collided_callback_true + ) + + self.assertDictEqual(expected_dict, crashed) + + def test_collide_rect(self): + # Test colliding - some edges touching + self.assertTrue(pygame.sprite.collide_rect(self.s1, self.s2)) + self.assertTrue(pygame.sprite.collide_rect(self.s2, self.s1)) + + # Test colliding - all edges touching + self.s2.rect.center = self.s3.rect.center + + self.assertTrue(pygame.sprite.collide_rect(self.s2, self.s3)) + self.assertTrue(pygame.sprite.collide_rect(self.s3, self.s2)) + + # Test colliding - no edges touching + self.s2.rect.inflate_ip(10, 10) + + self.assertTrue(pygame.sprite.collide_rect(self.s2, self.s3)) + self.assertTrue(pygame.sprite.collide_rect(self.s3, self.s2)) + + # Test colliding - some edges intersecting + self.s2.rect.center = (self.s1.rect.right, self.s1.rect.bottom) + + self.assertTrue(pygame.sprite.collide_rect(self.s1, self.s2)) + self.assertTrue(pygame.sprite.collide_rect(self.s2, self.s1)) + + # Test not colliding + self.assertFalse(pygame.sprite.collide_rect(self.s1, self.s3)) + self.assertFalse(pygame.sprite.collide_rect(self.s3, self.s1)) + + +################################################################################ + + +class AbstractGroupTypeTest(unittest.TestCase): + def setUp(self): + self.ag = sprite.AbstractGroup() + self.ag2 = sprite.AbstractGroup() + self.s1 = sprite.Sprite(self.ag) + self.s2 = sprite.Sprite(self.ag) + self.s3 = sprite.Sprite(self.ag2) + self.s4 = sprite.Sprite(self.ag2) + + self.s1.image = pygame.Surface((10, 10)) + self.s1.image.fill(pygame.Color("red")) + self.s1.rect = self.s1.image.get_rect() + + self.s2.image = pygame.Surface((10, 10)) + self.s2.image.fill(pygame.Color("green")) + self.s2.rect = self.s2.image.get_rect() + self.s2.rect.left = 10 + + self.s3.image = pygame.Surface((10, 10)) + self.s3.image.fill(pygame.Color("blue")) + self.s3.rect = self.s3.image.get_rect() + self.s3.rect.top = 10 + + self.s4.image = pygame.Surface((10, 10)) + self.s4.image.fill(pygame.Color("white")) + self.s4.rect = self.s4.image.get_rect() + self.s4.rect.left = 10 + self.s4.rect.top = 10 + + self.bg = pygame.Surface((20, 20)) + self.scr = pygame.Surface((20, 20)) + self.scr.fill(pygame.Color("grey")) + + def test_has(self): + "See if AbstractGroup.has() works as expected." + + self.assertEqual(True, self.s1 in self.ag) + + self.assertEqual(True, self.ag.has(self.s1)) + + self.assertEqual(True, self.ag.has([self.s1, self.s2])) + + # see if one of them not being in there. + self.assertNotEqual(True, self.ag.has([self.s1, self.s2, self.s3])) + self.assertNotEqual(True, self.ag.has(self.s1, self.s2, self.s3)) + self.assertNotEqual(True, self.ag.has(self.s1, sprite.Group(self.s2, self.s3))) + self.assertNotEqual(True, self.ag.has(self.s1, [self.s2, self.s3])) + + # test empty list processing + self.assertFalse(self.ag.has(*[])) + self.assertFalse(self.ag.has([])) + self.assertFalse(self.ag.has([[]])) + + # see if a second AbstractGroup works. + self.assertEqual(True, self.ag2.has(self.s3)) + + def test_add(self): + ag3 = sprite.AbstractGroup() + sprites = (self.s1, self.s2, self.s3, self.s4) + + for s in sprites: + self.assertNotIn(s, ag3) + + ag3.add(self.s1, [self.s2], self.ag2) + + for s in sprites: + self.assertIn(s, ag3) + + def test_add_internal(self): + self.assertNotIn(self.s1, self.ag2) + + self.ag2.add_internal(self.s1) + + self.assertIn(self.s1, self.ag2) + + def test_clear(self): + self.ag.draw(self.scr) + self.ag.clear(self.scr, self.bg) + self.assertEqual((0, 0, 0, 255), self.scr.get_at((5, 5))) + self.assertEqual((0, 0, 0, 255), self.scr.get_at((15, 5))) + + def test_draw(self): + self.ag.draw(self.scr) + self.assertEqual((255, 0, 0, 255), self.scr.get_at((5, 5))) + self.assertEqual((0, 255, 0, 255), self.scr.get_at((15, 5))) + + self.assertEqual(self.ag.spritedict[self.s1], pygame.Rect(0, 0, 10, 10)) + self.assertEqual(self.ag.spritedict[self.s2], pygame.Rect(10, 0, 10, 10)) + + def test_empty(self): + self.ag.empty() + self.assertFalse(self.s1 in self.ag) + self.assertFalse(self.s2 in self.ag) + + def test_has_internal(self): + self.assertTrue(self.ag.has_internal(self.s1)) + self.assertFalse(self.ag.has_internal(self.s3)) + + def test_remove(self): + # Test removal of 1 sprite + self.ag.remove(self.s1) + self.assertFalse(self.ag in self.s1.groups()) + self.assertFalse(self.ag.has(self.s1)) + + # Test removal of 2 sprites as 2 arguments + self.ag2.remove(self.s3, self.s4) + self.assertFalse(self.ag2 in self.s3.groups()) + self.assertFalse(self.ag2 in self.s4.groups()) + self.assertFalse(self.ag2.has(self.s3, self.s4)) + + # Test removal of 4 sprites as a list containing a sprite and a group + # containing a sprite and another group containing 2 sprites. + self.ag.add(self.s1, self.s3, self.s4) + self.ag2.add(self.s3, self.s4) + g = sprite.Group(self.s2) + self.ag.remove([self.s1, g], self.ag2) + self.assertFalse(self.ag in self.s1.groups()) + self.assertFalse(self.ag in self.s2.groups()) + self.assertFalse(self.ag in self.s3.groups()) + self.assertFalse(self.ag in self.s4.groups()) + self.assertFalse(self.ag.has(self.s1, self.s2, self.s3, self.s4)) + + def test_remove_internal(self): + self.ag.remove_internal(self.s1) + self.assertFalse(self.ag.has_internal(self.s1)) + + def test_sprites(self): + expected_sprites = sorted((self.s1, self.s2), key=id) + sprite_list = sorted(self.ag.sprites(), key=id) + + self.assertListEqual(sprite_list, expected_sprites) + + def test_update(self): + class test_sprite(pygame.sprite.Sprite): + sink = [] + + def __init__(self, *groups): + pygame.sprite.Sprite.__init__(self, *groups) + + def update(self, *args): + self.sink += args + + s = test_sprite(self.ag) + self.ag.update(1, 2, 3) + + self.assertEqual(test_sprite.sink, [1, 2, 3]) + + def test_update_with_kwargs(self): + class test_sprite(pygame.sprite.Sprite): + sink = [] + sink_kwargs = {} + + def __init__(self, *groups): + pygame.sprite.Sprite.__init__(self, *groups) + + def update(self, *args, **kwargs): + self.sink += args + self.sink_kwargs.update(kwargs) + + s = test_sprite(self.ag) + self.ag.update(1, 2, 3, foo=4, bar=5) + + self.assertEqual(test_sprite.sink, [1, 2, 3]) + self.assertEqual(test_sprite.sink_kwargs, {"foo": 4, "bar": 5}) + + +################################################################################ + +# A base class to share tests between similar classes + + +class LayeredGroupBase: + def test_get_layer_of_sprite(self): + expected_layer = 666 + spr = self.sprite() + self.LG.add(spr, layer=expected_layer) + layer = self.LG.get_layer_of_sprite(spr) + + self.assertEqual(len(self.LG._spritelist), 1) + self.assertEqual(layer, self.LG.get_layer_of_sprite(spr)) + self.assertEqual(layer, expected_layer) + self.assertEqual(layer, self.LG._spritelayers[spr]) + + def test_add(self): + expected_layer = self.LG._default_layer + spr = self.sprite() + self.LG.add(spr) + layer = self.LG.get_layer_of_sprite(spr) + + self.assertEqual(len(self.LG._spritelist), 1) + self.assertEqual(layer, expected_layer) + + def test_add__sprite_with_layer_attribute(self): + expected_layer = 100 + spr = self.sprite() + spr._layer = expected_layer + self.LG.add(spr) + layer = self.LG.get_layer_of_sprite(spr) + + self.assertEqual(len(self.LG._spritelist), 1) + self.assertEqual(layer, expected_layer) + + def test_add__passing_layer_keyword(self): + expected_layer = 100 + spr = self.sprite() + self.LG.add(spr, layer=expected_layer) + layer = self.LG.get_layer_of_sprite(spr) + + self.assertEqual(len(self.LG._spritelist), 1) + self.assertEqual(layer, expected_layer) + + def test_add__overriding_sprite_layer_attr(self): + expected_layer = 200 + spr = self.sprite() + spr._layer = 100 + self.LG.add(spr, layer=expected_layer) + layer = self.LG.get_layer_of_sprite(spr) + + self.assertEqual(len(self.LG._spritelist), 1) + self.assertEqual(layer, expected_layer) + + def test_add__adding_sprite_on_init(self): + spr = self.sprite() + lrg2 = sprite.LayeredUpdates(spr) + expected_layer = lrg2._default_layer + layer = lrg2._spritelayers[spr] + + self.assertEqual(len(lrg2._spritelist), 1) + self.assertEqual(layer, expected_layer) + + def test_add__sprite_init_layer_attr(self): + expected_layer = 20 + spr = self.sprite() + spr._layer = expected_layer + lrg2 = sprite.LayeredUpdates(spr) + layer = lrg2._spritelayers[spr] + + self.assertEqual(len(lrg2._spritelist), 1) + self.assertEqual(layer, expected_layer) + + def test_add__sprite_init_passing_layer(self): + expected_layer = 33 + spr = self.sprite() + lrg2 = sprite.LayeredUpdates(spr, layer=expected_layer) + layer = lrg2._spritelayers[spr] + + self.assertEqual(len(lrg2._spritelist), 1) + self.assertEqual(layer, expected_layer) + + def test_add__sprite_init_overiding_layer(self): + expected_layer = 33 + spr = self.sprite() + spr._layer = 55 + lrg2 = sprite.LayeredUpdates(spr, layer=expected_layer) + layer = lrg2._spritelayers[spr] + + self.assertEqual(len(lrg2._spritelist), 1) + self.assertEqual(layer, expected_layer) + + def test_add__spritelist(self): + expected_layer = self.LG._default_layer + sprite_count = 10 + sprites = [self.sprite() for _ in range(sprite_count)] + + self.LG.add(sprites) + + self.assertEqual(len(self.LG._spritelist), sprite_count) + + for i in range(sprite_count): + layer = self.LG.get_layer_of_sprite(sprites[i]) + + self.assertEqual(layer, expected_layer) + + def test_add__spritelist_with_layer_attr(self): + sprites = [] + sprite_and_layer_count = 10 + for i in range(sprite_and_layer_count): + sprites.append(self.sprite()) + sprites[-1]._layer = i + + self.LG.add(sprites) + + self.assertEqual(len(self.LG._spritelist), sprite_and_layer_count) + + for i in range(sprite_and_layer_count): + layer = self.LG.get_layer_of_sprite(sprites[i]) + + self.assertEqual(layer, i) + + def test_add__spritelist_passing_layer(self): + expected_layer = 33 + sprite_count = 10 + sprites = [self.sprite() for _ in range(sprite_count)] + + self.LG.add(sprites, layer=expected_layer) + + self.assertEqual(len(self.LG._spritelist), sprite_count) + + for i in range(sprite_count): + layer = self.LG.get_layer_of_sprite(sprites[i]) + + self.assertEqual(layer, expected_layer) + + def test_add__spritelist_overriding_layer(self): + expected_layer = 33 + sprites = [] + sprite_and_layer_count = 10 + for i in range(sprite_and_layer_count): + sprites.append(self.sprite()) + sprites[-1].layer = i + + self.LG.add(sprites, layer=expected_layer) + + self.assertEqual(len(self.LG._spritelist), sprite_and_layer_count) + + for i in range(sprite_and_layer_count): + layer = self.LG.get_layer_of_sprite(sprites[i]) + + self.assertEqual(layer, expected_layer) + + def test_add__spritelist_init(self): + sprite_count = 10 + sprites = [self.sprite() for _ in range(sprite_count)] + + lrg2 = sprite.LayeredUpdates(sprites) + expected_layer = lrg2._default_layer + + self.assertEqual(len(lrg2._spritelist), sprite_count) + + for i in range(sprite_count): + layer = lrg2.get_layer_of_sprite(sprites[i]) + + self.assertEqual(layer, expected_layer) + + def test_remove__sprite(self): + sprites = [] + sprite_count = 10 + for i in range(sprite_count): + sprites.append(self.sprite()) + sprites[-1].rect = pygame.Rect((0, 0, 0, 0)) + + self.LG.add(sprites) + + self.assertEqual(len(self.LG._spritelist), sprite_count) + + for i in range(sprite_count): + self.LG.remove(sprites[i]) + + self.assertEqual(len(self.LG._spritelist), 0) + + def test_sprites(self): + sprites = [] + sprite_and_layer_count = 10 + for i in range(sprite_and_layer_count, 0, -1): + sprites.append(self.sprite()) + sprites[-1]._layer = i + + self.LG.add(sprites) + + self.assertEqual(len(self.LG._spritelist), sprite_and_layer_count) + + # Sprites should be ordered based on their layer (bottom to top), + # which is the reverse order of the sprites list. + expected_sprites = list(reversed(sprites)) + actual_sprites = self.LG.sprites() + + self.assertListEqual(actual_sprites, expected_sprites) + + def test_layers(self): + sprites = [] + expected_layers = [] + layer_count = 10 + for i in range(layer_count): + expected_layers.append(i) + for j in range(5): + sprites.append(self.sprite()) + sprites[-1]._layer = i + self.LG.add(sprites) + + layers = self.LG.layers() + + self.assertListEqual(layers, expected_layers) + + def test_add__layers_are_correct(self): + layers = [1, 4, 6, 8, 3, 6, 2, 6, 4, 5, 6, 1, 0, 9, 7, 6, 54, 8, 2, 43, 6, 1] + for lay in layers: + self.LG.add(self.sprite(), layer=lay) + layers.sort() + + for idx, spr in enumerate(self.LG.sprites()): + layer = self.LG.get_layer_of_sprite(spr) + + self.assertEqual(layer, layers[idx]) + + def test_change_layer(self): + expected_layer = 99 + spr = self.sprite() + self.LG.add(spr, layer=expected_layer) + + self.assertEqual(self.LG._spritelayers[spr], expected_layer) + + expected_layer = 44 + self.LG.change_layer(spr, expected_layer) + + self.assertEqual(self.LG._spritelayers[spr], expected_layer) + + expected_layer = 77 + spr2 = self.sprite() + spr2.layer = 55 + self.LG.add(spr2) + self.LG.change_layer(spr2, expected_layer) + + self.assertEqual(spr2.layer, expected_layer) + + def test_get_sprites_at(self): + sprites = [] + expected_sprites = [] + for i in range(3): + spr = self.sprite() + spr.rect = pygame.Rect(i * 50, i * 50, 100, 100) + sprites.append(spr) + if i < 2: + expected_sprites.append(spr) + self.LG.add(sprites) + result = self.LG.get_sprites_at((50, 50)) + self.assertEqual(result, expected_sprites) + + def test_get_top_layer(self): + layers = [1, 5, 2, 8, 4, 5, 3, 88, 23, 0] + for i in layers: + self.LG.add(self.sprite(), layer=i) + top_layer = self.LG.get_top_layer() + + self.assertEqual(top_layer, self.LG.get_top_layer()) + self.assertEqual(top_layer, max(layers)) + self.assertEqual(top_layer, max(self.LG._spritelayers.values())) + self.assertEqual(top_layer, self.LG._spritelayers[self.LG._spritelist[-1]]) + + def test_get_bottom_layer(self): + layers = [1, 5, 2, 8, 4, 5, 3, 88, 23, 0] + for i in layers: + self.LG.add(self.sprite(), layer=i) + bottom_layer = self.LG.get_bottom_layer() + + self.assertEqual(bottom_layer, self.LG.get_bottom_layer()) + self.assertEqual(bottom_layer, min(layers)) + self.assertEqual(bottom_layer, min(self.LG._spritelayers.values())) + self.assertEqual(bottom_layer, self.LG._spritelayers[self.LG._spritelist[0]]) + + def test_move_to_front(self): + layers = [1, 5, 2, 8, 4, 5, 3, 88, 23, 0] + for i in layers: + self.LG.add(self.sprite(), layer=i) + spr = self.sprite() + self.LG.add(spr, layer=3) + + self.assertNotEqual(spr, self.LG._spritelist[-1]) + + self.LG.move_to_front(spr) + + self.assertEqual(spr, self.LG._spritelist[-1]) + + def test_move_to_back(self): + layers = [1, 5, 2, 8, 4, 5, 3, 88, 23, 0] + for i in layers: + self.LG.add(self.sprite(), layer=i) + spr = self.sprite() + self.LG.add(spr, layer=55) + + self.assertNotEqual(spr, self.LG._spritelist[0]) + + self.LG.move_to_back(spr) + + self.assertEqual(spr, self.LG._spritelist[0]) + + def test_get_top_sprite(self): + layers = [1, 5, 2, 8, 4, 5, 3, 88, 23, 0] + for i in layers: + self.LG.add(self.sprite(), layer=i) + expected_layer = self.LG.get_top_layer() + layer = self.LG.get_layer_of_sprite(self.LG.get_top_sprite()) + + self.assertEqual(layer, expected_layer) + + def test_get_sprites_from_layer(self): + sprites = {} + layers = [ + 1, + 4, + 5, + 6, + 3, + 7, + 8, + 2, + 1, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 0, + 1, + 6, + 5, + 4, + 3, + 2, + ] + for lay in layers: + spr = self.sprite() + spr._layer = lay + self.LG.add(spr) + if lay not in sprites: + sprites[lay] = [] + sprites[lay].append(spr) + + for lay in self.LG.layers(): + for spr in self.LG.get_sprites_from_layer(lay): + self.assertIn(spr, sprites[lay]) + + sprites[lay].remove(spr) + if len(sprites[lay]) == 0: + del sprites[lay] + + self.assertEqual(len(sprites.values()), 0) + + def test_switch_layer(self): + sprites1 = [] + sprites2 = [] + layers = [3, 2, 3, 2, 3, 3, 2, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 3, 2, 2, 3, 2, 3] + for lay in layers: + spr = self.sprite() + spr._layer = lay + self.LG.add(spr) + if lay == 2: + sprites1.append(spr) + else: + sprites2.append(spr) + + sprites1.sort(key=id) + sprites2.sort(key=id) + layer2_sprites = sorted(self.LG.get_sprites_from_layer(2), key=id) + layer3_sprites = sorted(self.LG.get_sprites_from_layer(3), key=id) + + self.assertListEqual(sprites1, layer2_sprites) + self.assertListEqual(sprites2, layer3_sprites) + self.assertEqual(len(self.LG), len(sprites1) + len(sprites2)) + + self.LG.switch_layer(2, 3) + layer2_sprites = sorted(self.LG.get_sprites_from_layer(2), key=id) + layer3_sprites = sorted(self.LG.get_sprites_from_layer(3), key=id) + + self.assertListEqual(sprites1, layer3_sprites) + self.assertListEqual(sprites2, layer2_sprites) + self.assertEqual(len(self.LG), len(sprites1) + len(sprites2)) + + def test_copy(self): + self.LG.add(self.sprite()) + spr = self.LG.sprites()[0] + lg_copy = self.LG.copy() + + self.assertIsInstance(lg_copy, type(self.LG)) + self.assertIn(spr, lg_copy) + self.assertIn(lg_copy, spr.groups()) + + +########################## LAYERED RENDER GROUP TESTS ########################## + + +class LayeredUpdatesTypeTest__SpriteTest(LayeredGroupBase, unittest.TestCase): + sprite = sprite.Sprite + + def setUp(self): + self.LG = sprite.LayeredUpdates() + + +class LayeredUpdatesTypeTest__DirtySprite(LayeredGroupBase, unittest.TestCase): + sprite = sprite.DirtySprite + + def setUp(self): + self.LG = sprite.LayeredUpdates() + + +class LayeredDirtyTypeTest__DirtySprite(LayeredGroupBase, unittest.TestCase): + sprite = sprite.DirtySprite + + def setUp(self): + self.LG = sprite.LayeredDirty() + + def test_repaint_rect(self): + group = self.LG + surface = pygame.Surface((100, 100)) + + group.repaint_rect(pygame.Rect(0, 0, 100, 100)) + group.draw(surface) + + def test_repaint_rect_with_clip(self): + group = self.LG + surface = pygame.Surface((100, 100)) + + group.set_clip(pygame.Rect(0, 0, 100, 100)) + group.repaint_rect(pygame.Rect(0, 0, 100, 100)) + group.draw(surface) + + def _nondirty_intersections_redrawn(self, use_source_rect=False): + # Helper method to ensure non-dirty sprites are redrawn correctly. + # + # Parameters: + # use_source_rect - allows non-dirty sprites to be tested + # with (True) and without (False) a source_rect + # + # This test was written to reproduce the behavior seen in issue #898. + # A non-dirty sprite (using source_rect) was being redrawn incorrectly + # after a dirty sprite intersected with it. + # + # This test does the following. + # 1. Creates a surface filled with white. Also creates an image_source + # with a default fill color of yellow and adds 2 images to it + # (red and blue rectangles). + # 2. Creates 2 DirtySprites (red_sprite and blue_sprite) using the + # image_source and adds them to a LayeredDirty group. + # 3. Moves the red_sprite and calls LayeredDirty.draw(surface) a few + # times. + # 4. Checks to make sure the sprites were redrawn correctly. + RED = pygame.Color("red") + BLUE = pygame.Color("blue") + WHITE = pygame.Color("white") + YELLOW = pygame.Color("yellow") + + surface = pygame.Surface((60, 80)) + surface.fill(WHITE) + start_pos = (10, 10) + + # These rects define each sprite's image area in the image_source. + red_sprite_source = pygame.Rect((45, 0), (5, 4)) + blue_sprite_source = pygame.Rect((0, 40), (20, 10)) + + # Create a source image/surface. + image_source = pygame.Surface((50, 50)) + image_source.fill(YELLOW) + image_source.fill(RED, red_sprite_source) + image_source.fill(BLUE, blue_sprite_source) + + # The blue_sprite is stationary and will not reset its dirty flag. It + # will be the non-dirty sprite in this test. Its values are dependent + # on the use_source_rect flag. + blue_sprite = pygame.sprite.DirtySprite(self.LG) + + if use_source_rect: + blue_sprite.image = image_source + # The rect is a bit smaller than the source_rect to make sure + # LayeredDirty.draw() is using the correct dimensions. + blue_sprite.rect = pygame.Rect( + start_pos, (blue_sprite_source.w - 7, blue_sprite_source.h - 7) + ) + blue_sprite.source_rect = blue_sprite_source + start_x, start_y = blue_sprite.rect.topleft + end_x = start_x + blue_sprite.source_rect.w + end_y = start_y + blue_sprite.source_rect.h + else: + blue_sprite.image = image_source.subsurface(blue_sprite_source) + blue_sprite.rect = pygame.Rect(start_pos, blue_sprite_source.size) + start_x, start_y = blue_sprite.rect.topleft + end_x, end_y = blue_sprite.rect.bottomright + + # The red_sprite is moving and will always be dirty. + red_sprite = pygame.sprite.DirtySprite(self.LG) + red_sprite.image = image_source + red_sprite.rect = pygame.Rect(start_pos, red_sprite_source.size) + red_sprite.source_rect = red_sprite_source + red_sprite.dirty = 2 + + # Draw the red_sprite as it moves a few steps. + for _ in range(4): + red_sprite.rect.move_ip(2, 1) + + # This is the method being tested. + self.LG.draw(surface) + + # Check colors where the blue_sprite is drawn. We expect red where the + # red_sprite is drawn over the blue_sprite, but the rest should be + # blue. + surface.lock() # Lock surface for possible speed up. + try: + for y in range(start_y, end_y): + for x in range(start_x, end_x): + if red_sprite.rect.collidepoint(x, y): + expected_color = RED + else: + expected_color = BLUE + + color = surface.get_at((x, y)) + + self.assertEqual(color, expected_color, f"pos=({x}, {y})") + finally: + surface.unlock() + + def test_nondirty_intersections_redrawn(self): + """Ensure non-dirty sprites are correctly redrawn + when dirty sprites intersect with them. + """ + self._nondirty_intersections_redrawn() + + def test_nondirty_intersections_redrawn__with_source_rect(self): + """Ensure non-dirty sprites using source_rects are correctly redrawn + when dirty sprites intersect with them. + + Related to issue #898. + """ + self._nondirty_intersections_redrawn(True) + + +############################### SPRITE BASE CLASS ############################## +# +# tests common between sprite classes + + +class SpriteBase: + def setUp(self): + self.groups = [] + for Group in self.Groups: + self.groups.append(Group()) + + self.sprite = self.Sprite() + + def test_add_internal(self): + for g in self.groups: + self.sprite.add_internal(g) + + for g in self.groups: + self.assertIn(g, self.sprite.groups()) + + def test_remove_internal(self): + for g in self.groups: + self.sprite.add_internal(g) + + for g in self.groups: + self.sprite.remove_internal(g) + + for g in self.groups: + self.assertFalse(g in self.sprite.groups()) + + def test_update(self): + # What does this and the next test actually test? + class test_sprite(pygame.sprite.Sprite): + sink = [] + + def __init__(self, *groups): + pygame.sprite.Sprite.__init__(self, *groups) + + def update(self, *args): + self.sink += args + + s = test_sprite() + s.update(1, 2, 3) + + self.assertEqual(test_sprite.sink, [1, 2, 3]) + + def test_update_with_kwargs(self): + class test_sprite(pygame.sprite.Sprite): + sink = [] + sink_dict = {} + + def __init__(self, *groups): + pygame.sprite.Sprite.__init__(self, *groups) + + def update(self, *args, **kwargs): + self.sink += args + self.sink_dict.update(kwargs) + + s = test_sprite() + s.update(1, 2, 3, foo=4, bar=5) + + self.assertEqual(test_sprite.sink, [1, 2, 3]) + self.assertEqual(test_sprite.sink_dict, {"foo": 4, "bar": 5}) + + def test___init____added_to_groups_passed(self): + expected_groups = sorted(self.groups, key=id) + sprite = self.Sprite(self.groups) + groups = sorted(sprite.groups(), key=id) + + self.assertListEqual(groups, expected_groups) + + def test_add(self): + expected_groups = sorted(self.groups, key=id) + self.sprite.add(self.groups) + groups = sorted(self.sprite.groups(), key=id) + + self.assertListEqual(groups, expected_groups) + + def test_alive(self): + self.assertFalse( + self.sprite.alive(), "Sprite should not be alive if in no groups" + ) + + self.sprite.add(self.groups) + + self.assertTrue(self.sprite.alive()) + + def test_groups(self): + for i, g in enumerate(self.groups): + expected_groups = sorted(self.groups[: i + 1], key=id) + self.sprite.add(g) + groups = sorted(self.sprite.groups(), key=id) + + self.assertListEqual(groups, expected_groups) + + def test_kill(self): + self.sprite.add(self.groups) + + self.assertTrue(self.sprite.alive()) + + self.sprite.kill() + + self.assertListEqual(self.sprite.groups(), []) + self.assertFalse(self.sprite.alive()) + + def test_remove(self): + self.sprite.add(self.groups) + self.sprite.remove(self.groups) + + self.assertListEqual(self.sprite.groups(), []) + + +############################## SPRITE CLASS TESTS ############################## + + +class SpriteTypeTest(SpriteBase, unittest.TestCase): + Sprite = sprite.Sprite + + Groups = [ + sprite.Group, + sprite.LayeredUpdates, + sprite.RenderUpdates, + sprite.OrderedUpdates, + ] + + +class DirtySpriteTypeTest(SpriteBase, unittest.TestCase): + Sprite = sprite.DirtySprite + + Groups = [ + sprite.Group, + sprite.LayeredUpdates, + sprite.RenderUpdates, + sprite.OrderedUpdates, + sprite.LayeredDirty, + ] + + +class WeakSpriteTypeTest(SpriteTypeTest): + Sprite = sprite.WeakSprite + + def test_weak_group_ref(self): + """ + We create a list of groups, add them to the sprite. + When we then delete the groups, the sprite should be "dead" + """ + import gc + + groups = [Group() for Group in self.Groups] + self.sprite.add(groups) + del groups + gc.collect() + self.assertFalse(self.sprite.alive()) + + +class DirtyWeakSpriteTypeTest(DirtySpriteTypeTest, WeakSpriteTypeTest): + Sprite = sprite.WeakDirtySprite + + +############################## BUG TESTS ####################################### + + +class SingleGroupBugsTest(unittest.TestCase): + def test_memoryleak_bug(self): + # For memoryleak bug posted to mailing list by Tobias Steinrücken on 16/11/10. + # Fixed in revision 2953. + + import weakref + import gc + + class MySprite(sprite.Sprite): + def __init__(self, *args, **kwargs): + sprite.Sprite.__init__(self, *args, **kwargs) + self.image = pygame.Surface((2, 4), 0, 24) + self.rect = self.image.get_rect() + + g = sprite.GroupSingle() + screen = pygame.Surface((4, 8), 0, 24) + s = MySprite() + r = weakref.ref(s) + g.sprite = s + del s + gc.collect() + + self.assertIsNotNone(r()) + + g.update() + g.draw(screen) + g.sprite = MySprite() + gc.collect() + + self.assertIsNone(r()) + + +################################################################################ + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/surface_test.py b/.venv/Lib/site-packages/pygame/tests/surface_test.py new file mode 100644 index 00000000..5ce78b6e --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/surface_test.py @@ -0,0 +1,4072 @@ +import os +import unittest +from pygame.tests import test_utils +from pygame.tests.test_utils import ( + example_path, + SurfaceSubclass, +) + +try: + from pygame.tests.test_utils.arrinter import * +except (ImportError, NameError): + pass +import pygame +from pygame.locals import * +from pygame.bufferproxy import BufferProxy + +import platform +import gc +import weakref +import ctypes + +IS_PYPY = "PyPy" == platform.python_implementation() + + +class SurfaceTypeTest(unittest.TestCase): + def test_surface__pixel_format_as_surface_subclass(self): + """Ensure a subclassed surface can be used for pixel format + when creating a new surface.""" + expected_depth = 16 + expected_flags = SRCALPHA + expected_size = (13, 37) + depth_surface = SurfaceSubclass((11, 21), expected_flags, expected_depth) + + surface = pygame.Surface(expected_size, expected_flags, depth_surface) + + self.assertIsNot(surface, depth_surface) + self.assertIsInstance(surface, pygame.Surface) + self.assertNotIsInstance(surface, SurfaceSubclass) + self.assertEqual(surface.get_size(), expected_size) + self.assertEqual(surface.get_flags(), expected_flags) + self.assertEqual(surface.get_bitsize(), expected_depth) + + def test_surface_created_opaque_black(self): + surf = pygame.Surface((20, 20)) + self.assertEqual(surf.get_at((0, 0)), (0, 0, 0, 255)) + + # See https://github.com/pygame/pygame/issues/1395 + pygame.display.set_mode((500, 500)) + surf = pygame.Surface((20, 20)) + self.assertEqual(surf.get_at((0, 0)), (0, 0, 0, 255)) + + def test_set_clip(self): + """see if surface.set_clip(None) works correctly.""" + s = pygame.Surface((800, 600)) + r = pygame.Rect(10, 10, 10, 10) + s.set_clip(r) + r.move_ip(10, 0) + s.set_clip(None) + res = s.get_clip() + # this was garbled before. + self.assertEqual(res[0], 0) + self.assertEqual(res[2], 800) + + def test_print(self): + surf = pygame.Surface((70, 70), 0, 32) + self.assertEqual(repr(surf), "") + + def test_keyword_arguments(self): + surf = pygame.Surface((70, 70), flags=SRCALPHA, depth=32) + self.assertEqual(surf.get_flags() & SRCALPHA, SRCALPHA) + self.assertEqual(surf.get_bitsize(), 32) + + # sanity check to make sure the check below is valid + surf_16 = pygame.Surface((70, 70), 0, 16) + self.assertEqual(surf_16.get_bytesize(), 2) + + # try again with an argument list + surf_16 = pygame.Surface((70, 70), depth=16) + self.assertEqual(surf_16.get_bytesize(), 2) + + def test_set_at(self): + # 24bit surfaces + s = pygame.Surface((100, 100), 0, 24) + s.fill((0, 0, 0)) + + # set it with a tuple. + s.set_at((0, 0), (10, 10, 10, 255)) + r = s.get_at((0, 0)) + self.assertIsInstance(r, pygame.Color) + self.assertEqual(r, (10, 10, 10, 255)) + + # try setting a color with a single integer. + s.fill((0, 0, 0, 255)) + s.set_at((10, 1), 0x0000FF) + r = s.get_at((10, 1)) + self.assertEqual(r, (0, 0, 255, 255)) + + def test_set_at__big_endian(self): + """png files are loaded in big endian format (BGR rather than RGB)""" + pygame.display.init() + try: + image = pygame.image.load(example_path(os.path.join("data", "BGR.png"))) + # Check they start red, green and blue + self.assertEqual(image.get_at((10, 10)), pygame.Color(255, 0, 0)) + self.assertEqual(image.get_at((10, 20)), pygame.Color(0, 255, 0)) + self.assertEqual(image.get_at((10, 40)), pygame.Color(0, 0, 255)) + # Set three pixels that are already red, green, blue + # to red, green and, blue with set_at: + image.set_at((10, 10), pygame.Color(255, 0, 0)) + image.set_at((10, 20), pygame.Color(0, 255, 0)) + image.set_at((10, 40), pygame.Color(0, 0, 255)) + + # Check they still are + self.assertEqual(image.get_at((10, 10)), pygame.Color(255, 0, 0)) + self.assertEqual(image.get_at((10, 20)), pygame.Color(0, 255, 0)) + self.assertEqual(image.get_at((10, 40)), pygame.Color(0, 0, 255)) + + finally: + pygame.display.quit() + + def test_SRCALPHA(self): + # has the flag been passed in ok? + surf = pygame.Surface((70, 70), SRCALPHA, 32) + self.assertEqual(surf.get_flags() & SRCALPHA, SRCALPHA) + + # 24bit surfaces can not have SRCALPHA. + self.assertRaises(ValueError, pygame.Surface, (100, 100), pygame.SRCALPHA, 24) + + # if we have a 32 bit surface, the SRCALPHA should have worked too. + surf2 = pygame.Surface((70, 70), SRCALPHA) + if surf2.get_bitsize() == 32: + self.assertEqual(surf2.get_flags() & SRCALPHA, SRCALPHA) + + def test_flags_default0_nodisplay(self): + """is set to zero, and SRCALPHA is not set by default with no display initialized.""" + pygame.display.quit() + surf = pygame.Surface((70, 70)) + self.assertEqual(surf.get_flags() & SRCALPHA, 0) + + def test_flags_default0_display(self): + """is set to zero, and SRCALPH is not set by default even when the display is initialized.""" + pygame.display.set_mode((320, 200)) + try: + surf = pygame.Surface((70, 70)) + self.assertEqual(surf.get_flags() & SRCALPHA, 0) + finally: + pygame.display.quit() + + def test_masks(self): + def make_surf(bpp, flags, masks): + pygame.Surface((10, 10), flags, bpp, masks) + + # With some masks SDL_CreateRGBSurface does not work properly. + masks = (0xFF000000, 0xFF0000, 0xFF00, 0) + self.assertEqual(make_surf(32, 0, masks), None) + # For 24 and 32 bit surfaces Pygame assumes no losses. + masks = (0x7F0000, 0xFF00, 0xFF, 0) + self.assertRaises(ValueError, make_surf, 24, 0, masks) + self.assertRaises(ValueError, make_surf, 32, 0, masks) + # What contiguous bits in a mask. + masks = (0x6F0000, 0xFF00, 0xFF, 0) + self.assertRaises(ValueError, make_surf, 32, 0, masks) + + def test_get_bounding_rect(self): + surf = pygame.Surface((70, 70), SRCALPHA, 32) + surf.fill((0, 0, 0, 0)) + bound_rect = surf.get_bounding_rect() + self.assertEqual(bound_rect.width, 0) + self.assertEqual(bound_rect.height, 0) + surf.set_at((30, 30), (255, 255, 255, 1)) + bound_rect = surf.get_bounding_rect() + self.assertEqual(bound_rect.left, 30) + self.assertEqual(bound_rect.top, 30) + self.assertEqual(bound_rect.width, 1) + self.assertEqual(bound_rect.height, 1) + surf.set_at((29, 29), (255, 255, 255, 1)) + bound_rect = surf.get_bounding_rect() + self.assertEqual(bound_rect.left, 29) + self.assertEqual(bound_rect.top, 29) + self.assertEqual(bound_rect.width, 2) + self.assertEqual(bound_rect.height, 2) + + surf = pygame.Surface((70, 70), 0, 24) + surf.fill((0, 0, 0)) + bound_rect = surf.get_bounding_rect() + self.assertEqual(bound_rect.width, surf.get_width()) + self.assertEqual(bound_rect.height, surf.get_height()) + + surf.set_colorkey((0, 0, 0)) + bound_rect = surf.get_bounding_rect() + self.assertEqual(bound_rect.width, 0) + self.assertEqual(bound_rect.height, 0) + surf.set_at((30, 30), (255, 255, 255)) + bound_rect = surf.get_bounding_rect() + self.assertEqual(bound_rect.left, 30) + self.assertEqual(bound_rect.top, 30) + self.assertEqual(bound_rect.width, 1) + self.assertEqual(bound_rect.height, 1) + surf.set_at((60, 60), (255, 255, 255)) + bound_rect = surf.get_bounding_rect() + self.assertEqual(bound_rect.left, 30) + self.assertEqual(bound_rect.top, 30) + self.assertEqual(bound_rect.width, 31) + self.assertEqual(bound_rect.height, 31) + + # Issue #180 + pygame.display.init() + try: + surf = pygame.Surface((4, 1), 0, 8) + surf.fill((255, 255, 255)) + surf.get_bounding_rect() # Segfault. + finally: + pygame.display.quit() + + def test_copy(self): + """Ensure a surface can be copied.""" + color = (25, 25, 25, 25) + s1 = pygame.Surface((32, 32), pygame.SRCALPHA, 32) + s1.fill(color) + + s2 = s1.copy() + + s1rect = s1.get_rect() + s2rect = s2.get_rect() + + self.assertEqual(s1rect.size, s2rect.size) + self.assertEqual(s2.get_at((10, 10)), color) + + def test_fill(self): + """Ensure a surface can be filled.""" + color = (25, 25, 25, 25) + fill_rect = pygame.Rect(0, 0, 16, 16) + s1 = pygame.Surface((32, 32), pygame.SRCALPHA, 32) + s1.fill(color, fill_rect) + + for pt in test_utils.rect_area_pts(fill_rect): + self.assertEqual(s1.get_at(pt), color) + + for pt in test_utils.rect_outer_bounds(fill_rect): + self.assertNotEqual(s1.get_at(pt), color) + + def test_fill_rle(self): + """Test RLEACCEL flag with fill()""" + color = (250, 25, 25, 255) + + surf = pygame.Surface((32, 32)) + blit_surf = pygame.Surface((32, 32)) + + blit_surf.set_colorkey((255, 0, 255), pygame.RLEACCEL) + self.assertTrue(blit_surf.get_flags() & pygame.RLEACCELOK) + surf.blit(blit_surf, (0, 0)) + blit_surf.fill(color) + self.assertEqual( + blit_surf.mustlock(), (blit_surf.get_flags() & pygame.RLEACCEL) != 0 + ) + self.assertTrue(blit_surf.get_flags() & pygame.RLEACCEL) + + def test_mustlock_rle(self): + """Test RLEACCEL flag with mustlock()""" + surf = pygame.Surface((100, 100)) + blit_surf = pygame.Surface((100, 100)) + blit_surf.set_colorkey((0, 0, 255), pygame.RLEACCEL) + self.assertTrue(blit_surf.get_flags() & pygame.RLEACCELOK) + surf.blit(blit_surf, (0, 0)) + self.assertTrue(blit_surf.get_flags() & pygame.RLEACCEL) + self.assertTrue(blit_surf.mustlock()) + + def test_mustlock_surf_alpha_rle(self): + """Test RLEACCEL flag with mustlock() on a surface + with per pixel alpha - new feature in SDL2""" + surf = pygame.Surface((100, 100)) + blit_surf = pygame.Surface((100, 100), depth=32, flags=pygame.SRCALPHA) + blit_surf.set_colorkey((192, 191, 192, 255), pygame.RLEACCEL) + self.assertTrue(blit_surf.get_flags() & pygame.RLEACCELOK) + surf.blit(blit_surf, (0, 0)) + self.assertTrue(blit_surf.get_flags() & pygame.RLEACCEL) + self.assertTrue(blit_surf.get_flags() & pygame.SRCALPHA) + self.assertTrue(blit_surf.mustlock()) + + def test_copy_rle(self): + """Test copying a surface set to use run length encoding""" + s1 = pygame.Surface((32, 32), 24) + s1.set_colorkey((255, 0, 255), pygame.RLEACCEL) + self.assertTrue(s1.get_flags() & pygame.RLEACCELOK) + + newsurf = s1.copy() + self.assertTrue(s1.get_flags() & pygame.RLEACCELOK) + self.assertTrue(newsurf.get_flags() & pygame.RLEACCELOK) + + def test_subsurface_rle(self): + """Ensure an RLE sub-surface works independently of its parent.""" + color = (250, 25, 25, 255) + color2 = (200, 200, 250, 255) + sub_rect = pygame.Rect(16, 16, 16, 16) + s0 = pygame.Surface((32, 32), 24) + s1 = pygame.Surface((32, 32), 24) + s1.set_colorkey((255, 0, 255), pygame.RLEACCEL) + s1.fill(color) + s2 = s1.subsurface(sub_rect) + s2.fill(color2) + s0.blit(s1, (0, 0)) + self.assertTrue(s1.get_flags() & pygame.RLEACCEL) + self.assertTrue(not s2.get_flags() & pygame.RLEACCEL) + + def test_subsurface_rle2(self): + """Ensure an RLE sub-surface works independently of its parent.""" + color = (250, 25, 25, 255) + color2 = (200, 200, 250, 255) + sub_rect = pygame.Rect(16, 16, 16, 16) + + s0 = pygame.Surface((32, 32), 24) + s1 = pygame.Surface((32, 32), 24) + s1.set_colorkey((255, 0, 255), pygame.RLEACCEL) + s1.fill(color) + s2 = s1.subsurface(sub_rect) + s2.fill(color2) + s0.blit(s2, (0, 0)) + self.assertTrue(s1.get_flags() & pygame.RLEACCELOK) + self.assertTrue(not s2.get_flags() & pygame.RLEACCELOK) + + def test_solarwolf_rle_usage(self): + """Test for error/crash when calling set_colorkey() followed + by convert twice in succession. Code originally taken + from solarwolf.""" + + def optimize(img): + clear = img.get_colorkey() + img.set_colorkey(clear, RLEACCEL) + self.assertEqual(img.get_colorkey(), clear) + return img.convert() + + pygame.display.init() + try: + pygame.display.set_mode((640, 480)) + + image = pygame.image.load(example_path(os.path.join("data", "alien1.png"))) + image = image.convert() + orig_colorkey = image.get_colorkey() + + image = optimize(image) + image = optimize(image) + self.assertTrue(image.get_flags() & pygame.RLEACCELOK) + self.assertTrue(not image.get_flags() & pygame.RLEACCEL) + self.assertEqual(image.get_colorkey(), orig_colorkey) + self.assertTrue(isinstance(image, pygame.Surface)) + finally: + pygame.display.quit() + + def test_solarwolf_rle_usage_2(self): + """Test for RLE status after setting alpha""" + + pygame.display.init() + try: + pygame.display.set_mode((640, 480), depth=32) + blit_to_surf = pygame.Surface((100, 100)) + + image = pygame.image.load(example_path(os.path.join("data", "alien1.png"))) + image = image.convert() + orig_colorkey = image.get_colorkey() + + # set the colorkey with RLEACCEL, should add the RLEACCELOK flag + image.set_colorkey(orig_colorkey, RLEACCEL) + self.assertTrue(image.get_flags() & pygame.RLEACCELOK) + self.assertTrue(not image.get_flags() & pygame.RLEACCEL) + + # now blit the surface - should add the RLEACCEL flag + blit_to_surf.blit(image, (0, 0)) + self.assertTrue(image.get_flags() & pygame.RLEACCELOK) + self.assertTrue(image.get_flags() & pygame.RLEACCEL) + + # Now set the alpha, without RLE acceleration - should strip all + # RLE flags + image.set_alpha(90) + self.assertTrue(not image.get_flags() & pygame.RLEACCELOK) + self.assertTrue(not image.get_flags() & pygame.RLEACCEL) + + finally: + pygame.display.quit() + + def test_set_alpha__set_colorkey_rle(self): + pygame.display.init() + try: + pygame.display.set_mode((640, 480)) + blit_to_surf = pygame.Surface((80, 71)) + blit_to_surf.fill((255, 255, 255)) + + image = pygame.image.load(example_path(os.path.join("data", "alien1.png"))) + image = image.convert() + orig_colorkey = image.get_colorkey() + + # Add the RLE flag while setting alpha for the whole surface + image.set_alpha(90, RLEACCEL) + blit_to_surf.blit(image, (0, 0)) + sample_pixel_rle = blit_to_surf.get_at((50, 50)) + + # Now reset the colorkey to the original value with RLE + self.assertEqual(image.get_colorkey(), orig_colorkey) + image.set_colorkey(orig_colorkey, RLEACCEL) + blit_to_surf.fill((255, 255, 255)) + blit_to_surf.blit(image, (0, 0)) + sample_pixel_no_rle = blit_to_surf.get_at((50, 50)) + + self.assertAlmostEqual(sample_pixel_rle.r, sample_pixel_no_rle.r, delta=2) + self.assertAlmostEqual(sample_pixel_rle.g, sample_pixel_no_rle.g, delta=2) + self.assertAlmostEqual(sample_pixel_rle.b, sample_pixel_no_rle.b, delta=2) + + finally: + pygame.display.quit() + + def test_fill_negative_coordinates(self): + # negative coordinates should be clipped by fill, and not draw outside the surface. + color = (25, 25, 25, 25) + color2 = (20, 20, 20, 25) + fill_rect = pygame.Rect(-10, -10, 16, 16) + + s1 = pygame.Surface((32, 32), pygame.SRCALPHA, 32) + r1 = s1.fill(color, fill_rect) + c = s1.get_at((0, 0)) + self.assertEqual(c, color) + + # make subsurface in the middle to test it doesn't over write. + s2 = s1.subsurface((5, 5, 5, 5)) + r2 = s2.fill(color2, (-3, -3, 5, 5)) + c2 = s1.get_at((4, 4)) + self.assertEqual(c, color) + + # rect returns the area we actually fill. + r3 = s2.fill(color2, (-30, -30, 5, 5)) + # since we are using negative coords, it should be an zero sized rect. + self.assertEqual(tuple(r3), (0, 0, 0, 0)) + + def test_fill_keyword_args(self): + """Ensure fill() accepts keyword arguments.""" + color = (1, 2, 3, 255) + area = (1, 1, 2, 2) + s1 = pygame.Surface((4, 4), 0, 32) + s1.fill(special_flags=pygame.BLEND_ADD, color=color, rect=area) + + self.assertEqual(s1.get_at((0, 0)), (0, 0, 0, 255)) + self.assertEqual(s1.get_at((1, 1)), color) + + ######################################################################## + + def test_get_alpha(self): + """Ensure a surface's alpha value can be retrieved.""" + s1 = pygame.Surface((32, 32), pygame.SRCALPHA, 32) + + self.assertEqual(s1.get_alpha(), 255) + + for alpha in (0, 32, 127, 255): + s1.set_alpha(alpha) + for t in range(4): + s1.set_alpha(s1.get_alpha()) + + self.assertEqual(s1.get_alpha(), alpha) + + ######################################################################## + + def test_get_bytesize(self): + """Ensure a surface's bit and byte sizes can be retrieved.""" + pygame.display.init() + try: + depth = 32 + depth_bytes = 4 + s1 = pygame.Surface((32, 32), pygame.SRCALPHA, depth) + + self.assertEqual(s1.get_bytesize(), depth_bytes) + self.assertEqual(s1.get_bitsize(), depth) + + depth = 15 + depth_bytes = 2 + s1 = pygame.Surface((32, 32), 0, depth) + + self.assertEqual(s1.get_bytesize(), depth_bytes) + self.assertEqual(s1.get_bitsize(), depth) + + depth = 12 + depth_bytes = 2 + s1 = pygame.Surface((32, 32), 0, depth) + + self.assertEqual(s1.get_bytesize(), depth_bytes) + self.assertEqual(s1.get_bitsize(), depth) + + with self.assertRaises(pygame.error): + surface = pygame.display.set_mode() + pygame.display.quit() + surface.get_bytesize() + finally: + pygame.display.quit() + + ######################################################################## + + def test_get_flags(self): + """Ensure a surface's flags can be retrieved.""" + s1 = pygame.Surface((32, 32), pygame.SRCALPHA, 32) + + self.assertEqual(s1.get_flags(), pygame.SRCALPHA) + + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") == "dummy", + 'requires a non-"dummy" SDL_VIDEODRIVER', + ) + def test_get_flags__display_surf(self): + pygame.display.init() + try: + # FULLSCREEN + screen_surf = pygame.display.set_mode((600, 400), flags=0) + self.assertFalse(screen_surf.get_flags() & pygame.FULLSCREEN) + + screen_surf = pygame.display.set_mode((600, 400), flags=pygame.FULLSCREEN) + self.assertTrue(screen_surf.get_flags() & pygame.FULLSCREEN) + + # NOFRAME + screen_surf = pygame.display.set_mode((600, 400), flags=0) + self.assertFalse(screen_surf.get_flags() & pygame.NOFRAME) + + screen_surf = pygame.display.set_mode((600, 400), flags=pygame.NOFRAME) + self.assertTrue(screen_surf.get_flags() & pygame.NOFRAME) + + # RESIZABLE + screen_surf = pygame.display.set_mode((600, 400), flags=0) + self.assertFalse(screen_surf.get_flags() & pygame.RESIZABLE) + + screen_surf = pygame.display.set_mode((600, 400), flags=pygame.RESIZABLE) + self.assertTrue(screen_surf.get_flags() & pygame.RESIZABLE) + + # OPENGL + screen_surf = pygame.display.set_mode((600, 400), flags=0) + # it can have an OPENGL flag by default on Macos? + if not (screen_surf.get_flags() & pygame.OPENGL): + self.assertFalse(screen_surf.get_flags() & pygame.OPENGL) + + try: + pygame.display.set_mode((200, 200), pygame.OPENGL, 32) + except pygame.error: + pass # If we can't create OPENGL surface don't try this test + else: + self.assertTrue(screen_surf.get_flags() & pygame.OPENGL) + finally: + pygame.display.quit() + + ######################################################################## + + def test_get_parent(self): + """Ensure a surface's parent can be retrieved.""" + pygame.display.init() + try: + parent = pygame.Surface((16, 16)) + child = parent.subsurface((0, 0, 5, 5)) + + self.assertIs(child.get_parent(), parent) + + with self.assertRaises(pygame.error): + surface = pygame.display.set_mode() + pygame.display.quit() + surface.get_parent() + finally: + pygame.display.quit() + + ######################################################################## + + def test_get_rect(self): + """Ensure a surface's rect can be retrieved.""" + size = (16, 16) + surf = pygame.Surface(size) + rect = surf.get_rect() + + self.assertEqual(rect.size, size) + + ######################################################################## + + def test_get_width__size_and_height(self): + """Ensure a surface's size, width and height can be retrieved.""" + for w in range(0, 255, 32): + for h in range(0, 127, 15): + s = pygame.Surface((w, h)) + self.assertEqual(s.get_width(), w) + self.assertEqual(s.get_height(), h) + self.assertEqual(s.get_size(), (w, h)) + + def test_get_view(self): + """Ensure a buffer view of the surface's pixels can be retrieved.""" + # Check that BufferProxys are returned when array depth is supported, + # ValueErrors returned otherwise. + Error = ValueError + s = pygame.Surface((5, 7), 0, 8) + v2 = s.get_view("2") + + self.assertRaises(Error, s.get_view, "0") + self.assertRaises(Error, s.get_view, "1") + self.assertIsInstance(v2, BufferProxy) + self.assertRaises(Error, s.get_view, "3") + + s = pygame.Surface((8, 7), 0, 8) + length = s.get_bytesize() * s.get_width() * s.get_height() + v0 = s.get_view("0") + v1 = s.get_view("1") + + self.assertIsInstance(v0, BufferProxy) + self.assertEqual(v0.length, length) + self.assertIsInstance(v1, BufferProxy) + self.assertEqual(v1.length, length) + + s = pygame.Surface((5, 7), 0, 16) + v2 = s.get_view("2") + + self.assertRaises(Error, s.get_view, "0") + self.assertRaises(Error, s.get_view, "1") + self.assertIsInstance(v2, BufferProxy) + self.assertRaises(Error, s.get_view, "3") + + s = pygame.Surface((8, 7), 0, 16) + length = s.get_bytesize() * s.get_width() * s.get_height() + v0 = s.get_view("0") + v1 = s.get_view("1") + + self.assertIsInstance(v0, BufferProxy) + self.assertEqual(v0.length, length) + self.assertIsInstance(v1, BufferProxy) + self.assertEqual(v1.length, length) + + s = pygame.Surface((5, 7), pygame.SRCALPHA, 16) + v2 = s.get_view("2") + + self.assertIsInstance(v2, BufferProxy) + self.assertRaises(Error, s.get_view, "3") + + s = pygame.Surface((5, 7), 0, 24) + v2 = s.get_view("2") + v3 = s.get_view("3") + + self.assertRaises(Error, s.get_view, "0") + self.assertRaises(Error, s.get_view, "1") + self.assertIsInstance(v2, BufferProxy) + self.assertIsInstance(v3, BufferProxy) + + s = pygame.Surface((8, 7), 0, 24) + length = s.get_bytesize() * s.get_width() * s.get_height() + v0 = s.get_view("0") + v1 = s.get_view("1") + + self.assertIsInstance(v0, BufferProxy) + self.assertEqual(v0.length, length) + self.assertIsInstance(v1, BufferProxy) + self.assertEqual(v1.length, length) + + s = pygame.Surface((5, 7), 0, 32) + length = s.get_bytesize() * s.get_width() * s.get_height() + v0 = s.get_view("0") + v1 = s.get_view("1") + v2 = s.get_view("2") + v3 = s.get_view("3") + + self.assertIsInstance(v0, BufferProxy) + self.assertEqual(v0.length, length) + self.assertIsInstance(v1, BufferProxy) + self.assertEqual(v1.length, length) + self.assertIsInstance(v2, BufferProxy) + self.assertIsInstance(v3, BufferProxy) + + s2 = s.subsurface((0, 0, 4, 7)) + + self.assertRaises(Error, s2.get_view, "0") + self.assertRaises(Error, s2.get_view, "1") + + s2 = None + s = pygame.Surface((5, 7), pygame.SRCALPHA, 32) + + for kind in ("2", "3", "a", "A", "r", "R", "g", "G", "b", "B"): + self.assertIsInstance(s.get_view(kind), BufferProxy) + + # Check default argument value: '2' + s = pygame.Surface((2, 4), 0, 32) + v = s.get_view() + if not IS_PYPY: + ai = ArrayInterface(v) + self.assertEqual(ai.nd, 2) + + # Check locking. + s = pygame.Surface((2, 4), 0, 32) + + self.assertFalse(s.get_locked()) + + v = s.get_view("2") + + self.assertFalse(s.get_locked()) + + c = v.__array_interface__ + + self.assertTrue(s.get_locked()) + + c = None + gc.collect() + + self.assertTrue(s.get_locked()) + + v = None + gc.collect() + + self.assertFalse(s.get_locked()) + + # Check invalid view kind values. + s = pygame.Surface((2, 4), pygame.SRCALPHA, 32) + self.assertRaises(TypeError, s.get_view, "") + self.assertRaises(TypeError, s.get_view, "9") + self.assertRaises(TypeError, s.get_view, "RGBA") + self.assertRaises(TypeError, s.get_view, 2) + + # Both unicode and bytes strings are allowed for kind. + s = pygame.Surface((2, 4), 0, 32) + s.get_view("2") + s.get_view(b"2") + + # Garbage collection + s = pygame.Surface((2, 4), 0, 32) + weak_s = weakref.ref(s) + v = s.get_view("3") + weak_v = weakref.ref(v) + gc.collect() + self.assertTrue(weak_s() is s) + self.assertTrue(weak_v() is v) + del v + gc.collect() + self.assertTrue(weak_s() is s) + self.assertTrue(weak_v() is None) + del s + gc.collect() + self.assertTrue(weak_s() is None) + + def test_get_buffer(self): + # Check that get_buffer works for all pixel sizes and for a subsurface. + + # Check for all pixel sizes + for bitsize in [8, 16, 24, 32]: + s = pygame.Surface((5, 7), 0, bitsize) + length = s.get_pitch() * s.get_height() + v = s.get_buffer() + + self.assertIsInstance(v, BufferProxy) + self.assertEqual(v.length, length) + self.assertEqual(repr(v), f"") + + # Check for a subsurface (not contiguous) + s = pygame.Surface((7, 10), 0, 32) + s2 = s.subsurface((1, 2, 5, 7)) + length = s2.get_pitch() * s2.get_height() + v = s2.get_buffer() + + self.assertIsInstance(v, BufferProxy) + self.assertEqual(v.length, length) + + # Check locking. + s = pygame.Surface((2, 4), 0, 32) + v = s.get_buffer() + self.assertTrue(s.get_locked()) + v = None + gc.collect() + self.assertFalse(s.get_locked()) + + OLDBUF = hasattr(pygame.bufferproxy, "get_segcount") + + @unittest.skipIf(not OLDBUF, "old buffer not available") + def test_get_buffer_oldbuf(self): + from pygame.bufferproxy import get_segcount, get_write_buffer + + s = pygame.Surface((2, 4), pygame.SRCALPHA, 32) + v = s.get_buffer() + segcount, buflen = get_segcount(v) + self.assertEqual(segcount, 1) + self.assertEqual(buflen, s.get_pitch() * s.get_height()) + seglen, segaddr = get_write_buffer(v, 0) + self.assertEqual(segaddr, s._pixels_address) + self.assertEqual(seglen, buflen) + + @unittest.skipIf(not OLDBUF, "old buffer not available") + def test_get_view_oldbuf(self): + from pygame.bufferproxy import get_segcount, get_write_buffer + + s = pygame.Surface((2, 4), pygame.SRCALPHA, 32) + v = s.get_view("1") + segcount, buflen = get_segcount(v) + self.assertEqual(segcount, 8) + self.assertEqual(buflen, s.get_pitch() * s.get_height()) + seglen, segaddr = get_write_buffer(v, 7) + self.assertEqual(segaddr, s._pixels_address + s.get_bytesize() * 7) + self.assertEqual(seglen, s.get_bytesize()) + + def test_set_colorkey(self): + # __doc__ (as of 2008-06-25) for pygame.surface.Surface.set_colorkey: + + # Surface.set_colorkey(Color, flags=0): return None + # Surface.set_colorkey(None): return None + # Set the transparent colorkey + + s = pygame.Surface((16, 16), pygame.SRCALPHA, 32) + + colorkeys = ((20, 189, 20, 255), (128, 50, 50, 255), (23, 21, 255, 255)) + + for colorkey in colorkeys: + s.set_colorkey(colorkey) + + for t in range(4): + s.set_colorkey(s.get_colorkey()) + + self.assertEqual(s.get_colorkey(), colorkey) + + def test_set_masks(self): + s = pygame.Surface((32, 32)) + r, g, b, a = s.get_masks() + self.assertRaises(TypeError, s.set_masks, (b, g, r, a)) + + def test_set_shifts(self): + s = pygame.Surface((32, 32)) + r, g, b, a = s.get_shifts() + self.assertRaises(TypeError, s.set_shifts, (b, g, r, a)) + + def test_blit_keyword_args(self): + color = (1, 2, 3, 255) + s1 = pygame.Surface((4, 4), 0, 32) + s2 = pygame.Surface((2, 2), 0, 32) + s2.fill((1, 2, 3)) + s1.blit(special_flags=BLEND_ADD, source=s2, dest=(1, 1), area=s2.get_rect()) + self.assertEqual(s1.get_at((0, 0)), (0, 0, 0, 255)) + self.assertEqual(s1.get_at((1, 1)), color) + + def test_blit_big_rects(self): + """SDL2 can have more than 16 bits for x, y, width, height.""" + big_surf = pygame.Surface((100, 68000), 0, 32) + big_surf_color = (255, 0, 0) + big_surf.fill(big_surf_color) + + background = pygame.Surface((500, 500), 0, 32) + background_color = (0, 255, 0) + background.fill(background_color) + + # copy parts of the big_surf using more than 16bit parts. + background.blit(big_surf, (100, 100), area=(0, 16000, 100, 100)) + background.blit(big_surf, (200, 200), area=(0, 32000, 100, 100)) + background.blit(big_surf, (300, 300), area=(0, 66000, 100, 100)) + + # check that all three areas are drawn. + self.assertEqual(background.get_at((101, 101)), big_surf_color) + self.assertEqual(background.get_at((201, 201)), big_surf_color) + self.assertEqual(background.get_at((301, 301)), big_surf_color) + + # areas outside the 3 blitted areas not covered by those blits. + self.assertEqual(background.get_at((400, 301)), background_color) + self.assertEqual(background.get_at((400, 201)), background_color) + self.assertEqual(background.get_at((100, 201)), background_color) + self.assertEqual(background.get_at((99, 99)), background_color) + self.assertEqual(background.get_at((450, 450)), background_color) + + +class TestSurfaceBlit(unittest.TestCase): + """Tests basic blitting functionality and options.""" + + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.blit: + + # Surface.blit(source, dest, area=None, special_flags = 0): return Rect + # draw one image onto another + # + # Draws a source Surface onto this Surface. The draw can be positioned + # with the dest argument. Dest can either be pair of coordinates + # representing the upper left corner of the source. A Rect can also be + # passed as the destination and the topleft corner of the rectangle + # will be used as the position for the blit. The size of the + # destination rectangle does not effect the blit. + # + # An optional area rectangle can be passed as well. This represents a + # smaller portion of the source Surface to draw. + # + # An optional special flags is for passing in new in 1.8.0: BLEND_ADD, + # BLEND_SUB, BLEND_MULT, BLEND_MIN, BLEND_MAX new in 1.8.1: + # BLEND_RGBA_ADD, BLEND_RGBA_SUB, BLEND_RGBA_MULT, BLEND_RGBA_MIN, + # BLEND_RGBA_MAX BLEND_RGB_ADD, BLEND_RGB_SUB, BLEND_RGB_MULT, + # BLEND_RGB_MIN, BLEND_RGB_MAX With other special blitting flags + # perhaps added in the future. + # + # The return rectangle is the area of the affected pixels, excluding + # any pixels outside the destination Surface, or outside the clipping + # area. + # + # Pixel alphas will be ignored when blitting to an 8 bit Surface. + # special_flags new in pygame 1.8. + + def setUp(self): + """Resets starting surfaces.""" + self.src_surface = pygame.Surface((256, 256), 32) + self.src_surface.fill(pygame.Color(255, 255, 255)) + self.dst_surface = pygame.Surface((64, 64), 32) + self.dst_surface.fill(pygame.Color(0, 0, 0)) + + def test_blit_overflow_coord(self): + """Full coverage w/ overflow, specified with Coordinate""" + result = self.dst_surface.blit(self.src_surface, (0, 0)) + self.assertIsInstance(result, pygame.Rect) + self.assertEqual(result.size, (64, 64)) + for k in [(x, x) for x in range(64)]: + self.assertEqual(self.dst_surface.get_at(k), (255, 255, 255)) + + def test_blit_overflow_rect(self): + """Full coverage w/ overflow, specified with a Rect""" + result = self.dst_surface.blit(self.src_surface, pygame.Rect(-1, -1, 300, 300)) + self.assertIsInstance(result, pygame.Rect) + self.assertEqual(result.size, (64, 64)) + for k in [(x, x) for x in range(64)]: + self.assertEqual(self.dst_surface.get_at(k), (255, 255, 255)) + + def test_blit_overflow_nonorigin(self): + """Test Rectangle Dest, with overflow but with starting rect with top-left at (1,1)""" + result = self.dst_surface.blit(self.src_surface, dest=pygame.Rect((1, 1, 1, 1))) + self.assertIsInstance(result, pygame.Rect) + self.assertEqual(result.size, (63, 63)) + self.assertEqual(self.dst_surface.get_at((0, 0)), (0, 0, 0)) + self.assertEqual(self.dst_surface.get_at((63, 0)), (0, 0, 0)) + self.assertEqual(self.dst_surface.get_at((0, 63)), (0, 0, 0)) + self.assertEqual(self.dst_surface.get_at((1, 1)), (255, 255, 255)) + self.assertEqual(self.dst_surface.get_at((63, 63)), (255, 255, 255)) + + def test_blit_area_contraint(self): + """Testing area constraint""" + result = self.dst_surface.blit( + self.src_surface, + dest=pygame.Rect((1, 1, 1, 1)), + area=pygame.Rect((2, 2, 2, 2)), + ) + self.assertIsInstance(result, pygame.Rect) + self.assertEqual(result.size, (2, 2)) + self.assertEqual(self.dst_surface.get_at((0, 0)), (0, 0, 0)) # Corners + self.assertEqual(self.dst_surface.get_at((63, 0)), (0, 0, 0)) + self.assertEqual(self.dst_surface.get_at((0, 63)), (0, 0, 0)) + self.assertEqual(self.dst_surface.get_at((63, 63)), (0, 0, 0)) + self.assertEqual( + self.dst_surface.get_at((1, 1)), (255, 255, 255) + ) # Blitted Area + self.assertEqual(self.dst_surface.get_at((2, 2)), (255, 255, 255)) + self.assertEqual(self.dst_surface.get_at((3, 3)), (0, 0, 0)) + # Should stop short of filling in (3,3) + + def test_blit_zero_overlap(self): + """Testing zero-overlap condition.""" + result = self.dst_surface.blit( + self.src_surface, + dest=pygame.Rect((-256, -256, 1, 1)), + area=pygame.Rect((2, 2, 256, 256)), + ) + self.assertIsInstance(result, pygame.Rect) + self.assertEqual(result.size, (0, 0)) # No blitting expected + for k in [(x, x) for x in range(64)]: + self.assertEqual(self.dst_surface.get_at(k), (0, 0, 0)) # Diagonal + self.assertEqual( + self.dst_surface.get_at((63, 0)), (0, 0, 0) + ) # Remaining corners + self.assertEqual(self.dst_surface.get_at((0, 63)), (0, 0, 0)) + + def test_blit__SRCALPHA_opaque_source(self): + src = pygame.Surface((256, 256), SRCALPHA, 32) + dst = src.copy() + + for i, j in test_utils.rect_area_pts(src.get_rect()): + dst.set_at((i, j), (i, 0, 0, j)) + src.set_at((i, j), (0, i, 0, 255)) + + dst.blit(src, (0, 0)) + + for pt in test_utils.rect_area_pts(src.get_rect()): + self.assertEqual(dst.get_at(pt)[1], src.get_at(pt)[1]) + + def test_blit__blit_to_self(self): + """Test that blit operation works on self, alpha value is + correct, and that no RGB distortion occurs.""" + test_surface = pygame.Surface((128, 128), SRCALPHA, 32) + area = test_surface.get_rect() + + for pt, test_color in test_utils.gradient(area.width, area.height): + test_surface.set_at(pt, test_color) + + reference_surface = test_surface.copy() + + test_surface.blit(test_surface, (0, 0)) + + for x in range(area.width): + for y in range(area.height): + (r, g, b, a) = reference_color = reference_surface.get_at((x, y)) + expected_color = (r, g, b, (a + (a * ((256 - a) // 256)))) + self.assertEqual(reference_color, expected_color) + + self.assertEqual(reference_surface.get_rect(), test_surface.get_rect()) + + def test_blit__SRCALPHA_to_SRCALPHA_non_zero(self): + """Tests blitting a nonzero alpha surface to another nonzero alpha surface + both straight alpha compositing method. Test is fuzzy (+/- 1/256) to account for + different implementations in SDL1 and SDL2. + """ + + size = (32, 32) + + def check_color_diff(color1, color2): + """Returns True if two colors are within (1, 1, 1, 1) of each other.""" + for val in color1 - color2: + if abs(val) > 1: + return False + return True + + def high_a_onto_low(high, low): + """Tests straight alpha case. Source is low alpha, destination is high alpha""" + high_alpha_surface = pygame.Surface(size, pygame.SRCALPHA, 32) + low_alpha_surface = high_alpha_surface.copy() + high_alpha_color = Color( + (high, high, low, high) + ) # Injecting some RGB variance. + low_alpha_color = Color((high, low, low, low)) + high_alpha_surface.fill(high_alpha_color) + low_alpha_surface.fill(low_alpha_color) + + high_alpha_surface.blit(low_alpha_surface, (0, 0)) + + expected_color = low_alpha_color + Color( + tuple( + ((x * (255 - low_alpha_color.a)) // 255) for x in high_alpha_color + ) + ) + self.assertTrue( + check_color_diff(high_alpha_surface.get_at((0, 0)), expected_color) + ) + + def low_a_onto_high(high, low): + """Tests straight alpha case. Source is high alpha, destination is low alpha""" + high_alpha_surface = pygame.Surface(size, pygame.SRCALPHA, 32) + low_alpha_surface = high_alpha_surface.copy() + high_alpha_color = Color( + (high, high, low, high) + ) # Injecting some RGB variance. + low_alpha_color = Color((high, low, low, low)) + high_alpha_surface.fill(high_alpha_color) + low_alpha_surface.fill(low_alpha_color) + + low_alpha_surface.blit(high_alpha_surface, (0, 0)) + + expected_color = high_alpha_color + Color( + tuple( + ((x * (255 - high_alpha_color.a)) // 255) for x in low_alpha_color + ) + ) + self.assertTrue( + check_color_diff(low_alpha_surface.get_at((0, 0)), expected_color) + ) + + for low_a in range(0, 128): + for high_a in range(128, 256): + high_a_onto_low(high_a, low_a) + low_a_onto_high(high_a, low_a) + + def test_blit__SRCALPHA32_to_8(self): + # Bug: fatal + # SDL_DisplayConvert segfaults when video is uninitialized. + target = pygame.Surface((11, 8), 0, 8) + test_color = target.get_palette_at(2) + source = pygame.Surface((1, 1), pygame.SRCALPHA, 32) + source.set_at((0, 0), test_color) + target.blit(source, (0, 0)) + + +class GeneralSurfaceTests(unittest.TestCase): + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") == "dummy", + 'requires a non-"dummy" SDL_VIDEODRIVER', + ) + def test_image_convert_bug_131(self): + # bug #131: Unable to Surface.convert(32) some 1-bit images. + # https://github.com/pygame/pygame/issues/131 + + pygame.display.init() + try: + pygame.display.set_mode((640, 480)) + + im = pygame.image.load(example_path(os.path.join("data", "city.png"))) + im2 = pygame.image.load(example_path(os.path.join("data", "brick.png"))) + + self.assertEqual(im.get_palette(), ((0, 0, 0, 255), (255, 255, 255, 255))) + self.assertEqual(im2.get_palette(), ((0, 0, 0, 255), (0, 0, 0, 255))) + + self.assertEqual(repr(im.convert(32)), "") + self.assertEqual(repr(im2.convert(32)), "") + + # Ensure a palette format to palette format works. + im3 = im.convert(8) + self.assertEqual(repr(im3), "") + self.assertEqual(im3.get_palette(), im.get_palette()) + + finally: + pygame.display.quit() + + def test_convert_init(self): + """Ensure initialization exceptions are raised + for surf.convert().""" + pygame.display.quit() + surf = pygame.Surface((1, 1)) + + self.assertRaisesRegex(pygame.error, "display initialized", surf.convert) + + pygame.display.init() + try: + if os.environ.get("SDL_VIDEODRIVER") != "dummy": + try: + surf.convert(32) + surf.convert(pygame.Surface((1, 1))) + except pygame.error: + self.fail("convert() should not raise an exception here.") + + self.assertRaisesRegex(pygame.error, "No video mode", surf.convert) + + pygame.display.set_mode((640, 480)) + try: + surf.convert() + except pygame.error: + self.fail("convert() should not raise an exception here.") + finally: + pygame.display.quit() + + def test_convert_alpha_init(self): + """Ensure initialization exceptions are raised + for surf.convert_alpha().""" + pygame.display.quit() + surf = pygame.Surface((1, 1)) + + self.assertRaisesRegex(pygame.error, "display initialized", surf.convert_alpha) + + pygame.display.init() + try: + self.assertRaisesRegex(pygame.error, "No video mode", surf.convert_alpha) + + pygame.display.set_mode((640, 480)) + try: + surf.convert_alpha() + except pygame.error: + self.fail("convert_alpha() should not raise an exception here.") + finally: + pygame.display.quit() + + def test_convert_alpha_SRCALPHA(self): + """Ensure that the surface returned by surf.convert_alpha() + has alpha blending enabled""" + pygame.display.init() + try: + pygame.display.set_mode((640, 480)) + + s1 = pygame.Surface((100, 100), 0, 32) + # s2=pygame.Surface((100,100), pygame.SRCALPHA, 32) + s1_alpha = s1.convert_alpha() + self.assertEqual(s1_alpha.get_flags() & SRCALPHA, SRCALPHA) + self.assertEqual(s1_alpha.get_alpha(), 255) + finally: + pygame.display.quit() + + def test_src_alpha_issue_1289(self): + """blit should be white.""" + surf1 = pygame.Surface((1, 1), pygame.SRCALPHA, 32) + surf1.fill((255, 255, 255, 100)) + + surf2 = pygame.Surface((1, 1), pygame.SRCALPHA, 32) + self.assertEqual(surf2.get_at((0, 0)), (0, 0, 0, 0)) + surf2.blit(surf1, (0, 0)) + + self.assertEqual(surf1.get_at((0, 0)), (255, 255, 255, 100)) + self.assertEqual(surf2.get_at((0, 0)), (255, 255, 255, 100)) + + def test_src_alpha_compatible(self): + """ "What pygame 1.9.x did". Is the alpha blitter as before?""" + + # The table below was generated with the SDL1 blit. + # def print_table(): + # nums = [0, 1, 65, 126, 127, 199, 254, 255] + # results = {} + # for dest_r, dest_b, dest_a in zip(nums, reversed(nums), reversed(nums)): + # for src_r, src_b, src_a in zip(nums, reversed(nums), nums): + # src_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32) + # src_surf.fill((src_r, 255, src_b, src_a)) + # dest_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32) + # dest_surf.fill((dest_r, 255, dest_b, dest_a)) + # dest_surf.blit(src_surf, (0, 0)) + # key = ((dest_r, dest_b, dest_a), (src_r, src_b, src_a)) + # results[key] = dest_surf.get_at((65, 33)) + # print("(dest_r, dest_b, dest_a), (src_r, src_b, src_a): color") + # pprint(results) + + results_expected = { + ((0, 255, 255), (0, 255, 0)): (0, 255, 255, 255), + ((0, 255, 255), (1, 254, 1)): (0, 255, 255, 255), + ((0, 255, 255), (65, 199, 65)): (16, 255, 241, 255), + ((0, 255, 255), (126, 127, 126)): (62, 255, 192, 255), + ((0, 255, 255), (127, 126, 127)): (63, 255, 191, 255), + ((0, 255, 255), (199, 65, 199)): (155, 255, 107, 255), + ((0, 255, 255), (254, 1, 254)): (253, 255, 2, 255), + ((0, 255, 255), (255, 0, 255)): (255, 255, 0, 255), + ((1, 254, 254), (0, 255, 0)): (1, 255, 254, 254), + ((1, 254, 254), (1, 254, 1)): (1, 255, 254, 255), + ((1, 254, 254), (65, 199, 65)): (17, 255, 240, 255), + ((1, 254, 254), (126, 127, 126)): (63, 255, 191, 255), + ((1, 254, 254), (127, 126, 127)): (64, 255, 190, 255), + ((1, 254, 254), (199, 65, 199)): (155, 255, 107, 255), + ((1, 254, 254), (254, 1, 254)): (253, 255, 2, 255), + ((1, 254, 254), (255, 0, 255)): (255, 255, 0, 255), + ((65, 199, 199), (0, 255, 0)): (65, 255, 199, 199), + ((65, 199, 199), (1, 254, 1)): (64, 255, 200, 200), + ((65, 199, 199), (65, 199, 65)): (65, 255, 199, 214), + ((65, 199, 199), (126, 127, 126)): (95, 255, 164, 227), + ((65, 199, 199), (127, 126, 127)): (96, 255, 163, 227), + ((65, 199, 199), (199, 65, 199)): (169, 255, 95, 243), + ((65, 199, 199), (254, 1, 254)): (253, 255, 2, 255), + ((65, 199, 199), (255, 0, 255)): (255, 255, 0, 255), + ((126, 127, 127), (0, 255, 0)): (126, 255, 127, 127), + ((126, 127, 127), (1, 254, 1)): (125, 255, 128, 128), + ((126, 127, 127), (65, 199, 65)): (110, 255, 146, 160), + ((126, 127, 127), (126, 127, 126)): (126, 255, 127, 191), + ((126, 127, 127), (127, 126, 127)): (126, 255, 126, 191), + ((126, 127, 127), (199, 65, 199)): (183, 255, 79, 227), + ((126, 127, 127), (254, 1, 254)): (253, 255, 1, 255), + ((126, 127, 127), (255, 0, 255)): (255, 255, 0, 255), + ((127, 126, 126), (0, 255, 0)): (127, 255, 126, 126), + ((127, 126, 126), (1, 254, 1)): (126, 255, 127, 127), + ((127, 126, 126), (65, 199, 65)): (111, 255, 145, 159), + ((127, 126, 126), (126, 127, 126)): (127, 255, 126, 190), + ((127, 126, 126), (127, 126, 127)): (127, 255, 126, 191), + ((127, 126, 126), (199, 65, 199)): (183, 255, 78, 227), + ((127, 126, 126), (254, 1, 254)): (254, 255, 1, 255), + ((127, 126, 126), (255, 0, 255)): (255, 255, 0, 255), + ((199, 65, 65), (0, 255, 0)): (199, 255, 65, 65), + ((199, 65, 65), (1, 254, 1)): (198, 255, 66, 66), + ((199, 65, 65), (65, 199, 65)): (165, 255, 99, 114), + ((199, 65, 65), (126, 127, 126)): (163, 255, 96, 159), + ((199, 65, 65), (127, 126, 127)): (163, 255, 95, 160), + ((199, 65, 65), (199, 65, 199)): (199, 255, 65, 214), + ((199, 65, 65), (254, 1, 254)): (254, 255, 1, 255), + ((199, 65, 65), (255, 0, 255)): (255, 255, 0, 255), + ((254, 1, 1), (0, 255, 0)): (254, 255, 1, 1), + ((254, 1, 1), (1, 254, 1)): (253, 255, 2, 2), + ((254, 1, 1), (65, 199, 65)): (206, 255, 52, 66), + ((254, 1, 1), (126, 127, 126)): (191, 255, 63, 127), + ((254, 1, 1), (127, 126, 127)): (191, 255, 63, 128), + ((254, 1, 1), (199, 65, 199)): (212, 255, 51, 200), + ((254, 1, 1), (254, 1, 254)): (254, 255, 1, 255), + ((254, 1, 1), (255, 0, 255)): (255, 255, 0, 255), + ((255, 0, 0), (0, 255, 0)): (0, 255, 255, 0), + ((255, 0, 0), (1, 254, 1)): (1, 255, 254, 1), + ((255, 0, 0), (65, 199, 65)): (65, 255, 199, 65), + ((255, 0, 0), (126, 127, 126)): (126, 255, 127, 126), + ((255, 0, 0), (127, 126, 127)): (127, 255, 126, 127), + ((255, 0, 0), (199, 65, 199)): (199, 255, 65, 199), + ((255, 0, 0), (254, 1, 254)): (254, 255, 1, 254), + ((255, 0, 0), (255, 0, 255)): (255, 255, 0, 255), + } + + # chosen because they contain edge cases. + nums = [0, 1, 65, 126, 127, 199, 254, 255] + results = {} + for dst_r, dst_b, dst_a in zip(nums, reversed(nums), reversed(nums)): + for src_r, src_b, src_a in zip(nums, reversed(nums), nums): + with self.subTest( + src_r=src_r, + src_b=src_b, + src_a=src_a, + dest_r=dst_r, + dest_b=dst_b, + dest_a=dst_a, + ): + src_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32) + src_surf.fill((src_r, 255, src_b, src_a)) + dest_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32) + dest_surf.fill((dst_r, 255, dst_b, dst_a)) + + dest_surf.blit(src_surf, (0, 0)) + key = ((dst_r, dst_b, dst_a), (src_r, src_b, src_a)) + results[key] = dest_surf.get_at((65, 33)) + self.assertEqual(results[key], results_expected[key]) + + self.assertEqual(results, results_expected) + + def test_src_alpha_compatible_16bit(self): + """ "What pygame 1.9.x did". Is the alpha blitter as before?""" + + # The table below was generated with the SDL1 blit. + # def print_table(): + # nums = [0, 1, 65, 126, 127, 199, 254, 255] + # results = {} + # for dest_r, dest_b, dest_a in zip(nums, reversed(nums), reversed(nums)): + # for src_r, src_b, src_a in zip(nums, reversed(nums), nums): + # src_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 16) + # src_surf.fill((src_r, 255, src_b, src_a)) + # dest_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 16) + # dest_surf.fill((dest_r, 255, dest_b, dest_a)) + # dest_surf.blit(src_surf, (0, 0)) + # key = ((dest_r, dest_b, dest_a), (src_r, src_b, src_a)) + # results[key] = dest_surf.get_at((65, 33)) + # print("(dest_r, dest_b, dest_a), (src_r, src_b, src_a): color") + # pprint(results) + + results_expected = { + ((0, 255, 255), (0, 255, 0)): (0, 255, 255, 255), + ((0, 255, 255), (1, 254, 1)): (0, 255, 255, 255), + ((0, 255, 255), (65, 199, 65)): (17, 255, 255, 255), + ((0, 255, 255), (126, 127, 126)): (51, 255, 204, 255), + ((0, 255, 255), (127, 126, 127)): (51, 255, 204, 255), + ((0, 255, 255), (199, 65, 199)): (170, 255, 102, 255), + ((0, 255, 255), (254, 1, 254)): (255, 255, 0, 255), + ((0, 255, 255), (255, 0, 255)): (255, 255, 0, 255), + ((1, 254, 254), (0, 255, 0)): (0, 255, 255, 255), + ((1, 254, 254), (1, 254, 1)): (0, 255, 255, 255), + ((1, 254, 254), (65, 199, 65)): (17, 255, 255, 255), + ((1, 254, 254), (126, 127, 126)): (51, 255, 204, 255), + ((1, 254, 254), (127, 126, 127)): (51, 255, 204, 255), + ((1, 254, 254), (199, 65, 199)): (170, 255, 102, 255), + ((1, 254, 254), (254, 1, 254)): (255, 255, 0, 255), + ((1, 254, 254), (255, 0, 255)): (255, 255, 0, 255), + ((65, 199, 199), (0, 255, 0)): (68, 255, 204, 204), + ((65, 199, 199), (1, 254, 1)): (68, 255, 204, 204), + ((65, 199, 199), (65, 199, 65)): (68, 255, 204, 221), + ((65, 199, 199), (126, 127, 126)): (85, 255, 170, 238), + ((65, 199, 199), (127, 126, 127)): (85, 255, 170, 238), + ((65, 199, 199), (199, 65, 199)): (187, 255, 85, 255), + ((65, 199, 199), (254, 1, 254)): (255, 255, 0, 255), + ((65, 199, 199), (255, 0, 255)): (255, 255, 0, 255), + ((126, 127, 127), (0, 255, 0)): (119, 255, 119, 119), + ((126, 127, 127), (1, 254, 1)): (119, 255, 119, 119), + ((126, 127, 127), (65, 199, 65)): (102, 255, 136, 153), + ((126, 127, 127), (126, 127, 126)): (119, 255, 119, 187), + ((126, 127, 127), (127, 126, 127)): (119, 255, 119, 187), + ((126, 127, 127), (199, 65, 199)): (187, 255, 68, 238), + ((126, 127, 127), (254, 1, 254)): (255, 255, 0, 255), + ((126, 127, 127), (255, 0, 255)): (255, 255, 0, 255), + ((127, 126, 126), (0, 255, 0)): (119, 255, 119, 119), + ((127, 126, 126), (1, 254, 1)): (119, 255, 119, 119), + ((127, 126, 126), (65, 199, 65)): (102, 255, 136, 153), + ((127, 126, 126), (126, 127, 126)): (119, 255, 119, 187), + ((127, 126, 126), (127, 126, 127)): (119, 255, 119, 187), + ((127, 126, 126), (199, 65, 199)): (187, 255, 68, 238), + ((127, 126, 126), (254, 1, 254)): (255, 255, 0, 255), + ((127, 126, 126), (255, 0, 255)): (255, 255, 0, 255), + ((199, 65, 65), (0, 255, 0)): (204, 255, 68, 68), + ((199, 65, 65), (1, 254, 1)): (204, 255, 68, 68), + ((199, 65, 65), (65, 199, 65)): (170, 255, 102, 119), + ((199, 65, 65), (126, 127, 126)): (170, 255, 85, 153), + ((199, 65, 65), (127, 126, 127)): (170, 255, 85, 153), + ((199, 65, 65), (199, 65, 199)): (204, 255, 68, 221), + ((199, 65, 65), (254, 1, 254)): (255, 255, 0, 255), + ((199, 65, 65), (255, 0, 255)): (255, 255, 0, 255), + ((254, 1, 1), (0, 255, 0)): (0, 255, 255, 0), + ((254, 1, 1), (1, 254, 1)): (0, 255, 255, 0), + ((254, 1, 1), (65, 199, 65)): (68, 255, 204, 68), + ((254, 1, 1), (126, 127, 126)): (119, 255, 119, 119), + ((254, 1, 1), (127, 126, 127)): (119, 255, 119, 119), + ((254, 1, 1), (199, 65, 199)): (204, 255, 68, 204), + ((254, 1, 1), (254, 1, 254)): (255, 255, 0, 255), + ((254, 1, 1), (255, 0, 255)): (255, 255, 0, 255), + ((255, 0, 0), (0, 255, 0)): (0, 255, 255, 0), + ((255, 0, 0), (1, 254, 1)): (0, 255, 255, 0), + ((255, 0, 0), (65, 199, 65)): (68, 255, 204, 68), + ((255, 0, 0), (126, 127, 126)): (119, 255, 119, 119), + ((255, 0, 0), (127, 126, 127)): (119, 255, 119, 119), + ((255, 0, 0), (199, 65, 199)): (204, 255, 68, 204), + ((255, 0, 0), (254, 1, 254)): (255, 255, 0, 255), + ((255, 0, 0), (255, 0, 255)): (255, 255, 0, 255), + } + + # chosen because they contain edge cases. + nums = [0, 1, 65, 126, 127, 199, 254, 255] + results = {} + for dst_r, dst_b, dst_a in zip(nums, reversed(nums), reversed(nums)): + for src_r, src_b, src_a in zip(nums, reversed(nums), nums): + with self.subTest( + src_r=src_r, + src_b=src_b, + src_a=src_a, + dest_r=dst_r, + dest_b=dst_b, + dest_a=dst_a, + ): + src_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 16) + src_surf.fill((src_r, 255, src_b, src_a)) + dest_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 16) + dest_surf.fill((dst_r, 255, dst_b, dst_a)) + + dest_surf.blit(src_surf, (0, 0)) + key = ((dst_r, dst_b, dst_a), (src_r, src_b, src_a)) + results[key] = dest_surf.get_at((65, 33)) + self.assertEqual(results[key], results_expected[key]) + + self.assertEqual(results, results_expected) + + def test_sdl1_mimic_blitter_with_set_alpha(self): + """does the SDL 1 style blitter in pygame 2 work with set_alpha(), + this feature only exists in pygame 2/SDL2 SDL1 did not support + combining surface and pixel alpha""" + + results_expected = { + ((0, 255, 255), (0, 255, 0)): (0, 255, 255, 255), + ((0, 255, 255), (1, 254, 1)): (0, 255, 255, 255), + ((0, 255, 255), (65, 199, 65)): (16, 255, 241, 255), + ((0, 255, 255), (126, 127, 126)): (62, 255, 192, 255), + ((0, 255, 255), (127, 126, 127)): (63, 255, 191, 255), + ((0, 255, 255), (199, 65, 199)): (155, 255, 107, 255), + ((0, 255, 255), (254, 1, 254)): (253, 255, 2, 255), + ((0, 255, 255), (255, 0, 255)): (255, 255, 0, 255), + ((1, 254, 254), (0, 255, 0)): (1, 255, 254, 254), + ((1, 254, 254), (1, 254, 1)): (1, 255, 254, 255), + ((1, 254, 254), (65, 199, 65)): (17, 255, 240, 255), + ((1, 254, 254), (126, 127, 126)): (63, 255, 191, 255), + ((1, 254, 254), (127, 126, 127)): (64, 255, 190, 255), + ((1, 254, 254), (199, 65, 199)): (155, 255, 107, 255), + ((1, 254, 254), (254, 1, 254)): (253, 255, 2, 255), + ((1, 254, 254), (255, 0, 255)): (255, 255, 0, 255), + ((65, 199, 199), (0, 255, 0)): (65, 255, 199, 199), + ((65, 199, 199), (1, 254, 1)): (64, 255, 200, 200), + ((65, 199, 199), (65, 199, 65)): (65, 255, 199, 214), + ((65, 199, 199), (126, 127, 126)): (95, 255, 164, 227), + ((65, 199, 199), (127, 126, 127)): (96, 255, 163, 227), + ((65, 199, 199), (199, 65, 199)): (169, 255, 95, 243), + ((65, 199, 199), (254, 1, 254)): (253, 255, 2, 255), + ((65, 199, 199), (255, 0, 255)): (255, 255, 0, 255), + ((126, 127, 127), (0, 255, 0)): (126, 255, 127, 127), + ((126, 127, 127), (1, 254, 1)): (125, 255, 128, 128), + ((126, 127, 127), (65, 199, 65)): (110, 255, 146, 160), + ((126, 127, 127), (126, 127, 126)): (126, 255, 127, 191), + ((126, 127, 127), (127, 126, 127)): (126, 255, 126, 191), + ((126, 127, 127), (199, 65, 199)): (183, 255, 79, 227), + ((126, 127, 127), (254, 1, 254)): (253, 255, 1, 255), + ((126, 127, 127), (255, 0, 255)): (255, 255, 0, 255), + ((127, 126, 126), (0, 255, 0)): (127, 255, 126, 126), + ((127, 126, 126), (1, 254, 1)): (126, 255, 127, 127), + ((127, 126, 126), (65, 199, 65)): (111, 255, 145, 159), + ((127, 126, 126), (126, 127, 126)): (127, 255, 126, 190), + ((127, 126, 126), (127, 126, 127)): (127, 255, 126, 191), + ((127, 126, 126), (199, 65, 199)): (183, 255, 78, 227), + ((127, 126, 126), (254, 1, 254)): (254, 255, 1, 255), + ((127, 126, 126), (255, 0, 255)): (255, 255, 0, 255), + ((199, 65, 65), (0, 255, 0)): (199, 255, 65, 65), + ((199, 65, 65), (1, 254, 1)): (198, 255, 66, 66), + ((199, 65, 65), (65, 199, 65)): (165, 255, 99, 114), + ((199, 65, 65), (126, 127, 126)): (163, 255, 96, 159), + ((199, 65, 65), (127, 126, 127)): (163, 255, 95, 160), + ((199, 65, 65), (199, 65, 199)): (199, 255, 65, 214), + ((199, 65, 65), (254, 1, 254)): (254, 255, 1, 255), + ((199, 65, 65), (255, 0, 255)): (255, 255, 0, 255), + ((254, 1, 1), (0, 255, 0)): (254, 255, 1, 1), + ((254, 1, 1), (1, 254, 1)): (253, 255, 2, 2), + ((254, 1, 1), (65, 199, 65)): (206, 255, 52, 66), + ((254, 1, 1), (126, 127, 126)): (191, 255, 63, 127), + ((254, 1, 1), (127, 126, 127)): (191, 255, 63, 128), + ((254, 1, 1), (199, 65, 199)): (212, 255, 51, 200), + ((254, 1, 1), (254, 1, 254)): (254, 255, 1, 255), + ((254, 1, 1), (255, 0, 255)): (255, 255, 0, 255), + ((255, 0, 0), (0, 255, 0)): (0, 255, 255, 0), + ((255, 0, 0), (1, 254, 1)): (1, 255, 254, 1), + ((255, 0, 0), (65, 199, 65)): (65, 255, 199, 65), + ((255, 0, 0), (126, 127, 126)): (126, 255, 127, 126), + ((255, 0, 0), (127, 126, 127)): (127, 255, 126, 127), + ((255, 0, 0), (199, 65, 199)): (199, 255, 65, 199), + ((255, 0, 0), (254, 1, 254)): (254, 255, 1, 254), + ((255, 0, 0), (255, 0, 255)): (255, 255, 0, 255), + } + + # chosen because they contain edge cases. + nums = [0, 1, 65, 126, 127, 199, 254, 255] + results = {} + for dst_r, dst_b, dst_a in zip(nums, reversed(nums), reversed(nums)): + for src_r, src_b, src_a in zip(nums, reversed(nums), nums): + with self.subTest( + src_r=src_r, + src_b=src_b, + src_a=src_a, + dest_r=dst_r, + dest_b=dst_b, + dest_a=dst_a, + ): + src_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32) + src_surf.fill((src_r, 255, src_b, 255)) + src_surf.set_alpha(src_a) + dest_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32) + dest_surf.fill((dst_r, 255, dst_b, dst_a)) + + dest_surf.blit(src_surf, (0, 0)) + key = ((dst_r, dst_b, dst_a), (src_r, src_b, src_a)) + results[key] = dest_surf.get_at((65, 33)) + self.assertEqual(results[key], results_expected[key]) + + self.assertEqual(results, results_expected) + + @unittest.skipIf( + "arm" in platform.machine() or "aarch64" in platform.machine(), + "sdl2 blitter produces different results on arm", + ) + def test_src_alpha_sdl2_blitter(self): + """Checking that the BLEND_ALPHA_SDL2 flag works - this feature + only exists when using SDL2""" + + results_expected = { + ((0, 255, 255), (0, 255, 0)): (0, 255, 255, 255), + ((0, 255, 255), (1, 254, 1)): (0, 253, 253, 253), + ((0, 255, 255), (65, 199, 65)): (16, 253, 239, 253), + ((0, 255, 255), (126, 127, 126)): (62, 253, 190, 253), + ((0, 255, 255), (127, 126, 127)): (63, 253, 189, 253), + ((0, 255, 255), (199, 65, 199)): (154, 253, 105, 253), + ((0, 255, 255), (254, 1, 254)): (252, 253, 0, 253), + ((0, 255, 255), (255, 0, 255)): (255, 255, 0, 255), + ((1, 254, 254), (0, 255, 0)): (1, 255, 254, 254), + ((1, 254, 254), (1, 254, 1)): (0, 253, 252, 252), + ((1, 254, 254), (65, 199, 65)): (16, 253, 238, 252), + ((1, 254, 254), (126, 127, 126)): (62, 253, 189, 252), + ((1, 254, 254), (127, 126, 127)): (63, 253, 189, 253), + ((1, 254, 254), (199, 65, 199)): (154, 253, 105, 253), + ((1, 254, 254), (254, 1, 254)): (252, 253, 0, 253), + ((1, 254, 254), (255, 0, 255)): (255, 255, 0, 255), + ((65, 199, 199), (0, 255, 0)): (65, 255, 199, 199), + ((65, 199, 199), (1, 254, 1)): (64, 253, 197, 197), + ((65, 199, 199), (65, 199, 65)): (64, 253, 197, 211), + ((65, 199, 199), (126, 127, 126)): (94, 253, 162, 225), + ((65, 199, 199), (127, 126, 127)): (95, 253, 161, 225), + ((65, 199, 199), (199, 65, 199)): (168, 253, 93, 241), + ((65, 199, 199), (254, 1, 254)): (252, 253, 0, 253), + ((65, 199, 199), (255, 0, 255)): (255, 255, 0, 255), + ((126, 127, 127), (0, 255, 0)): (126, 255, 127, 127), + ((126, 127, 127), (1, 254, 1)): (125, 253, 126, 126), + ((126, 127, 127), (65, 199, 65)): (109, 253, 144, 158), + ((126, 127, 127), (126, 127, 126)): (125, 253, 125, 188), + ((126, 127, 127), (127, 126, 127)): (126, 253, 125, 189), + ((126, 127, 127), (199, 65, 199)): (181, 253, 77, 225), + ((126, 127, 127), (254, 1, 254)): (252, 253, 0, 253), + ((126, 127, 127), (255, 0, 255)): (255, 255, 0, 255), + ((127, 126, 126), (0, 255, 0)): (127, 255, 126, 126), + ((127, 126, 126), (1, 254, 1)): (126, 253, 125, 125), + ((127, 126, 126), (65, 199, 65)): (110, 253, 143, 157), + ((127, 126, 126), (126, 127, 126)): (125, 253, 125, 188), + ((127, 126, 126), (127, 126, 127)): (126, 253, 125, 189), + ((127, 126, 126), (199, 65, 199)): (181, 253, 77, 225), + ((127, 126, 126), (254, 1, 254)): (252, 253, 0, 253), + ((127, 126, 126), (255, 0, 255)): (255, 255, 0, 255), + ((199, 65, 65), (0, 255, 0)): (199, 255, 65, 65), + ((199, 65, 65), (1, 254, 1)): (197, 253, 64, 64), + ((199, 65, 65), (65, 199, 65)): (163, 253, 98, 112), + ((199, 65, 65), (126, 127, 126)): (162, 253, 94, 157), + ((199, 65, 65), (127, 126, 127)): (162, 253, 94, 158), + ((199, 65, 65), (199, 65, 199)): (197, 253, 64, 212), + ((199, 65, 65), (254, 1, 254)): (252, 253, 0, 253), + ((199, 65, 65), (255, 0, 255)): (255, 255, 0, 255), + ((254, 1, 1), (0, 255, 0)): (254, 255, 1, 1), + ((254, 1, 1), (1, 254, 1)): (252, 253, 0, 0), + ((254, 1, 1), (65, 199, 65)): (204, 253, 50, 64), + ((254, 1, 1), (126, 127, 126)): (189, 253, 62, 125), + ((254, 1, 1), (127, 126, 127)): (190, 253, 62, 126), + ((254, 1, 1), (199, 65, 199)): (209, 253, 50, 198), + ((254, 1, 1), (254, 1, 254)): (252, 253, 0, 253), + ((254, 1, 1), (255, 0, 255)): (255, 255, 0, 255), + ((255, 0, 0), (0, 255, 0)): (255, 255, 0, 0), + ((255, 0, 0), (1, 254, 1)): (253, 253, 0, 0), + ((255, 0, 0), (65, 199, 65)): (205, 253, 50, 64), + ((255, 0, 0), (126, 127, 126)): (190, 253, 62, 125), + ((255, 0, 0), (127, 126, 127)): (190, 253, 62, 126), + ((255, 0, 0), (199, 65, 199)): (209, 253, 50, 198), + ((255, 0, 0), (254, 1, 254)): (252, 253, 0, 253), + ((255, 0, 0), (255, 0, 255)): (255, 255, 0, 255), + } + + # chosen because they contain edge cases. + nums = [0, 1, 65, 126, 127, 199, 254, 255] + results = {} + for dst_r, dst_b, dst_a in zip(nums, reversed(nums), reversed(nums)): + for src_r, src_b, src_a in zip(nums, reversed(nums), nums): + with self.subTest( + src_r=src_r, + src_b=src_b, + src_a=src_a, + dest_r=dst_r, + dest_b=dst_b, + dest_a=dst_a, + ): + src_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32) + src_surf.fill((src_r, 255, src_b, src_a)) + dest_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32) + dest_surf.fill((dst_r, 255, dst_b, dst_a)) + + dest_surf.blit( + src_surf, (0, 0), special_flags=pygame.BLEND_ALPHA_SDL2 + ) + key = ((dst_r, dst_b, dst_a), (src_r, src_b, src_a)) + results[key] = tuple(dest_surf.get_at((65, 33))) + for i in range(4): + self.assertAlmostEqual( + results[key][i], results_expected[key][i], delta=4 + ) + + # print("(dest_r, dest_b, dest_a), (src_r, src_b, src_a): color") + # pprint(results) + + def test_opaque_destination_blit_with_set_alpha(self): + # no set_alpha() + src_surf = pygame.Surface((32, 32), pygame.SRCALPHA, 32) + src_surf.fill((255, 255, 255, 200)) + dest_surf = pygame.Surface((32, 32)) + dest_surf.fill((100, 100, 100)) + + dest_surf.blit(src_surf, (0, 0)) + + no_surf_alpha_col = dest_surf.get_at((0, 0)) + + dest_surf.fill((100, 100, 100)) + dest_surf.set_alpha(200) + dest_surf.blit(src_surf, (0, 0)) + + surf_alpha_col = dest_surf.get_at((0, 0)) + + self.assertEqual(no_surf_alpha_col, surf_alpha_col) + + def todo_test_convert(self): + self.fail() + + # Below should not use a display Surface, but create one and check it is converted + # to the depth of the display surface. + + # def test_convert(self): + # """Ensure to creates a new copy of the Surface with the pixel format changed""" + # width = 23 + # height = 17 + # size = (width, height) + # flags = 0 + # depth = 32 + # pygame.display.init() + + # try: + # convert_surface = pygame.display.set_mode(size) + # surface = pygame.surface.Surface.convert(convert_surface) + # self.assertIsNot(surface, convert_surface) + # self.assertNotEqual(surface.get_size(), size) + + # depth_surface = pygame.display.set_mode(size, flags, depth) + # surface2 = pygame.surface.Surface.convert(depth_surface) + # self.assertIsNot(surface2, depth_surface) + # self.assertEqual(surface2.get_size(), size) + # finally: + # pygame.display.quit() + + def test_convert__pixel_format_as_surface_subclass(self): + """Ensure convert accepts a Surface subclass argument.""" + expected_size = (23, 17) + convert_surface = SurfaceSubclass(expected_size, 0, 32) + depth_surface = SurfaceSubclass((31, 61), 0, 32) + + pygame.display.init() + try: + surface = convert_surface.convert(depth_surface) + + self.assertIsNot(surface, depth_surface) + self.assertIsNot(surface, convert_surface) + self.assertIsInstance(surface, pygame.Surface) + self.assertIsInstance(surface, SurfaceSubclass) + self.assertEqual(surface.get_size(), expected_size) + finally: + pygame.display.quit() + + def test_convert_alpha(self): + """Ensure the surface returned by surf.convert_alpha + has alpha values added""" + pygame.display.init() + try: + pygame.display.set_mode((640, 480)) + + s1 = pygame.Surface((100, 100), 0, 32) + s1_alpha = pygame.Surface.convert_alpha(s1) + + s2 = pygame.Surface((100, 100), 0, 32) + s2_alpha = s2.convert_alpha() + + s3 = pygame.Surface((100, 100), 0, 8) + s3_alpha = s3.convert_alpha() + + s4 = pygame.Surface((100, 100), 0, 12) + s4_alpha = s4.convert_alpha() + + s5 = pygame.Surface((100, 100), 0, 15) + s5_alpha = s5.convert_alpha() + + s6 = pygame.Surface((100, 100), 0, 16) + s6_alpha = s6.convert_alpha() + + s7 = pygame.Surface((100, 100), 0, 24) + s7_alpha = s7.convert_alpha() + + self.assertEqual(s1_alpha.get_alpha(), 255) + self.assertEqual(s2_alpha.get_alpha(), 255) + self.assertEqual(s3_alpha.get_alpha(), 255) + self.assertEqual(s4_alpha.get_alpha(), 255) + self.assertEqual(s5_alpha.get_alpha(), 255) + self.assertEqual(s6_alpha.get_alpha(), 255) + self.assertEqual(s7_alpha.get_alpha(), 255) + + self.assertEqual(s1_alpha.get_bitsize(), 32) + self.assertEqual(s2_alpha.get_bitsize(), 32) + self.assertEqual(s3_alpha.get_bitsize(), 32) + self.assertEqual(s4_alpha.get_bitsize(), 32) + self.assertEqual(s5_alpha.get_bitsize(), 32) + self.assertEqual(s6_alpha.get_bitsize(), 32) + self.assertEqual(s6_alpha.get_bitsize(), 32) + + with self.assertRaises(pygame.error): + surface = pygame.display.set_mode() + pygame.display.quit() + surface.convert_alpha() + + finally: + pygame.display.quit() + + def test_convert_alpha__pixel_format_as_surface_subclass(self): + """Ensure convert_alpha accepts a Surface subclass argument.""" + expected_size = (23, 17) + convert_surface = SurfaceSubclass(expected_size, SRCALPHA, 32) + depth_surface = SurfaceSubclass((31, 57), SRCALPHA, 32) + + pygame.display.init() + try: + pygame.display.set_mode((60, 60)) + + # This is accepted as an argument, but its values are ignored. + # See issue #599. + surface = convert_surface.convert_alpha(depth_surface) + + self.assertIsNot(surface, depth_surface) + self.assertIsNot(surface, convert_surface) + self.assertIsInstance(surface, pygame.Surface) + self.assertIsInstance(surface, SurfaceSubclass) + self.assertEqual(surface.get_size(), expected_size) + finally: + pygame.display.quit() + + def test_get_abs_offset(self): + pygame.display.init() + try: + parent = pygame.Surface((64, 64), SRCALPHA, 32) + + # Stack bunch of subsurfaces + sub_level_1 = parent.subsurface((2, 2), (34, 37)) + sub_level_2 = sub_level_1.subsurface((0, 0), (30, 29)) + sub_level_3 = sub_level_2.subsurface((3, 7), (20, 21)) + sub_level_4 = sub_level_3.subsurface((6, 1), (14, 14)) + sub_level_5 = sub_level_4.subsurface((5, 6), (3, 4)) + + # Parent is always (0, 0) + self.assertEqual(parent.get_abs_offset(), (0, 0)) + # Total offset: (0+2, 0+2) = (2, 2) + self.assertEqual(sub_level_1.get_abs_offset(), (2, 2)) + # Total offset: (0+2+0, 0+2+0) = (2, 2) + self.assertEqual(sub_level_2.get_abs_offset(), (2, 2)) + # Total offset: (0+2+0+3, 0+2+0+7) = (5, 9) + self.assertEqual(sub_level_3.get_abs_offset(), (5, 9)) + # Total offset: (0+2+0+3+6, 0+2+0+7+1) = (11, 10) + self.assertEqual(sub_level_4.get_abs_offset(), (11, 10)) + # Total offset: (0+2+0+3+6+5, 0+2+0+7+1+6) = (16, 16) + self.assertEqual(sub_level_5.get_abs_offset(), (16, 16)) + + with self.assertRaises(pygame.error): + surface = pygame.display.set_mode() + pygame.display.quit() + surface.get_abs_offset() + finally: + pygame.display.quit() + + def test_get_abs_parent(self): + pygame.display.init() + try: + parent = pygame.Surface((32, 32), SRCALPHA, 32) + + # Stack bunch of subsurfaces + sub_level_1 = parent.subsurface((1, 1), (15, 15)) + sub_level_2 = sub_level_1.subsurface((1, 1), (12, 12)) + sub_level_3 = sub_level_2.subsurface((1, 1), (9, 9)) + sub_level_4 = sub_level_3.subsurface((1, 1), (8, 8)) + sub_level_5 = sub_level_4.subsurface((2, 2), (3, 4)) + sub_level_6 = sub_level_5.subsurface((0, 0), (2, 1)) + + # Can't have subsurfaces bigger than parents + self.assertRaises(ValueError, parent.subsurface, (5, 5), (100, 100)) + self.assertRaises(ValueError, sub_level_3.subsurface, (0, 0), (11, 5)) + self.assertRaises(ValueError, sub_level_6.subsurface, (0, 0), (5, 5)) + + # Calling get_abs_parent on parent should return itself + self.assertEqual(parent.get_abs_parent(), parent) + + # On subclass "depth" of 1, get_abs_parent and get_parent should return the same + self.assertEqual(sub_level_1.get_abs_parent(), sub_level_1.get_parent()) + self.assertEqual(sub_level_2.get_abs_parent(), parent) + self.assertEqual(sub_level_3.get_abs_parent(), parent) + self.assertEqual(sub_level_4.get_abs_parent(), parent) + self.assertEqual(sub_level_5.get_abs_parent(), parent) + self.assertEqual( + sub_level_6.get_abs_parent(), sub_level_6.get_parent().get_abs_parent() + ) + + with self.assertRaises(pygame.error): + surface = pygame.display.set_mode() + pygame.display.quit() + surface.get_abs_parent() + finally: + pygame.display.quit() + + def test_get_at(self): + surf = pygame.Surface((2, 2), 0, 24) + c00 = pygame.Color(1, 2, 3) + c01 = pygame.Color(5, 10, 15) + c10 = pygame.Color(100, 50, 0) + c11 = pygame.Color(4, 5, 6) + surf.set_at((0, 0), c00) + surf.set_at((0, 1), c01) + surf.set_at((1, 0), c10) + surf.set_at((1, 1), c11) + c = surf.get_at((0, 0)) + self.assertIsInstance(c, pygame.Color) + self.assertEqual(c, c00) + self.assertEqual(surf.get_at((0, 1)), c01) + self.assertEqual(surf.get_at((1, 0)), c10) + self.assertEqual(surf.get_at((1, 1)), c11) + for p in [(-1, 0), (0, -1), (2, 0), (0, 2)]: + self.assertRaises(IndexError, surf.get_at, p) + + def test_get_at_mapped(self): + color = pygame.Color(10, 20, 30) + for bitsize in [8, 16, 24, 32]: + surf = pygame.Surface((2, 2), 0, bitsize) + surf.fill(color) + pixel = surf.get_at_mapped((0, 0)) + self.assertEqual( + pixel, + surf.map_rgb(color), + "%i != %i, bitsize: %i" % (pixel, surf.map_rgb(color), bitsize), + ) + + def test_get_bitsize(self): + pygame.display.init() + try: + expected_size = (11, 21) + + # Check that get_bitsize returns passed depth + expected_depth = 32 + surface = pygame.Surface(expected_size, pygame.SRCALPHA, expected_depth) + self.assertEqual(surface.get_size(), expected_size) + self.assertEqual(surface.get_bitsize(), expected_depth) + + expected_depth = 16 + surface = pygame.Surface(expected_size, pygame.SRCALPHA, expected_depth) + self.assertEqual(surface.get_size(), expected_size) + self.assertEqual(surface.get_bitsize(), expected_depth) + + expected_depth = 15 + surface = pygame.Surface(expected_size, 0, expected_depth) + self.assertEqual(surface.get_size(), expected_size) + self.assertEqual(surface.get_bitsize(), expected_depth) + # Check for invalid depths + expected_depth = -1 + self.assertRaises( + ValueError, pygame.Surface, expected_size, 0, expected_depth + ) + expected_depth = 11 + self.assertRaises( + ValueError, pygame.Surface, expected_size, 0, expected_depth + ) + expected_depth = 1024 + self.assertRaises( + ValueError, pygame.Surface, expected_size, 0, expected_depth + ) + + with self.assertRaises(pygame.error): + surface = pygame.display.set_mode() + pygame.display.quit() + surface.get_bitsize() + finally: + pygame.display.quit() + + def test_get_clip(self): + s = pygame.Surface((800, 600)) + rectangle = s.get_clip() + self.assertEqual(rectangle, (0, 0, 800, 600)) + + def test_get_colorkey(self): + pygame.display.init() + try: + # if set_colorkey is not used + s = pygame.Surface((800, 600), 0, 32) + self.assertIsNone(s.get_colorkey()) + + # if set_colorkey is used + s.set_colorkey(None) + self.assertIsNone(s.get_colorkey()) + + # setting up remainder of tests... + r, g, b, a = 20, 40, 60, 12 + colorkey = pygame.Color(r, g, b) + s.set_colorkey(colorkey) + + # test for ideal case + self.assertEqual(s.get_colorkey(), (r, g, b, 255)) + + # test for if the color_key is set using pygame.RLEACCEL + s.set_colorkey(colorkey, pygame.RLEACCEL) + self.assertEqual(s.get_colorkey(), (r, g, b, 255)) + + # test for if the color key is not what's expected + s.set_colorkey(pygame.Color(r + 1, g + 1, b + 1)) + self.assertNotEqual(s.get_colorkey(), (r, g, b, 255)) + + s.set_colorkey(pygame.Color(r, g, b, a)) + # regardless of whether alpha is not 255 + # colorkey returned from surface is always 255 + self.assertEqual(s.get_colorkey(), (r, g, b, 255)) + finally: + # test for using method after display.quit() is called... + s = pygame.display.set_mode((200, 200), 0, 32) + pygame.display.quit() + with self.assertRaises(pygame.error): + s.get_colorkey() + + def test_get_height(self): + sizes = ((1, 1), (119, 10), (10, 119), (1, 1000), (1000, 1), (1000, 1000)) + for width, height in sizes: + surf = pygame.Surface((width, height)) + found_height = surf.get_height() + self.assertEqual(height, found_height) + + def test_get_locked(self): + def blit_locked_test(surface): + newSurf = pygame.Surface((10, 10)) + try: + newSurf.blit(surface, (0, 0)) + except pygame.error: + return True + else: + return False + + surf = pygame.Surface((100, 100)) + + self.assertIs(surf.get_locked(), blit_locked_test(surf)) # Unlocked + # Surface should lock + surf.lock() + self.assertIs(surf.get_locked(), blit_locked_test(surf)) # Locked + # Surface should unlock + surf.unlock() + self.assertIs(surf.get_locked(), blit_locked_test(surf)) # Unlocked + + # Check multiple locks + surf = pygame.Surface((100, 100)) + surf.lock() + surf.lock() + self.assertIs(surf.get_locked(), blit_locked_test(surf)) # Locked + surf.unlock() + self.assertIs(surf.get_locked(), blit_locked_test(surf)) # Locked + surf.unlock() + self.assertIs(surf.get_locked(), blit_locked_test(surf)) # Unlocked + + # Check many locks + surf = pygame.Surface((100, 100)) + for i in range(1000): + surf.lock() + self.assertIs(surf.get_locked(), blit_locked_test(surf)) # Locked + for i in range(1000): + surf.unlock() + self.assertFalse(surf.get_locked()) # Unlocked + + # Unlocking an unlocked surface + surf = pygame.Surface((100, 100)) + surf.unlock() + self.assertIs(surf.get_locked(), blit_locked_test(surf)) # Unlocked + surf.unlock() + self.assertIs(surf.get_locked(), blit_locked_test(surf)) # Unlocked + + def test_get_locks(self): + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_locks: + + # Surface.get_locks(): return tuple + # Gets the locks for the Surface + # + # Returns the currently existing locks for the Surface. + + # test on a surface that is not initially locked + surface = pygame.Surface((100, 100)) + self.assertEqual(surface.get_locks(), ()) + + # test on the same surface after it has been locked + surface.lock() + self.assertEqual(surface.get_locks(), (surface,)) + + # test on the same surface after it has been unlocked + surface.unlock() + self.assertEqual(surface.get_locks(), ()) + + # test with PixelArray initialization: locks surface + pxarray = pygame.PixelArray(surface) + self.assertNotEqual(surface.get_locks(), ()) + + # closing the PixelArray releases the surface lock + pxarray.close() + self.assertEqual(surface.get_locks(), ()) + + # AttributeError raised when called on invalid object type (i.e. not a pygame.Surface object) + with self.assertRaises(AttributeError): + "DUMMY".get_locks() + + # test multiple locks and unlocks on the same surface + surface.lock() + surface.lock() + surface.lock() + self.assertEqual(surface.get_locks(), (surface, surface, surface)) + + surface.unlock() + surface.unlock() + self.assertEqual(surface.get_locks(), (surface,)) + surface.unlock() + self.assertEqual(surface.get_locks(), ()) + + def test_get_losses(self): + """Ensure a surface's losses can be retrieved""" + pygame.display.init() + try: + # Masks for different color component configurations + mask8 = (224, 28, 3, 0) + mask15 = (31744, 992, 31, 0) + mask16 = (63488, 2016, 31, 0) + mask24 = (16711680, 65280, 255, 0) + mask32 = (4278190080, 16711680, 65280, 255) + + # Surfaces with standard depths and masks + display_surf = pygame.display.set_mode((100, 100)) + surf = pygame.Surface((100, 100)) + surf_8bit = pygame.Surface((100, 100), depth=8, masks=mask8) + surf_15bit = pygame.Surface((100, 100), depth=15, masks=mask15) + surf_16bit = pygame.Surface((100, 100), depth=16, masks=mask16) + surf_24bit = pygame.Surface((100, 100), depth=24, masks=mask24) + surf_32bit = pygame.Surface((100, 100), depth=32, masks=mask32) + + # Test output is correct type, length, and value range + losses = surf.get_losses() + self.assertIsInstance(losses, tuple) + self.assertEqual(len(losses), 4) + for loss in losses: + self.assertIsInstance(loss, int) + self.assertGreaterEqual(loss, 0) + self.assertLessEqual(loss, 8) + + # Test each surface for correct losses + # Display surface losses gives idea of default surface losses + if display_surf.get_losses() == (0, 0, 0, 8): + self.assertEqual(losses, (0, 0, 0, 8)) + elif display_surf.get_losses() == (8, 8, 8, 8): + self.assertEqual(losses, (8, 8, 8, 8)) + + self.assertEqual(surf_8bit.get_losses(), (5, 5, 6, 8)) + self.assertEqual(surf_15bit.get_losses(), (3, 3, 3, 8)) + self.assertEqual(surf_16bit.get_losses(), (3, 2, 3, 8)) + self.assertEqual(surf_24bit.get_losses(), (0, 0, 0, 8)) + self.assertEqual(surf_32bit.get_losses(), (0, 0, 0, 0)) + + # Method should fail when display is not initialized + with self.assertRaises(pygame.error): + surface = pygame.display.set_mode((100, 100)) + pygame.display.quit() + surface.get_losses() + finally: + pygame.display.quit() + + def test_get_masks__rgba(self): + """ + Ensure that get_mask can return RGBA mask. + """ + masks = [ + (0x0F00, 0x00F0, 0x000F, 0xF000), + (0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000), + ] + depths = [16, 32] + for expected, depth in list(zip(masks, depths)): + surface = pygame.Surface((10, 10), pygame.SRCALPHA, depth) + self.assertEqual(expected, surface.get_masks()) + + def test_get_masks__rgb(self): + """ + Ensure that get_mask can return RGB mask. + """ + masks = [ + (0x60, 0x1C, 0x03, 0x00), + (0xF00, 0x0F0, 0x00F, 0x000), + (0x7C00, 0x03E0, 0x001F, 0x0000), + (0xF800, 0x07E0, 0x001F, 0x0000), + (0xFF0000, 0x00FF00, 0x0000FF, 0x000000), + (0xFF0000, 0x00FF00, 0x0000FF, 0x000000), + ] + depths = [8, 12, 15, 16, 24, 32] + for expected, depth in list(zip(masks, depths)): + surface = pygame.Surface((10, 10), 0, depth) + if depth == 8: + expected = (0x00, 0x00, 0x00, 0x00) + self.assertEqual(expected, surface.get_masks()) + + def test_get_masks__no_surface(self): + """ + Ensure that after display.quit, calling get_masks raises pygame.error. + """ + with self.assertRaises(pygame.error): + surface = pygame.display.set_mode((10, 10)) + pygame.display.quit() + surface.get_masks() + + def test_get_offset(self): + """get_offset returns the (0,0) if surface is not a child + returns the position of child subsurface inside of parent + """ + pygame.display.init() + try: + surf = pygame.Surface((100, 100)) + self.assertEqual(surf.get_offset(), (0, 0)) + + # subsurface offset test + subsurf = surf.subsurface(1, 1, 10, 10) + self.assertEqual(subsurf.get_offset(), (1, 1)) + + with self.assertRaises(pygame.error): + surface = pygame.display.set_mode() + pygame.display.quit() + surface.get_offset() + finally: + pygame.display.quit() + + def test_get_palette(self): + palette = [Color(i, i, i) for i in range(256)] + surf = pygame.Surface((2, 2), 0, 8) + surf.set_palette(palette) + palette2 = surf.get_palette() + + self.assertEqual(len(palette2), len(palette)) + for c2, c in zip(palette2, palette): + self.assertEqual(c2, c) + for c in palette2: + self.assertIsInstance(c, pygame.Color) + + def test_get_palette_at(self): + # See also test_get_palette + surf = pygame.Surface((2, 2), 0, 8) + color = pygame.Color(1, 2, 3, 255) + surf.set_palette_at(0, color) + color2 = surf.get_palette_at(0) + self.assertIsInstance(color2, pygame.Color) + self.assertEqual(color2, color) + self.assertRaises(IndexError, surf.get_palette_at, -1) + self.assertRaises(IndexError, surf.get_palette_at, 256) + + def test_get_pitch(self): + # Test get_pitch() on several surfaces of varying size/depth + sizes = ((2, 2), (7, 33), (33, 7), (2, 734), (734, 2), (734, 734)) + depths = [8, 24, 32] + for width, height in sizes: + for depth in depths: + # Test get_pitch() on parent surface + surf = pygame.Surface((width, height), depth=depth) + buff = surf.get_buffer() + pitch = buff.length / surf.get_height() + test_pitch = surf.get_pitch() + self.assertEqual(pitch, test_pitch) + # Test get_pitch() on subsurface with same rect as parent + rect1 = surf.get_rect() + subsurf1 = surf.subsurface(rect1) + sub_buff1 = subsurf1.get_buffer() + sub_pitch1 = sub_buff1.length / subsurf1.get_height() + test_sub_pitch1 = subsurf1.get_pitch() + self.assertEqual(sub_pitch1, test_sub_pitch1) + # Test get_pitch on subsurface with modified rect + rect2 = rect1.inflate(-width / 2, -height / 2) + subsurf2 = surf.subsurface(rect2) + sub_buff2 = subsurf2.get_buffer() + sub_pitch2 = sub_buff2.length / float(subsurf2.get_height()) + test_sub_pitch2 = subsurf2.get_pitch() + self.assertEqual(sub_pitch2, test_sub_pitch2) + + def test_get_shifts(self): + """ + Tests whether Surface.get_shifts returns proper + RGBA shifts under various conditions. + """ + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_shifts: + # Surface.get_shifts(): return (R, G, B, A) + # the bit shifts needed to convert between color and mapped integer. + # Returns the pixel shifts need to convert between each color and a + # mapped integer. + # This value is not needed for normal Pygame usage. + + # Test for SDL2 on surfaces with various depths and alpha on/off + depths = [8, 24, 32] + alpha = 128 + off = None + for bit_depth in depths: + surface = pygame.Surface((32, 32), depth=bit_depth) + surface.set_alpha(alpha) + r1, g1, b1, a1 = surface.get_shifts() + surface.set_alpha(off) + r2, g2, b2, a2 = surface.get_shifts() + self.assertEqual((r1, g1, b1, a1), (r2, g2, b2, a2)) + + def test_get_size(self): + sizes = ((1, 1), (119, 10), (1000, 1000), (1, 5000), (1221, 1), (99, 999)) + for width, height in sizes: + surf = pygame.Surface((width, height)) + found_size = surf.get_size() + self.assertEqual((width, height), found_size) + + def test_lock(self): + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.lock: + + # Surface.lock(): return None + # lock the Surface memory for pixel access + # + # Lock the pixel data of a Surface for access. On accelerated + # Surfaces, the pixel data may be stored in volatile video memory or + # nonlinear compressed forms. When a Surface is locked the pixel + # memory becomes available to access by regular software. Code that + # reads or writes pixel values will need the Surface to be locked. + # + # Surfaces should not remain locked for more than necessary. A locked + # Surface can often not be displayed or managed by Pygame. + # + # Not all Surfaces require locking. The Surface.mustlock() method can + # determine if it is actually required. There is no performance + # penalty for locking and unlocking a Surface that does not need it. + # + # All pygame functions will automatically lock and unlock the Surface + # data as needed. If a section of code is going to make calls that + # will repeatedly lock and unlock the Surface many times, it can be + # helpful to wrap the block inside a lock and unlock pair. + # + # It is safe to nest locking and unlocking calls. The surface will + # only be unlocked after the final lock is released. + # + + # Basic + surf = pygame.Surface((100, 100)) + surf.lock() + self.assertTrue(surf.get_locked()) + + # Nested + surf = pygame.Surface((100, 100)) + surf.lock() + surf.lock() + surf.unlock() + self.assertTrue(surf.get_locked()) + surf.unlock() + surf.lock() + surf.lock() + self.assertTrue(surf.get_locked()) + surf.unlock() + self.assertTrue(surf.get_locked()) + surf.unlock() + self.assertFalse(surf.get_locked()) + + # Already Locked + surf = pygame.Surface((100, 100)) + surf.lock() + surf.lock() + self.assertTrue(surf.get_locked()) + surf.unlock() + self.assertTrue(surf.get_locked()) + surf.unlock() + self.assertFalse(surf.get_locked()) + + def test_map_rgb(self): + color = Color(0, 128, 255, 64) + surf = pygame.Surface((5, 5), SRCALPHA, 32) + c = surf.map_rgb(color) + self.assertEqual(surf.unmap_rgb(c), color) + + self.assertEqual(surf.get_at((0, 0)), (0, 0, 0, 0)) + surf.fill(c) + self.assertEqual(surf.get_at((0, 0)), color) + + surf.fill((0, 0, 0, 0)) + self.assertEqual(surf.get_at((0, 0)), (0, 0, 0, 0)) + surf.set_at((0, 0), c) + self.assertEqual(surf.get_at((0, 0)), color) + + def test_mustlock(self): + # Test that subsurfaces mustlock + surf = pygame.Surface((1024, 1024)) + subsurf = surf.subsurface((0, 0, 1024, 1024)) + self.assertTrue(subsurf.mustlock()) + self.assertFalse(surf.mustlock()) + # Tests nested subsurfaces + rects = ((0, 0, 512, 512), (0, 0, 256, 256), (0, 0, 128, 128)) + surf_stack = [] + surf_stack.append(surf) + surf_stack.append(subsurf) + for rect in rects: + surf_stack.append(surf_stack[-1].subsurface(rect)) + self.assertTrue(surf_stack[-1].mustlock()) + self.assertTrue(surf_stack[-2].mustlock()) + + def test_set_alpha_none(self): + """surf.set_alpha(None) disables blending""" + s = pygame.Surface((1, 1), SRCALPHA, 32) + s.fill((0, 255, 0, 128)) + s.set_alpha(None) + self.assertEqual(None, s.get_alpha()) + + s2 = pygame.Surface((1, 1), SRCALPHA, 32) + s2.fill((255, 0, 0, 255)) + s2.blit(s, (0, 0)) + self.assertEqual(s2.get_at((0, 0))[0], 0, "the red component should be 0") + + def test_set_alpha_value(self): + """surf.set_alpha(x), where x != None, enables blending""" + s = pygame.Surface((1, 1), SRCALPHA, 32) + s.fill((0, 255, 0, 128)) + s.set_alpha(255) + + s2 = pygame.Surface((1, 1), SRCALPHA, 32) + s2.fill((255, 0, 0, 255)) + s2.blit(s, (0, 0)) + self.assertGreater( + s2.get_at((0, 0))[0], 0, "the red component should be above 0" + ) + + def test_palette_colorkey(self): + """test bug discovered by robertpfeiffer + https://github.com/pygame/pygame/issues/721 + """ + surf = pygame.image.load(example_path(os.path.join("data", "alien2.png"))) + key = surf.get_colorkey() + self.assertEqual(surf.get_palette()[surf.map_rgb(key)], key) + + def test_palette_colorkey_set_px(self): + surf = pygame.image.load(example_path(os.path.join("data", "alien2.png"))) + key = surf.get_colorkey() + surf.set_at((0, 0), key) + self.assertEqual(surf.get_at((0, 0)), key) + + def test_palette_colorkey_fill(self): + surf = pygame.image.load(example_path(os.path.join("data", "alien2.png"))) + key = surf.get_colorkey() + surf.fill(key) + self.assertEqual(surf.get_at((0, 0)), key) + + def test_set_palette(self): + palette = [pygame.Color(i, i, i) for i in range(256)] + palette[10] = tuple(palette[10]) # 4 element tuple + palette[11] = tuple(palette[11])[0:3] # 3 element tuple + + surf = pygame.Surface((2, 2), 0, 8) + surf.set_palette(palette) + for i in range(256): + self.assertEqual(surf.map_rgb(palette[i]), i, "palette color %i" % (i,)) + c = palette[i] + surf.fill(c) + self.assertEqual(surf.get_at((0, 0)), c, "palette color %i" % (i,)) + for i in range(10): + palette[i] = pygame.Color(255 - i, 0, 0) + surf.set_palette(palette[0:10]) + for i in range(256): + self.assertEqual(surf.map_rgb(palette[i]), i, "palette color %i" % (i,)) + c = palette[i] + surf.fill(c) + self.assertEqual(surf.get_at((0, 0)), c, "palette color %i" % (i,)) + self.assertRaises(ValueError, surf.set_palette, [Color(1, 2, 3, 254)]) + self.assertRaises(ValueError, surf.set_palette, (1, 2, 3, 254)) + + def test_set_palette__fail(self): + palette = 256 * [(10, 20, 30)] + surf = pygame.Surface((2, 2), 0, 32) + self.assertRaises(pygame.error, surf.set_palette, palette) + + def test_set_palette__set_at(self): + surf = pygame.Surface((2, 2), depth=8) + palette = 256 * [(10, 20, 30)] + palette[1] = (50, 40, 30) + surf.set_palette(palette) + + # calling set_at on a palettized surface should set the pixel to + # the closest color in the palette. + surf.set_at((0, 0), (60, 50, 40)) + self.assertEqual(surf.get_at((0, 0)), (50, 40, 30, 255)) + self.assertEqual(surf.get_at((1, 0)), (10, 20, 30, 255)) + + def test_set_palette_at(self): + surf = pygame.Surface((2, 2), 0, 8) + original = surf.get_palette_at(10) + replacement = Color(1, 1, 1, 255) + if replacement == original: + replacement = Color(2, 2, 2, 255) + surf.set_palette_at(10, replacement) + self.assertEqual(surf.get_palette_at(10), replacement) + next = tuple(original) + surf.set_palette_at(10, next) + self.assertEqual(surf.get_palette_at(10), next) + next = tuple(original)[0:3] + surf.set_palette_at(10, next) + self.assertEqual(surf.get_palette_at(10), next) + self.assertRaises(IndexError, surf.set_palette_at, 256, replacement) + self.assertRaises(IndexError, surf.set_palette_at, -1, replacement) + + def test_subsurface(self): + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.subsurface: + + # Surface.subsurface(Rect): return Surface + # create a new surface that references its parent + # + # Returns a new Surface that shares its pixels with its new parent. + # The new Surface is considered a child of the original. Modifications + # to either Surface pixels will effect each other. Surface information + # like clipping area and color keys are unique to each Surface. + # + # The new Surface will inherit the palette, color key, and alpha + # settings from its parent. + # + # It is possible to have any number of subsurfaces and subsubsurfaces + # on the parent. It is also possible to subsurface the display Surface + # if the display mode is not hardware accelerated. + # + # See the Surface.get_offset(), Surface.get_parent() to learn more + # about the state of a subsurface. + # + + surf = pygame.Surface((16, 16)) + s = surf.subsurface(0, 0, 1, 1) + s = surf.subsurface((0, 0, 1, 1)) + + # s = surf.subsurface((0,0,1,1), 1) + # This form is not acceptable. + # s = surf.subsurface(0,0,10,10, 1) + + self.assertRaises(ValueError, surf.subsurface, (0, 0, 1, 1, 666)) + + self.assertEqual(s.get_shifts(), surf.get_shifts()) + self.assertEqual(s.get_masks(), surf.get_masks()) + self.assertEqual(s.get_losses(), surf.get_losses()) + + # Issue https://github.com/pygame/pygame/issues/2 + surf = pygame.Surface.__new__(pygame.Surface) + self.assertRaises(pygame.error, surf.subsurface, (0, 0, 0, 0)) + + def test_unlock(self): + # Basic + surf = pygame.Surface((100, 100)) + surf.lock() + surf.unlock() + self.assertFalse(surf.get_locked()) + + # Nested + surf = pygame.Surface((100, 100)) + surf.lock() + surf.lock() + surf.unlock() + self.assertTrue(surf.get_locked()) + surf.unlock() + self.assertFalse(surf.get_locked()) + + # Already Unlocked + surf = pygame.Surface((100, 100)) + surf.unlock() + self.assertFalse(surf.get_locked()) + surf.unlock() + self.assertFalse(surf.get_locked()) + + # Surface can be relocked + surf = pygame.Surface((100, 100)) + surf.lock() + surf.unlock() + self.assertFalse(surf.get_locked()) + surf.lock() + surf.unlock() + self.assertFalse(surf.get_locked()) + + def test_unmap_rgb(self): + # Special case, 8 bit-per-pixel surface (has a palette). + surf = pygame.Surface((2, 2), 0, 8) + c = (1, 1, 1) # Unlikely to be in a default palette. + i = 67 + surf.set_palette_at(i, c) + unmapped_c = surf.unmap_rgb(i) + self.assertEqual(unmapped_c, c) + # Confirm it is a Color instance + self.assertIsInstance(unmapped_c, pygame.Color) + + # Remaining, non-pallete, cases. + c = (128, 64, 12, 255) + formats = [(0, 16), (0, 24), (0, 32), (SRCALPHA, 16), (SRCALPHA, 32)] + for flags, bitsize in formats: + surf = pygame.Surface((2, 2), flags, bitsize) + unmapped_c = surf.unmap_rgb(surf.map_rgb(c)) + surf.fill(c) + comparison_c = surf.get_at((0, 0)) + self.assertEqual( + unmapped_c, + comparison_c, + "%s != %s, flags: %i, bitsize: %i" + % (unmapped_c, comparison_c, flags, bitsize), + ) + # Confirm it is a Color instance + self.assertIsInstance(unmapped_c, pygame.Color) + + def test_scroll(self): + scrolls = [ + (8, 2, 3), + (16, 2, 3), + (24, 2, 3), + (32, 2, 3), + (32, -1, -3), + (32, 0, 0), + (32, 11, 0), + (32, 0, 11), + (32, -11, 0), + (32, 0, -11), + (32, -11, 2), + (32, 2, -11), + ] + for bitsize, dx, dy in scrolls: + surf = pygame.Surface((10, 10), 0, bitsize) + surf.fill((255, 0, 0)) + surf.fill((0, 255, 0), (2, 2, 2, 2)) + comp = surf.copy() + comp.blit(surf, (dx, dy)) + surf.scroll(dx, dy) + w, h = surf.get_size() + for x in range(w): + for y in range(h): + with self.subTest(x=x, y=y): + self.assertEqual( + surf.get_at((x, y)), + comp.get_at((x, y)), + "%s != %s, bpp:, %i, x: %i, y: %i" + % ( + surf.get_at((x, y)), + comp.get_at((x, y)), + bitsize, + dx, + dy, + ), + ) + # Confirm clip rect containment + surf = pygame.Surface((20, 13), 0, 32) + surf.fill((255, 0, 0)) + surf.fill((0, 255, 0), (7, 1, 6, 6)) + comp = surf.copy() + clip = Rect(3, 1, 8, 14) + surf.set_clip(clip) + comp.set_clip(clip) + comp.blit(surf, (clip.x + 2, clip.y + 3), surf.get_clip()) + surf.scroll(2, 3) + w, h = surf.get_size() + for x in range(w): + for y in range(h): + self.assertEqual(surf.get_at((x, y)), comp.get_at((x, y))) + # Confirm keyword arguments and per-pixel alpha + spot_color = (0, 255, 0, 128) + surf = pygame.Surface((4, 4), pygame.SRCALPHA, 32) + surf.fill((255, 0, 0, 255)) + surf.set_at((1, 1), spot_color) + surf.scroll(dx=1) + self.assertEqual(surf.get_at((2, 1)), spot_color) + surf.scroll(dy=1) + self.assertEqual(surf.get_at((2, 2)), spot_color) + surf.scroll(dy=1, dx=1) + self.assertEqual(surf.get_at((3, 3)), spot_color) + surf.scroll(dx=-3, dy=-3) + self.assertEqual(surf.get_at((0, 0)), spot_color) + + +class SurfaceSubtypeTest(unittest.TestCase): + """Issue #280: Methods that return a new Surface preserve subclasses""" + + def setUp(self): + pygame.display.init() + + def tearDown(self): + pygame.display.quit() + + def test_copy(self): + """Ensure method copy() preserves the surface's class + + When Surface is subclassed, the inherited copy() method will return + instances of the subclass. Non Surface fields are uncopied, however. + This includes instance attributes. + """ + expected_size = (32, 32) + ms1 = SurfaceSubclass(expected_size, SRCALPHA, 32) + ms2 = ms1.copy() + + self.assertIsNot(ms1, ms2) + self.assertIsInstance(ms1, pygame.Surface) + self.assertIsInstance(ms2, pygame.Surface) + self.assertIsInstance(ms1, SurfaceSubclass) + self.assertIsInstance(ms2, SurfaceSubclass) + self.assertTrue(ms1.test_attribute) + self.assertRaises(AttributeError, getattr, ms2, "test_attribute") + self.assertEqual(ms2.get_size(), expected_size) + + def test_convert(self): + """Ensure method convert() preserves the surface's class + + When Surface is subclassed, the inherited convert() method will return + instances of the subclass. Non Surface fields are omitted, however. + This includes instance attributes. + """ + expected_size = (32, 32) + ms1 = SurfaceSubclass(expected_size, 0, 24) + ms2 = ms1.convert(24) + + self.assertIsNot(ms1, ms2) + self.assertIsInstance(ms1, pygame.Surface) + self.assertIsInstance(ms2, pygame.Surface) + self.assertIsInstance(ms1, SurfaceSubclass) + self.assertIsInstance(ms2, SurfaceSubclass) + self.assertTrue(ms1.test_attribute) + self.assertRaises(AttributeError, getattr, ms2, "test_attribute") + self.assertEqual(ms2.get_size(), expected_size) + + def test_convert_alpha(self): + """Ensure method convert_alpha() preserves the surface's class + + When Surface is subclassed, the inherited convert_alpha() method will + return instances of the subclass. Non Surface fields are omitted, + however. This includes instance attributes. + """ + pygame.display.set_mode((40, 40)) + expected_size = (32, 32) + s = pygame.Surface(expected_size, SRCALPHA, 16) + ms1 = SurfaceSubclass(expected_size, SRCALPHA, 32) + ms2 = ms1.convert_alpha(s) + + self.assertIsNot(ms1, ms2) + self.assertIsInstance(ms1, pygame.Surface) + self.assertIsInstance(ms2, pygame.Surface) + self.assertIsInstance(ms1, SurfaceSubclass) + self.assertIsInstance(ms2, SurfaceSubclass) + self.assertTrue(ms1.test_attribute) + self.assertRaises(AttributeError, getattr, ms2, "test_attribute") + self.assertEqual(ms2.get_size(), expected_size) + + def test_subsurface(self): + """Ensure method subsurface() preserves the surface's class + + When Surface is subclassed, the inherited subsurface() method will + return instances of the subclass. Non Surface fields are uncopied, + however. This includes instance attributes. + """ + expected_size = (10, 12) + ms1 = SurfaceSubclass((32, 32), SRCALPHA, 32) + ms2 = ms1.subsurface((4, 5), expected_size) + + self.assertIsNot(ms1, ms2) + self.assertIsInstance(ms1, pygame.Surface) + self.assertIsInstance(ms2, pygame.Surface) + self.assertIsInstance(ms1, SurfaceSubclass) + self.assertIsInstance(ms2, SurfaceSubclass) + self.assertTrue(ms1.test_attribute) + self.assertRaises(AttributeError, getattr, ms2, "test_attribute") + self.assertEqual(ms2.get_size(), expected_size) + + +class SurfaceGetBufferTest(unittest.TestCase): + # These tests requires ctypes. They are disabled if ctypes + # is not installed. + try: + ArrayInterface + except NameError: + __tags__ = ("ignore", "subprocess_ignore") + + lilendian = pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN + + def _check_interface_2D(self, s): + s_w, s_h = s.get_size() + s_bytesize = s.get_bytesize() + s_pitch = s.get_pitch() + s_pixels = s._pixels_address + + # check the array interface structure fields. + v = s.get_view("2") + if not IS_PYPY: + flags = PAI_ALIGNED | PAI_NOTSWAPPED | PAI_WRITEABLE + if s.get_pitch() == s_w * s_bytesize: + flags |= PAI_FORTRAN + + inter = ArrayInterface(v) + + self.assertEqual(inter.two, 2) + self.assertEqual(inter.nd, 2) + self.assertEqual(inter.typekind, "u") + self.assertEqual(inter.itemsize, s_bytesize) + self.assertEqual(inter.shape[0], s_w) + self.assertEqual(inter.shape[1], s_h) + self.assertEqual(inter.strides[0], s_bytesize) + self.assertEqual(inter.strides[1], s_pitch) + self.assertEqual(inter.flags, flags) + self.assertEqual(inter.data, s_pixels) + + def _check_interface_3D(self, s): + s_w, s_h = s.get_size() + s_bytesize = s.get_bytesize() + s_pitch = s.get_pitch() + s_pixels = s._pixels_address + s_shifts = list(s.get_shifts()) + + # Check for RGB or BGR surface. + if s_shifts[0:3] == [0, 8, 16]: + if self.lilendian: + # RGB + offset = 0 + step = 1 + else: + # BGR + offset = s_bytesize - 1 + step = -1 + elif s_shifts[0:3] == [8, 16, 24]: + if self.lilendian: + # xRGB + offset = 1 + step = 1 + else: + # BGRx + offset = s_bytesize - 2 + step = -1 + elif s_shifts[0:3] == [16, 8, 0]: + if self.lilendian: + # BGR + offset = 2 + step = -1 + else: + # RGB + offset = s_bytesize - 3 + step = 1 + elif s_shifts[0:3] == [24, 16, 8]: + if self.lilendian: + # BGRx + offset = 2 + step = -1 + else: + # RGBx + offset = s_bytesize - 4 + step = -1 + else: + return + + # check the array interface structure fields. + v = s.get_view("3") + if not IS_PYPY: + inter = ArrayInterface(v) + flags = PAI_ALIGNED | PAI_NOTSWAPPED | PAI_WRITEABLE + self.assertEqual(inter.two, 2) + self.assertEqual(inter.nd, 3) + self.assertEqual(inter.typekind, "u") + self.assertEqual(inter.itemsize, 1) + self.assertEqual(inter.shape[0], s_w) + self.assertEqual(inter.shape[1], s_h) + self.assertEqual(inter.shape[2], 3) + self.assertEqual(inter.strides[0], s_bytesize) + self.assertEqual(inter.strides[1], s_pitch) + self.assertEqual(inter.strides[2], step) + self.assertEqual(inter.flags, flags) + self.assertEqual(inter.data, s_pixels + offset) + + def _check_interface_rgba(self, s, plane): + s_w, s_h = s.get_size() + s_bytesize = s.get_bytesize() + s_pitch = s.get_pitch() + s_pixels = s._pixels_address + s_shifts = s.get_shifts() + s_masks = s.get_masks() + + # Find the color plane position within the pixel. + if not s_masks[plane]: + return + alpha_shift = s_shifts[plane] + offset = alpha_shift // 8 + if not self.lilendian: + offset = s_bytesize - offset - 1 + + # check the array interface structure fields. + v = s.get_view("rgba"[plane]) + if not IS_PYPY: + inter = ArrayInterface(v) + flags = PAI_ALIGNED | PAI_NOTSWAPPED | PAI_WRITEABLE + self.assertEqual(inter.two, 2) + self.assertEqual(inter.nd, 2) + self.assertEqual(inter.typekind, "u") + self.assertEqual(inter.itemsize, 1) + self.assertEqual(inter.shape[0], s_w) + self.assertEqual(inter.shape[1], s_h) + self.assertEqual(inter.strides[0], s_bytesize) + self.assertEqual(inter.strides[1], s_pitch) + self.assertEqual(inter.flags, flags) + self.assertEqual(inter.data, s_pixels + offset) + + def test_array_interface(self): + self._check_interface_2D(pygame.Surface((5, 7), 0, 8)) + self._check_interface_2D(pygame.Surface((5, 7), 0, 16)) + self._check_interface_2D(pygame.Surface((5, 7), pygame.SRCALPHA, 16)) + self._check_interface_3D(pygame.Surface((5, 7), 0, 24)) + self._check_interface_3D(pygame.Surface((8, 4), 0, 24)) # No gaps + self._check_interface_2D(pygame.Surface((5, 7), 0, 32)) + self._check_interface_3D(pygame.Surface((5, 7), 0, 32)) + self._check_interface_2D(pygame.Surface((5, 7), pygame.SRCALPHA, 32)) + self._check_interface_3D(pygame.Surface((5, 7), pygame.SRCALPHA, 32)) + + def test_array_interface_masks(self): + """Test non-default color byte orders on 3D views""" + + sz = (5, 7) + # Reversed RGB byte order + s = pygame.Surface(sz, 0, 32) + s_masks = list(s.get_masks()) + masks = [0xFF, 0xFF00, 0xFF0000] + if s_masks[0:3] == masks or s_masks[0:3] == masks[::-1]: + masks = s_masks[2::-1] + s_masks[3:4] + self._check_interface_3D(pygame.Surface(sz, 0, 32, masks)) + s = pygame.Surface(sz, 0, 24) + s_masks = list(s.get_masks()) + masks = [0xFF, 0xFF00, 0xFF0000] + if s_masks[0:3] == masks or s_masks[0:3] == masks[::-1]: + masks = s_masks[2::-1] + s_masks[3:4] + self._check_interface_3D(pygame.Surface(sz, 0, 24, masks)) + + masks = [0xFF00, 0xFF0000, 0xFF000000, 0] + self._check_interface_3D(pygame.Surface(sz, 0, 32, masks)) + + def test_array_interface_alpha(self): + for shifts in [[0, 8, 16, 24], [8, 16, 24, 0], [24, 16, 8, 0], [16, 8, 0, 24]]: + masks = [0xFF << s for s in shifts] + s = pygame.Surface((4, 2), pygame.SRCALPHA, 32, masks) + self._check_interface_rgba(s, 3) + + def test_array_interface_rgb(self): + for shifts in [[0, 8, 16, 24], [8, 16, 24, 0], [24, 16, 8, 0], [16, 8, 0, 24]]: + masks = [0xFF << s for s in shifts] + masks[3] = 0 + for plane in range(3): + s = pygame.Surface((4, 2), 0, 24) + self._check_interface_rgba(s, plane) + s = pygame.Surface((4, 2), 0, 32) + self._check_interface_rgba(s, plane) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + def test_newbuf_PyBUF_flags_bytes(self): + from pygame.tests.test_utils import buftools + + Importer = buftools.Importer + s = pygame.Surface((10, 6), 0, 32) + a = s.get_buffer() + b = Importer(a, buftools.PyBUF_SIMPLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, 1) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s._pixels_address) + b = Importer(a, buftools.PyBUF_WRITABLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertFalse(b.readonly) + b = Importer(a, buftools.PyBUF_FORMAT) + self.assertEqual(b.ndim, 0) + self.assertEqual(b.format, "B") + b = Importer(a, buftools.PyBUF_ND) + self.assertEqual(b.ndim, 1) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, 1) + self.assertEqual(b.shape, (a.length,)) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s._pixels_address) + b = Importer(a, buftools.PyBUF_STRIDES) + self.assertEqual(b.ndim, 1) + self.assertTrue(b.format is None) + self.assertEqual(b.strides, (1,)) + s2 = s.subsurface((1, 1, 7, 4)) # Not contiguous + a = s2.get_buffer() + b = Importer(a, buftools.PyBUF_SIMPLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, 1) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s2._pixels_address) + b = Importer(a, buftools.PyBUF_C_CONTIGUOUS) + self.assertEqual(b.ndim, 1) + self.assertEqual(b.strides, (1,)) + b = Importer(a, buftools.PyBUF_F_CONTIGUOUS) + self.assertEqual(b.ndim, 1) + self.assertEqual(b.strides, (1,)) + b = Importer(a, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertEqual(b.ndim, 1) + self.assertEqual(b.strides, (1,)) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + def test_newbuf_PyBUF_flags_0D(self): + # This is the same handler as used by get_buffer(), so just + # confirm that it succeeds for one case. + from pygame.tests.test_utils import buftools + + Importer = buftools.Importer + s = pygame.Surface((10, 6), 0, 32) + a = s.get_view("0") + b = Importer(a, buftools.PyBUF_SIMPLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, 1) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s._pixels_address) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + def test_newbuf_PyBUF_flags_1D(self): + from pygame.tests.test_utils import buftools + + Importer = buftools.Importer + s = pygame.Surface((10, 6), 0, 32) + a = s.get_view("1") + b = Importer(a, buftools.PyBUF_SIMPLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, s.get_bytesize()) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s._pixels_address) + b = Importer(a, buftools.PyBUF_WRITABLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertFalse(b.readonly) + b = Importer(a, buftools.PyBUF_FORMAT) + self.assertEqual(b.ndim, 0) + self.assertEqual(b.format, "=I") + b = Importer(a, buftools.PyBUF_ND) + self.assertEqual(b.ndim, 1) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, s.get_bytesize()) + self.assertEqual(b.shape, (s.get_width() * s.get_height(),)) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s._pixels_address) + b = Importer(a, buftools.PyBUF_STRIDES) + self.assertEqual(b.ndim, 1) + self.assertTrue(b.format is None) + self.assertEqual(b.strides, (s.get_bytesize(),)) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + def test_newbuf_PyBUF_flags_2D(self): + from pygame.tests.test_utils import buftools + + Importer = buftools.Importer + s = pygame.Surface((10, 6), 0, 32) + a = s.get_view("2") + # Non dimensional requests, no PyDEF_ND, are handled by the + # 1D surface buffer code, so only need to confirm a success. + b = Importer(a, buftools.PyBUF_SIMPLE) + self.assertEqual(b.ndim, 0) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, s.get_bytesize()) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s._pixels_address) + # Uniquely 2D + b = Importer(a, buftools.PyBUF_STRIDES) + self.assertEqual(b.ndim, 2) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, s.get_bytesize()) + self.assertEqual(b.shape, s.get_size()) + self.assertEqual(b.strides, (s.get_bytesize(), s.get_pitch())) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s._pixels_address) + b = Importer(a, buftools.PyBUF_RECORDS_RO) + self.assertEqual(b.ndim, 2) + self.assertEqual(b.format, "=I") + self.assertEqual(b.strides, (s.get_bytesize(), s.get_pitch())) + b = Importer(a, buftools.PyBUF_RECORDS) + self.assertEqual(b.ndim, 2) + self.assertEqual(b.format, "=I") + self.assertEqual(b.strides, (s.get_bytesize(), s.get_pitch())) + b = Importer(a, buftools.PyBUF_F_CONTIGUOUS) + self.assertEqual(b.ndim, 2) + self.assertEqual(b.format, None) + self.assertEqual(b.strides, (s.get_bytesize(), s.get_pitch())) + b = Importer(a, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertEqual(b.ndim, 2) + self.assertEqual(b.format, None) + self.assertEqual(b.strides, (s.get_bytesize(), s.get_pitch())) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + s2 = s.subsurface((1, 1, 7, 4)) # Not contiguous + a = s2.get_view("2") + b = Importer(a, buftools.PyBUF_STRIDES) + self.assertEqual(b.ndim, 2) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, s2.get_bytesize()) + self.assertEqual(b.shape, s2.get_size()) + self.assertEqual(b.strides, (s2.get_bytesize(), s.get_pitch())) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s2._pixels_address) + b = Importer(a, buftools.PyBUF_RECORDS) + self.assertEqual(b.ndim, 2) + self.assertEqual(b.format, "=I") + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_FORMAT) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_WRITABLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + def test_newbuf_PyBUF_flags_3D(self): + from pygame.tests.test_utils import buftools + + Importer = buftools.Importer + s = pygame.Surface((12, 6), 0, 24) + rmask, gmask, bmask, amask = s.get_masks() + if self.lilendian: + if rmask == 0x0000FF: + color_step = 1 + addr_offset = 0 + else: + color_step = -1 + addr_offset = 2 + else: + if rmask == 0xFF0000: + color_step = 1 + addr_offset = 0 + else: + color_step = -1 + addr_offset = 2 + a = s.get_view("3") + b = Importer(a, buftools.PyBUF_STRIDES) + w, h = s.get_size() + shape = w, h, 3 + strides = 3, s.get_pitch(), color_step + self.assertEqual(b.ndim, 3) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, 1) + self.assertEqual(b.shape, shape) + self.assertEqual(b.strides, strides) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s._pixels_address + addr_offset) + b = Importer(a, buftools.PyBUF_RECORDS_RO) + self.assertEqual(b.ndim, 3) + self.assertEqual(b.format, "B") + self.assertEqual(b.strides, strides) + b = Importer(a, buftools.PyBUF_RECORDS) + self.assertEqual(b.ndim, 3) + self.assertEqual(b.format, "B") + self.assertEqual(b.strides, strides) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_FORMAT) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_WRITABLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + def test_newbuf_PyBUF_flags_rgba(self): + # All color plane views are handled by the same routine, + # so only one plane need be checked. + from pygame.tests.test_utils import buftools + + Importer = buftools.Importer + s = pygame.Surface((12, 6), 0, 24) + rmask, gmask, bmask, amask = s.get_masks() + if self.lilendian: + if rmask == 0x0000FF: + addr_offset = 0 + else: + addr_offset = 2 + else: + if rmask == 0xFF0000: + addr_offset = 0 + else: + addr_offset = 2 + a = s.get_view("R") + b = Importer(a, buftools.PyBUF_STRIDES) + w, h = s.get_size() + shape = w, h + strides = s.get_bytesize(), s.get_pitch() + self.assertEqual(b.ndim, 2) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.length) + self.assertEqual(b.itemsize, 1) + self.assertEqual(b.shape, shape) + self.assertEqual(b.strides, strides) + self.assertTrue(b.suboffsets is None) + self.assertFalse(b.readonly) + self.assertEqual(b.buf, s._pixels_address + addr_offset) + b = Importer(a, buftools.PyBUF_RECORDS_RO) + self.assertEqual(b.ndim, 2) + self.assertEqual(b.format, "B") + self.assertEqual(b.strides, strides) + b = Importer(a, buftools.PyBUF_RECORDS) + self.assertEqual(b.ndim, 2) + self.assertEqual(b.format, "B") + self.assertEqual(b.strides, strides) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_FORMAT) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_WRITABLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS) + + +class SurfaceBlendTest(unittest.TestCase): + def setUp(self): + # Needed for 8 bits-per-pixel color palette surface tests. + pygame.display.init() + + def tearDown(self): + pygame.display.quit() + + _test_palette = [ + (0, 0, 0, 255), + (10, 30, 60, 0), + (25, 75, 100, 128), + (200, 150, 100, 200), + (0, 100, 200, 255), + ] + surf_size = (10, 12) + _test_points = [ + ((0, 0), 1), + ((4, 5), 1), + ((9, 0), 2), + ((5, 5), 2), + ((0, 11), 3), + ((4, 6), 3), + ((9, 11), 4), + ((5, 6), 4), + ] + + def _make_surface(self, bitsize, srcalpha=False, palette=None): + if palette is None: + palette = self._test_palette + flags = 0 + if srcalpha: + flags |= SRCALPHA + surf = pygame.Surface(self.surf_size, flags, bitsize) + if bitsize == 8: + surf.set_palette([c[:3] for c in palette]) + return surf + + def _fill_surface(self, surf, palette=None): + if palette is None: + palette = self._test_palette + surf.fill(palette[1], (0, 0, 5, 6)) + surf.fill(palette[2], (5, 0, 5, 6)) + surf.fill(palette[3], (0, 6, 5, 6)) + surf.fill(palette[4], (5, 6, 5, 6)) + + def _make_src_surface(self, bitsize, srcalpha=False, palette=None): + surf = self._make_surface(bitsize, srcalpha, palette) + self._fill_surface(surf, palette) + return surf + + def _assert_surface(self, surf, palette=None, msg=""): + if palette is None: + palette = self._test_palette + if surf.get_bitsize() == 16: + palette = [surf.unmap_rgb(surf.map_rgb(c)) for c in palette] + for posn, i in self._test_points: + self.assertEqual( + surf.get_at(posn), + palette[i], + "%s != %s: flags: %i, bpp: %i, posn: %s%s" + % ( + surf.get_at(posn), + palette[i], + surf.get_flags(), + surf.get_bitsize(), + posn, + msg, + ), + ) + + def test_blit_blend(self): + sources = [ + self._make_src_surface(8), + self._make_src_surface(16), + self._make_src_surface(16, srcalpha=True), + self._make_src_surface(24), + self._make_src_surface(32), + self._make_src_surface(32, srcalpha=True), + ] + destinations = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] + blend = [ + ("BLEND_ADD", (0, 25, 100, 255), lambda a, b: min(a + b, 255)), + ("BLEND_SUB", (100, 25, 0, 100), lambda a, b: max(a - b, 0)), + ("BLEND_MULT", (100, 200, 0, 0), lambda a, b: ((a * b) + 255) >> 8), + ("BLEND_MIN", (255, 0, 0, 255), min), + ("BLEND_MAX", (0, 255, 0, 255), max), + ] + + for src in sources: + src_palette = [src.unmap_rgb(src.map_rgb(c)) for c in self._test_palette] + for dst in destinations: + for blend_name, dst_color, op in blend: + dc = dst.unmap_rgb(dst.map_rgb(dst_color)) + p = [] + for sc in src_palette: + c = [op(dc[i], sc[i]) for i in range(3)] + if dst.get_masks()[3]: + c.append(dc[3]) + else: + c.append(255) + c = dst.unmap_rgb(dst.map_rgb(c)) + p.append(c) + dst.fill(dst_color) + dst.blit(src, (0, 0), special_flags=getattr(pygame, blend_name)) + self._assert_surface( + dst, + p, + ( + ", op: %s, src bpp: %i" + ", src flags: %i" + % (blend_name, src.get_bitsize(), src.get_flags()) + ), + ) + + src = self._make_src_surface(32) + masks = src.get_masks() + dst = pygame.Surface( + src.get_size(), 0, 32, [masks[2], masks[1], masks[0], masks[3]] + ) + for blend_name, dst_color, op in blend: + p = [] + for src_color in self._test_palette: + c = [op(dst_color[i], src_color[i]) for i in range(3)] + c.append(255) + p.append(tuple(c)) + dst.fill(dst_color) + dst.blit(src, (0, 0), special_flags=getattr(pygame, blend_name)) + self._assert_surface(dst, p, f", {blend_name}") + + # Blend blits are special cased for 32 to 32 bit surfaces. + # + # Confirm that it works when the rgb bytes are not the + # least significant bytes. + pat = self._make_src_surface(32) + masks = pat.get_masks() + if min(masks) == 0xFF000000: + masks = [m >> 8 for m in masks] + else: + masks = [m << 8 for m in masks] + src = pygame.Surface(pat.get_size(), 0, 32, masks) + self._fill_surface(src) + dst = pygame.Surface(src.get_size(), 0, 32, masks) + for blend_name, dst_color, op in blend: + p = [] + for src_color in self._test_palette: + c = [op(dst_color[i], src_color[i]) for i in range(3)] + c.append(255) + p.append(tuple(c)) + dst.fill(dst_color) + dst.blit(src, (0, 0), special_flags=getattr(pygame, blend_name)) + self._assert_surface(dst, p, f", {blend_name}") + + def test_blit_blend_rgba(self): + sources = [ + self._make_src_surface(8), + self._make_src_surface(16), + self._make_src_surface(16, srcalpha=True), + self._make_src_surface(24), + self._make_src_surface(32), + self._make_src_surface(32, srcalpha=True), + ] + destinations = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] + blend = [ + ("BLEND_RGBA_ADD", (0, 25, 100, 255), lambda a, b: min(a + b, 255)), + ("BLEND_RGBA_SUB", (0, 25, 100, 255), lambda a, b: max(a - b, 0)), + ("BLEND_RGBA_MULT", (0, 7, 100, 255), lambda a, b: ((a * b) + 255) >> 8), + ("BLEND_RGBA_MIN", (0, 255, 0, 255), min), + ("BLEND_RGBA_MAX", (0, 255, 0, 255), max), + ] + + for src in sources: + src_palette = [src.unmap_rgb(src.map_rgb(c)) for c in self._test_palette] + for dst in destinations: + for blend_name, dst_color, op in blend: + dc = dst.unmap_rgb(dst.map_rgb(dst_color)) + p = [] + for sc in src_palette: + c = [op(dc[i], sc[i]) for i in range(4)] + if not dst.get_masks()[3]: + c[3] = 255 + c = dst.unmap_rgb(dst.map_rgb(c)) + p.append(c) + dst.fill(dst_color) + dst.blit(src, (0, 0), special_flags=getattr(pygame, blend_name)) + self._assert_surface( + dst, + p, + ( + ", op: %s, src bpp: %i" + ", src flags: %i" + % (blend_name, src.get_bitsize(), src.get_flags()) + ), + ) + + # Blend blits are special cased for 32 to 32 bit surfaces + # with per-pixel alpha. + # + # Confirm the general case is used instead when the formats differ. + src = self._make_src_surface(32, srcalpha=True) + masks = src.get_masks() + dst = pygame.Surface( + src.get_size(), SRCALPHA, 32, (masks[2], masks[1], masks[0], masks[3]) + ) + for blend_name, dst_color, op in blend: + p = [ + tuple(op(dst_color[i], src_color[i]) for i in range(4)) + for src_color in self._test_palette + ] + dst.fill(dst_color) + dst.blit(src, (0, 0), special_flags=getattr(pygame, blend_name)) + self._assert_surface(dst, p, f", {blend_name}") + + # Confirm this special case handles subsurfaces. + src = pygame.Surface((8, 10), SRCALPHA, 32) + dst = pygame.Surface((8, 10), SRCALPHA, 32) + tst = pygame.Surface((8, 10), SRCALPHA, 32) + src.fill((1, 2, 3, 4)) + dst.fill((40, 30, 20, 10)) + subsrc = src.subsurface((2, 3, 4, 4)) + subdst = dst.subsurface((2, 3, 4, 4)) + subdst.blit(subsrc, (0, 0), special_flags=BLEND_RGBA_ADD) + tst.fill((40, 30, 20, 10)) + tst.fill((41, 32, 23, 14), (2, 3, 4, 4)) + for x in range(8): + for y in range(10): + self.assertEqual( + dst.get_at((x, y)), + tst.get_at((x, y)), + "%s != %s at (%i, %i)" + % (dst.get_at((x, y)), tst.get_at((x, y)), x, y), + ) + + def test_blit_blend_premultiplied(self): + def test_premul_surf( + src_col, + dst_col, + src_size=(16, 16), + dst_size=(16, 16), + src_bit_depth=32, + dst_bit_depth=32, + src_has_alpha=True, + dst_has_alpha=True, + ): + if src_bit_depth == 8: + src = pygame.Surface(src_size, 0, src_bit_depth) + palette = [src_col, dst_col] + src.set_palette(palette) + src.fill(palette[0]) + elif src_has_alpha: + src = pygame.Surface(src_size, SRCALPHA, src_bit_depth) + src.fill(src_col) + else: + src = pygame.Surface(src_size, 0, src_bit_depth) + src.fill(src_col) + + if dst_bit_depth == 8: + dst = pygame.Surface(dst_size, 0, dst_bit_depth) + palette = [src_col, dst_col] + dst.set_palette(palette) + dst.fill(palette[1]) + elif dst_has_alpha: + dst = pygame.Surface(dst_size, SRCALPHA, dst_bit_depth) + dst.fill(dst_col) + else: + dst = pygame.Surface(dst_size, 0, dst_bit_depth) + dst.fill(dst_col) + + dst.blit(src, (0, 0), special_flags=BLEND_PREMULTIPLIED) + + actual_col = dst.get_at( + (int(float(src_size[0] / 2.0)), int(float(src_size[0] / 2.0))) + ) + + # This is the blend pre-multiplied formula + if src_col.a == 0: + expected_col = dst_col + elif src_col.a == 255: + expected_col = src_col + else: + # sC + dC - (((dC + 1) * sA >> 8) + expected_col = pygame.Color( + (src_col.r + dst_col.r - ((dst_col.r + 1) * src_col.a >> 8)), + (src_col.g + dst_col.g - ((dst_col.g + 1) * src_col.a >> 8)), + (src_col.b + dst_col.b - ((dst_col.b + 1) * src_col.a >> 8)), + (src_col.a + dst_col.a - ((dst_col.a + 1) * src_col.a >> 8)), + ) + if not dst_has_alpha: + expected_col.a = 255 + + return (expected_col, actual_col) + + # # Colour Tests + self.assertEqual( + *test_premul_surf(pygame.Color(40, 20, 0, 51), pygame.Color(40, 20, 0, 51)) + ) + + self.assertEqual( + *test_premul_surf(pygame.Color(0, 0, 0, 0), pygame.Color(40, 20, 0, 51)) + ) + + self.assertEqual( + *test_premul_surf(pygame.Color(40, 20, 0, 51), pygame.Color(0, 0, 0, 0)) + ) + + self.assertEqual( + *test_premul_surf(pygame.Color(0, 0, 0, 0), pygame.Color(0, 0, 0, 0)) + ) + + self.assertEqual( + *test_premul_surf(pygame.Color(2, 2, 2, 2), pygame.Color(40, 20, 0, 51)) + ) + + self.assertEqual( + *test_premul_surf(pygame.Color(40, 20, 0, 51), pygame.Color(2, 2, 2, 2)) + ) + + self.assertEqual( + *test_premul_surf(pygame.Color(2, 2, 2, 2), pygame.Color(2, 2, 2, 2)) + ) + + self.assertEqual( + *test_premul_surf(pygame.Color(9, 9, 9, 9), pygame.Color(40, 20, 0, 51)) + ) + + self.assertEqual( + *test_premul_surf(pygame.Color(40, 20, 0, 51), pygame.Color(9, 9, 9, 9)) + ) + + self.assertEqual( + *test_premul_surf(pygame.Color(9, 9, 9, 9), pygame.Color(9, 9, 9, 9)) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(127, 127, 127, 127), pygame.Color(40, 20, 0, 51) + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(40, 20, 0, 51), pygame.Color(127, 127, 127, 127) + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(127, 127, 127, 127), pygame.Color(127, 127, 127, 127) + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(200, 200, 200, 200), pygame.Color(40, 20, 0, 51) + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(40, 20, 0, 51), pygame.Color(200, 200, 200, 200) + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(200, 200, 200, 200), pygame.Color(200, 200, 200, 200) + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(255, 255, 255, 255), pygame.Color(40, 20, 0, 51) + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(40, 20, 0, 51), pygame.Color(255, 255, 255, 255) + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(255, 255, 255, 255), pygame.Color(255, 255, 255, 255) + ) + ) + + # Surface format tests + self.assertRaises( + IndexError, + test_premul_surf, + pygame.Color(255, 255, 255, 255), + pygame.Color(255, 255, 255, 255), + src_size=(0, 0), + dst_size=(0, 0), + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(40, 20, 0, 51), + pygame.Color(30, 20, 0, 51), + src_size=(4, 4), + dst_size=(9, 9), + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(30, 20, 0, 51), + pygame.Color(40, 20, 0, 51), + src_size=(17, 67), + dst_size=(69, 69), + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(30, 20, 0, 255), + pygame.Color(40, 20, 0, 51), + src_size=(17, 67), + dst_size=(69, 69), + src_has_alpha=True, + ) + ) + self.assertEqual( + *test_premul_surf( + pygame.Color(30, 20, 0, 51), + pygame.Color(40, 20, 0, 255), + src_size=(17, 67), + dst_size=(69, 69), + dst_has_alpha=False, + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(30, 20, 0, 255), + pygame.Color(40, 20, 0, 255), + src_size=(17, 67), + dst_size=(69, 69), + src_has_alpha=False, + dst_has_alpha=False, + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(30, 20, 0, 255), + pygame.Color(40, 20, 0, 255), + src_size=(17, 67), + dst_size=(69, 69), + dst_bit_depth=24, + src_has_alpha=True, + dst_has_alpha=False, + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(30, 20, 0, 255), + pygame.Color(40, 20, 0, 255), + src_size=(17, 67), + dst_size=(69, 69), + src_bit_depth=24, + src_has_alpha=False, + dst_has_alpha=True, + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(30, 20, 0, 255), + pygame.Color(40, 20, 0, 255), + src_size=(17, 67), + dst_size=(69, 69), + src_bit_depth=24, + dst_bit_depth=24, + src_has_alpha=False, + dst_has_alpha=False, + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(30, 20, 0, 255), + pygame.Color(40, 20, 0, 255), + src_size=(17, 67), + dst_size=(69, 69), + src_bit_depth=8, + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(30, 20, 0, 255), + pygame.Color(40, 20, 0, 255), + src_size=(17, 67), + dst_size=(69, 69), + dst_bit_depth=8, + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(30, 20, 0, 255), + pygame.Color(40, 20, 0, 255), + src_size=(17, 67), + dst_size=(69, 69), + src_bit_depth=8, + dst_bit_depth=8, + ) + ) + + def test_blit_blend_big_rect(self): + """test that an oversized rect works ok.""" + color = (1, 2, 3, 255) + area = (1, 1, 30, 30) + s1 = pygame.Surface((4, 4), 0, 32) + r = s1.fill(special_flags=pygame.BLEND_ADD, color=color, rect=area) + + self.assertEqual(pygame.Rect((1, 1, 3, 3)), r) + self.assertEqual(s1.get_at((0, 0)), (0, 0, 0, 255)) + self.assertEqual(s1.get_at((1, 1)), color) + + black = pygame.Color("black") + red = pygame.Color("red") + self.assertNotEqual(black, red) + + surf = pygame.Surface((10, 10), 0, 32) + surf.fill(black) + subsurf = surf.subsurface(pygame.Rect(0, 1, 10, 8)) + self.assertEqual(surf.get_at((0, 0)), black) + self.assertEqual(surf.get_at((0, 9)), black) + + subsurf.fill(red, (0, -1, 10, 1), pygame.BLEND_RGB_ADD) + self.assertEqual(surf.get_at((0, 0)), black) + self.assertEqual(surf.get_at((0, 9)), black) + + subsurf.fill(red, (0, 8, 10, 1), pygame.BLEND_RGB_ADD) + self.assertEqual(surf.get_at((0, 0)), black) + self.assertEqual(surf.get_at((0, 9)), black) + + def test_GET_PIXELVALS(self): + # surface.h GET_PIXELVALS bug regarding whether of not + # a surface has per-pixel alpha. Looking at the Amask + # is not enough. The surface's SRCALPHA flag must also + # be considered. Fix rev. 1923. + src = self._make_surface(32, srcalpha=True) + src.fill((0, 0, 0, 128)) + src.set_alpha(None) # Clear SRCALPHA flag. + dst = self._make_surface(32, srcalpha=True) + dst.blit(src, (0, 0), special_flags=BLEND_RGBA_ADD) + self.assertEqual(dst.get_at((0, 0)), (0, 0, 0, 255)) + + def test_fill_blend(self): + destinations = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] + blend = [ + ("BLEND_ADD", (0, 25, 100, 255), lambda a, b: min(a + b, 255)), + ("BLEND_SUB", (0, 25, 100, 255), lambda a, b: max(a - b, 0)), + ("BLEND_MULT", (0, 7, 100, 255), lambda a, b: ((a * b) + 255) >> 8), + ("BLEND_MIN", (0, 255, 0, 255), min), + ("BLEND_MAX", (0, 255, 0, 255), max), + ] + + for dst in destinations: + dst_palette = [dst.unmap_rgb(dst.map_rgb(c)) for c in self._test_palette] + for blend_name, fill_color, op in blend: + fc = dst.unmap_rgb(dst.map_rgb(fill_color)) + self._fill_surface(dst) + p = [] + for dc in dst_palette: + c = [op(dc[i], fc[i]) for i in range(3)] + if dst.get_masks()[3]: + c.append(dc[3]) + else: + c.append(255) + c = dst.unmap_rgb(dst.map_rgb(c)) + p.append(c) + dst.fill(fill_color, special_flags=getattr(pygame, blend_name)) + self._assert_surface(dst, p, f", {blend_name}") + + def test_fill_blend_rgba(self): + destinations = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] + blend = [ + ("BLEND_RGBA_ADD", (0, 25, 100, 255), lambda a, b: min(a + b, 255)), + ("BLEND_RGBA_SUB", (0, 25, 100, 255), lambda a, b: max(a - b, 0)), + ("BLEND_RGBA_MULT", (0, 7, 100, 255), lambda a, b: ((a * b) + 255) >> 8), + ("BLEND_RGBA_MIN", (0, 255, 0, 255), min), + ("BLEND_RGBA_MAX", (0, 255, 0, 255), max), + ] + + for dst in destinations: + dst_palette = [dst.unmap_rgb(dst.map_rgb(c)) for c in self._test_palette] + for blend_name, fill_color, op in blend: + fc = dst.unmap_rgb(dst.map_rgb(fill_color)) + self._fill_surface(dst) + p = [] + for dc in dst_palette: + c = [op(dc[i], fc[i]) for i in range(4)] + if not dst.get_masks()[3]: + c[3] = 255 + c = dst.unmap_rgb(dst.map_rgb(c)) + p.append(c) + dst.fill(fill_color, special_flags=getattr(pygame, blend_name)) + self._assert_surface(dst, p, f", {blend_name}") + + def test_surface_premul_alpha(self): + """Ensure that .premul_alpha() works correctly""" + + # basic functionality at valid bit depths - 32, 16 & 8 + s1 = pygame.Surface((100, 100), pygame.SRCALPHA, 32) + s1.fill(pygame.Color(255, 255, 255, 100)) + s1_alpha = s1.premul_alpha() + self.assertEqual(s1_alpha.get_at((50, 50)), pygame.Color(100, 100, 100, 100)) + + # 16 bit colour has less precision + s2 = pygame.Surface((100, 100), pygame.SRCALPHA, 16) + s2.fill( + pygame.Color( + int(15 / 15 * 255), + int(15 / 15 * 255), + int(15 / 15 * 255), + int(10 / 15 * 255), + ) + ) + s2_alpha = s2.premul_alpha() + self.assertEqual( + s2_alpha.get_at((50, 50)), + pygame.Color( + int(10 / 15 * 255), + int(10 / 15 * 255), + int(10 / 15 * 255), + int(10 / 15 * 255), + ), + ) + + # invalid surface - we need alpha to pre-multiply + invalid_surf = pygame.Surface((100, 100), 0, 32) + invalid_surf.fill(pygame.Color(255, 255, 255, 100)) + with self.assertRaises(ValueError): + invalid_surf.premul_alpha() + + # churn a bunch of values + test_colors = [ + (200, 30, 74), + (76, 83, 24), + (184, 21, 6), + (74, 4, 74), + (76, 83, 24), + (184, 21, 234), + (160, 30, 74), + (96, 147, 204), + (198, 201, 60), + (132, 89, 74), + (245, 9, 224), + (184, 112, 6), + ] + + for r, g, b in test_colors: + for a in range(255): + with self.subTest(r=r, g=g, b=b, a=a): + surf = pygame.Surface((10, 10), pygame.SRCALPHA, 32) + surf.fill(pygame.Color(r, g, b, a)) + surf = surf.premul_alpha() + self.assertEqual( + surf.get_at((5, 5)), + Color( + ((r + 1) * a) >> 8, + ((g + 1) * a) >> 8, + ((b + 1) * a) >> 8, + a, + ), + ) + + +class SurfaceSelfBlitTest(unittest.TestCase): + """Blit to self tests. + + This test case is in response to https://github.com/pygame/pygame/issues/19 + """ + + def setUp(self): + # Needed for 8 bits-per-pixel color palette surface tests. + pygame.display.init() + + def tearDown(self): + pygame.display.quit() + + _test_palette = [(0, 0, 0, 255), (255, 0, 0, 0), (0, 255, 0, 255)] + surf_size = (9, 6) + + def _fill_surface(self, surf, palette=None): + if palette is None: + palette = self._test_palette + surf.fill(palette[1]) + surf.fill(palette[2], (1, 2, 1, 2)) + + def _make_surface(self, bitsize, srcalpha=False, palette=None): + if palette is None: + palette = self._test_palette + flags = 0 + if srcalpha: + flags |= SRCALPHA + surf = pygame.Surface(self.surf_size, flags, bitsize) + if bitsize == 8: + surf.set_palette([c[:3] for c in palette]) + self._fill_surface(surf, palette) + return surf + + def _assert_same(self, a, b): + w, h = a.get_size() + for x in range(w): + for y in range(h): + self.assertEqual( + a.get_at((x, y)), + b.get_at((x, y)), + ( + "%s != %s, bpp: %i" + % (a.get_at((x, y)), b.get_at((x, y)), a.get_bitsize()) + ), + ) + + def test_overlap_check(self): + # Ensure overlapping blits are properly detected. There are two + # places where this is done, within SoftBlitPyGame() in alphablit.c + # and PySurface_Blit() in surface.c. SoftBlitPyGame should catch the + # per-pixel alpha surface, PySurface_Blit the colorkey and blanket + # alpha surface. per-pixel alpha and blanket alpha self blits are + # not properly handled by SDL 1.2.13, so Pygame does them. + bgc = (0, 0, 0, 255) + rectc_left = (128, 64, 32, 255) + rectc_right = (255, 255, 255, 255) + colors = [(255, 255, 255, 255), (128, 64, 32, 255)] + overlaps = [ + (0, 0, 1, 0, (50, 0)), + (0, 0, 49, 1, (98, 2)), + (0, 0, 49, 49, (98, 98)), + (49, 0, 0, 1, (0, 2)), + (49, 0, 0, 49, (0, 98)), + ] + surfs = [pygame.Surface((100, 100), SRCALPHA, 32)] + surf = pygame.Surface((100, 100), 0, 32) + surf.set_alpha(255) + surfs.append(surf) + surf = pygame.Surface((100, 100), 0, 32) + surf.set_colorkey((0, 1, 0)) + surfs.append(surf) + for surf in surfs: + for s_x, s_y, d_x, d_y, test_posn in overlaps: + surf.fill(bgc) + surf.fill(rectc_right, (25, 0, 25, 50)) + surf.fill(rectc_left, (0, 0, 25, 50)) + surf.blit(surf, (d_x, d_y), (s_x, s_y, 50, 50)) + self.assertEqual(surf.get_at(test_posn), rectc_right) + + # https://github.com/pygame/pygame/issues/370#issuecomment-364625291 + @unittest.skipIf("ppc64le" in platform.uname(), "known ppc64le issue") + def test_colorkey(self): + # Check a workaround for an SDL 1.2.13 surface self-blit problem + # https://github.com/pygame/pygame/issues/19 + pygame.display.set_mode((100, 50)) # Needed for 8bit surface + bitsizes = [8, 16, 24, 32] + for bitsize in bitsizes: + surf = self._make_surface(bitsize) + surf.set_colorkey(self._test_palette[1]) + surf.blit(surf, (3, 0)) + p = [] + for c in self._test_palette: + c = surf.unmap_rgb(surf.map_rgb(c)) + p.append(c) + p[1] = (p[1][0], p[1][1], p[1][2], 0) + tmp = self._make_surface(32, srcalpha=True, palette=p) + tmp.blit(tmp, (3, 0)) + tmp.set_alpha(None) + comp = self._make_surface(bitsize) + comp.blit(tmp, (0, 0)) + self._assert_same(surf, comp) + + # https://github.com/pygame/pygame/issues/370#issuecomment-364625291 + @unittest.skipIf("ppc64le" in platform.uname(), "known ppc64le issue") + def test_blanket_alpha(self): + # Check a workaround for an SDL 1.2.13 surface self-blit problem + # https://github.com/pygame/pygame/issues/19 + pygame.display.set_mode((100, 50)) # Needed for 8bit surface + bitsizes = [8, 16, 24, 32] + for bitsize in bitsizes: + surf = self._make_surface(bitsize) + surf.set_alpha(128) + surf.blit(surf, (3, 0)) + p = [] + for c in self._test_palette: + c = surf.unmap_rgb(surf.map_rgb(c)) + p.append((c[0], c[1], c[2], 128)) + tmp = self._make_surface(32, srcalpha=True, palette=p) + tmp.blit(tmp, (3, 0)) + tmp.set_alpha(None) + comp = self._make_surface(bitsize) + comp.blit(tmp, (0, 0)) + self._assert_same(surf, comp) + + def test_pixel_alpha(self): + bitsizes = [16, 32] + for bitsize in bitsizes: + surf = self._make_surface(bitsize, srcalpha=True) + comp = self._make_surface(bitsize, srcalpha=True) + comp.blit(surf, (3, 0)) + surf.blit(surf, (3, 0)) + self._assert_same(surf, comp) + + def test_blend(self): + bitsizes = [8, 16, 24, 32] + blends = ["BLEND_ADD", "BLEND_SUB", "BLEND_MULT", "BLEND_MIN", "BLEND_MAX"] + for bitsize in bitsizes: + surf = self._make_surface(bitsize) + comp = self._make_surface(bitsize) + for blend in blends: + self._fill_surface(surf) + self._fill_surface(comp) + comp.blit(surf, (3, 0), special_flags=getattr(pygame, blend)) + surf.blit(surf, (3, 0), special_flags=getattr(pygame, blend)) + self._assert_same(surf, comp) + + def test_blend_rgba(self): + bitsizes = [16, 32] + blends = [ + "BLEND_RGBA_ADD", + "BLEND_RGBA_SUB", + "BLEND_RGBA_MULT", + "BLEND_RGBA_MIN", + "BLEND_RGBA_MAX", + ] + for bitsize in bitsizes: + surf = self._make_surface(bitsize, srcalpha=True) + comp = self._make_surface(bitsize, srcalpha=True) + for blend in blends: + self._fill_surface(surf) + self._fill_surface(comp) + comp.blit(surf, (3, 0), special_flags=getattr(pygame, blend)) + surf.blit(surf, (3, 0), special_flags=getattr(pygame, blend)) + self._assert_same(surf, comp) + + def test_subsurface(self): + # Blitting a surface to its subsurface is allowed. + surf = self._make_surface(32, srcalpha=True) + comp = surf.copy() + comp.blit(surf, (3, 0)) + sub = surf.subsurface((3, 0, 6, 6)) + sub.blit(surf, (0, 0)) + del sub + self._assert_same(surf, comp) + + # Blitting a subsurface to its owner is forbidden because of + # lock conflicts. This limitation allows the overlap check + # in PySurface_Blit of alphablit.c to be simplified. + def do_blit(d, s): + d.blit(s, (0, 0)) + + sub = surf.subsurface((1, 1, 2, 2)) + self.assertRaises(pygame.error, do_blit, surf, sub) + + def test_copy_alpha(self): + """issue 581: alpha of surface copy with SRCALPHA is set to 0.""" + surf = pygame.Surface((16, 16), pygame.SRCALPHA, 32) + self.assertEqual(surf.get_alpha(), 255) + surf2 = surf.copy() + self.assertEqual(surf2.get_alpha(), 255) + + +class SurfaceFillTest(unittest.TestCase): + def setUp(self): + pygame.display.init() + + def tearDown(self): + pygame.display.quit() + + def test_fill(self): + screen = pygame.display.set_mode((640, 480)) + + # Green and blue test pattern + screen.fill((0, 255, 0), (0, 0, 320, 240)) + screen.fill((0, 255, 0), (320, 240, 320, 240)) + screen.fill((0, 0, 255), (320, 0, 320, 240)) + screen.fill((0, 0, 255), (0, 240, 320, 240)) + + # Now apply a clip rect, such that only the left side of the + # screen should be effected by blit operations. + screen.set_clip((0, 0, 320, 480)) + + # Test fills with each special flag, and additionally without any. + screen.fill((255, 0, 0, 127), (160, 0, 320, 30), 0) + screen.fill((255, 0, 0, 127), (160, 30, 320, 30), pygame.BLEND_ADD) + screen.fill((0, 127, 127, 127), (160, 60, 320, 30), pygame.BLEND_SUB) + screen.fill((0, 63, 63, 127), (160, 90, 320, 30), pygame.BLEND_MULT) + screen.fill((0, 127, 127, 127), (160, 120, 320, 30), pygame.BLEND_MIN) + screen.fill((127, 0, 0, 127), (160, 150, 320, 30), pygame.BLEND_MAX) + screen.fill((255, 0, 0, 127), (160, 180, 320, 30), pygame.BLEND_RGBA_ADD) + screen.fill((0, 127, 127, 127), (160, 210, 320, 30), pygame.BLEND_RGBA_SUB) + screen.fill((0, 63, 63, 127), (160, 240, 320, 30), pygame.BLEND_RGBA_MULT) + screen.fill((0, 127, 127, 127), (160, 270, 320, 30), pygame.BLEND_RGBA_MIN) + screen.fill((127, 0, 0, 127), (160, 300, 320, 30), pygame.BLEND_RGBA_MAX) + screen.fill((255, 0, 0, 127), (160, 330, 320, 30), pygame.BLEND_RGB_ADD) + screen.fill((0, 127, 127, 127), (160, 360, 320, 30), pygame.BLEND_RGB_SUB) + screen.fill((0, 63, 63, 127), (160, 390, 320, 30), pygame.BLEND_RGB_MULT) + screen.fill((0, 127, 127, 127), (160, 420, 320, 30), pygame.BLEND_RGB_MIN) + screen.fill((255, 0, 0, 127), (160, 450, 320, 30), pygame.BLEND_RGB_MAX) + + # Update the display so we can see the results + pygame.display.flip() + + # Compare colors on both sides of window + for y in range(5, 480, 10): + self.assertEqual(screen.get_at((10, y)), screen.get_at((330, 480 - y))) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/surfarray_tags.py b/.venv/Lib/site-packages/pygame/tests/surfarray_tags.py new file mode 100644 index 00000000..baa535ce --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/surfarray_tags.py @@ -0,0 +1,16 @@ +__tags__ = ["array"] + +exclude = False + +try: + import numpy +except ImportError: + exclude = True +else: + try: + import pygame.pixelcopy + except ImportError: + exclude = True + +if exclude: + __tags__.extend(("ignore", "subprocess_ignore")) diff --git a/.venv/Lib/site-packages/pygame/tests/surfarray_test.py b/.venv/Lib/site-packages/pygame/tests/surfarray_test.py new file mode 100644 index 00000000..0863da77 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/surfarray_test.py @@ -0,0 +1,743 @@ +import unittest +import platform + +from numpy import ( + uint8, + uint16, + uint32, + uint64, + zeros, + float32, + float64, + alltrue, + rint, + arange, +) + +import pygame +from pygame.locals import * + +import pygame.surfarray + + +IS_PYPY = "PyPy" == platform.python_implementation() + + +@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO +class SurfarrayModuleTest(unittest.TestCase): + pixels2d = {8: True, 16: True, 24: False, 32: True} + pixels3d = {8: False, 16: False, 24: True, 32: True} + array2d = {8: True, 16: True, 24: True, 32: True} + array3d = {8: False, 16: False, 24: True, 32: True} + + test_palette = [ + (0, 0, 0, 255), + (10, 30, 60, 255), + (25, 75, 100, 255), + (100, 150, 200, 255), + (0, 100, 200, 255), + ] + surf_size = (10, 12) + test_points = [ + ((0, 0), 1), + ((4, 5), 1), + ((9, 0), 2), + ((5, 5), 2), + ((0, 11), 3), + ((4, 6), 3), + ((9, 11), 4), + ((5, 6), 4), + ] + + @classmethod + def setUpClass(cls): + # Needed for 8 bits-per-pixel color palette surface tests. + pygame.init() + + @classmethod + def tearDownClass(cls): + pygame.quit() + + def setUp(cls): + # This makes sure pygame is always initialized before each test (in + # case a test calls pygame.quit()). + if not pygame.get_init(): + pygame.init() + + def _make_surface(self, bitsize, srcalpha=False, palette=None): + if palette is None: + palette = self.test_palette + flags = 0 + if srcalpha: + flags |= SRCALPHA + surf = pygame.Surface(self.surf_size, flags, bitsize) + if bitsize == 8: + surf.set_palette([c[:3] for c in palette]) + return surf + + def _fill_surface(self, surf, palette=None): + if palette is None: + palette = self.test_palette + surf.fill(palette[1], (0, 0, 5, 6)) + surf.fill(palette[2], (5, 0, 5, 6)) + surf.fill(palette[3], (0, 6, 5, 6)) + surf.fill(palette[4], (5, 6, 5, 6)) + + def _make_src_surface(self, bitsize, srcalpha=False, palette=None): + surf = self._make_surface(bitsize, srcalpha, palette) + self._fill_surface(surf, palette) + return surf + + def _assert_surface(self, surf, palette=None, msg=""): + if palette is None: + palette = self.test_palette + if surf.get_bitsize() == 16: + palette = [surf.unmap_rgb(surf.map_rgb(c)) for c in palette] + for posn, i in self.test_points: + self.assertEqual( + surf.get_at(posn), + palette[i], + "%s != %s: flags: %i, bpp: %i, posn: %s%s" + % ( + surf.get_at(posn), + palette[i], + surf.get_flags(), + surf.get_bitsize(), + posn, + msg, + ), + ) + + def _make_array3d(self, dtype): + return zeros((self.surf_size[0], self.surf_size[1], 3), dtype) + + def _fill_array2d(self, arr, surf): + palette = self.test_palette + arr[:5, :6] = surf.map_rgb(palette[1]) + arr[5:, :6] = surf.map_rgb(palette[2]) + arr[:5, 6:] = surf.map_rgb(palette[3]) + arr[5:, 6:] = surf.map_rgb(palette[4]) + + def _fill_array3d(self, arr): + palette = self.test_palette + arr[:5, :6] = palette[1][:3] + arr[5:, :6] = palette[2][:3] + arr[:5, 6:] = palette[3][:3] + arr[5:, 6:] = palette[4][:3] + + def _make_src_array3d(self, dtype): + arr = self._make_array3d(dtype) + self._fill_array3d(arr) + return arr + + def _make_array2d(self, dtype): + return zeros(self.surf_size, dtype) + + def test_array2d(self): + sources = [ + self._make_src_surface(8), + self._make_src_surface(16), + self._make_src_surface(16, srcalpha=True), + self._make_src_surface(24), + self._make_src_surface(32), + self._make_src_surface(32, srcalpha=True), + ] + palette = self.test_palette + alpha_color = (0, 0, 0, 128) + + for surf in sources: + arr = pygame.surfarray.array2d(surf) + for posn, i in self.test_points: + self.assertEqual( + arr[posn], + surf.get_at_mapped(posn), + "%s != %s: flags: %i, bpp: %i, posn: %s" + % ( + arr[posn], + surf.get_at_mapped(posn), + surf.get_flags(), + surf.get_bitsize(), + posn, + ), + ) + + if surf.get_masks()[3]: + surf.fill(alpha_color) + arr = pygame.surfarray.array2d(surf) + posn = (0, 0) + self.assertEqual( + arr[posn], + surf.get_at_mapped(posn), + "%s != %s: bpp: %i" + % (arr[posn], surf.get_at_mapped(posn), surf.get_bitsize()), + ) + + def test_array3d(self): + sources = [ + self._make_src_surface(16), + self._make_src_surface(16, srcalpha=True), + self._make_src_surface(24), + self._make_src_surface(32), + self._make_src_surface(32, srcalpha=True), + ] + palette = self.test_palette + + for surf in sources: + arr = pygame.surfarray.array3d(surf) + + def same_color(ac, sc): + return ac[0] == sc[0] and ac[1] == sc[1] and ac[2] == sc[2] + + for posn, i in self.test_points: + self.assertTrue( + same_color(arr[posn], surf.get_at(posn)), + "%s != %s: flags: %i, bpp: %i, posn: %s" + % ( + tuple(arr[posn]), + surf.get_at(posn), + surf.get_flags(), + surf.get_bitsize(), + posn, + ), + ) + + def test_array_alpha(self): + palette = [ + (0, 0, 0, 0), + (10, 50, 100, 255), + (60, 120, 240, 130), + (64, 128, 255, 0), + (255, 128, 0, 65), + ] + targets = [ + self._make_src_surface(8, palette=palette), + self._make_src_surface(16, palette=palette), + self._make_src_surface(16, palette=palette, srcalpha=True), + self._make_src_surface(24, palette=palette), + self._make_src_surface(32, palette=palette), + self._make_src_surface(32, palette=palette, srcalpha=True), + ] + + for surf in targets: + p = palette + if surf.get_bitsize() == 16: + p = [surf.unmap_rgb(surf.map_rgb(c)) for c in p] + arr = pygame.surfarray.array_alpha(surf) + if surf.get_masks()[3]: + for (x, y), i in self.test_points: + self.assertEqual( + arr[x, y], + p[i][3], + ( + "%i != %i, posn: (%i, %i), " + "bitsize: %i" + % (arr[x, y], p[i][3], x, y, surf.get_bitsize()) + ), + ) + else: + self.assertTrue(alltrue(arr == 255)) + + # No per-pixel alpha when blanket alpha is None. + for surf in targets: + blanket_alpha = surf.get_alpha() + surf.set_alpha(None) + arr = pygame.surfarray.array_alpha(surf) + self.assertTrue( + alltrue(arr == 255), + "All alpha values should be 255 when" + " surf.set_alpha(None) has been set." + " bitsize: %i, flags: %i" % (surf.get_bitsize(), surf.get_flags()), + ) + surf.set_alpha(blanket_alpha) + + # Bug for per-pixel alpha surface when blanket alpha 0. + for surf in targets: + blanket_alpha = surf.get_alpha() + surf.set_alpha(0) + arr = pygame.surfarray.array_alpha(surf) + if surf.get_masks()[3]: + self.assertFalse( + alltrue(arr == 255), + "bitsize: %i, flags: %i" % (surf.get_bitsize(), surf.get_flags()), + ) + else: + self.assertTrue( + alltrue(arr == 255), + "bitsize: %i, flags: %i" % (surf.get_bitsize(), surf.get_flags()), + ) + surf.set_alpha(blanket_alpha) + + def test_array_colorkey(self): + palette = [ + (0, 0, 0, 0), + (10, 50, 100, 255), + (60, 120, 240, 130), + (64, 128, 255, 0), + (255, 128, 0, 65), + ] + targets = [ + self._make_src_surface(8, palette=palette), + self._make_src_surface(16, palette=palette), + self._make_src_surface(16, palette=palette, srcalpha=True), + self._make_src_surface(24, palette=palette), + self._make_src_surface(32, palette=palette), + self._make_src_surface(32, palette=palette, srcalpha=True), + ] + + for surf in targets: + p = palette + if surf.get_bitsize() == 16: + p = [surf.unmap_rgb(surf.map_rgb(c)) for c in p] + surf.set_colorkey(None) + arr = pygame.surfarray.array_colorkey(surf) + self.assertTrue(alltrue(arr == 255)) + + for i in range(1, len(palette)): + surf.set_colorkey(p[i]) + alphas = [255] * len(p) + alphas[i] = 0 + arr = pygame.surfarray.array_colorkey(surf) + for (x, y), j in self.test_points: + self.assertEqual( + arr[x, y], + alphas[j], + ( + "%i != %i, posn: (%i, %i), " + "bitsize: %i" + % (arr[x, y], alphas[j], x, y, surf.get_bitsize()) + ), + ) + + def test_array_red(self): + self._test_array_rgb("red", 0) + + def test_array_green(self): + self._test_array_rgb("green", 1) + + def test_array_blue(self): + self._test_array_rgb("blue", 2) + + def _test_array_rgb(self, operation, mask_posn): + method_name = "array_" + operation + + array_rgb = getattr(pygame.surfarray, method_name) + palette = [ + (0, 0, 0, 255), + (5, 13, 23, 255), + (29, 31, 37, 255), + (131, 157, 167, 255), + (179, 191, 251, 255), + ] + plane = [c[mask_posn] for c in palette] + + targets = [ + self._make_src_surface(24, palette=palette), + self._make_src_surface(32, palette=palette), + self._make_src_surface(32, palette=palette, srcalpha=True), + ] + + for surf in targets: + self.assertFalse(surf.get_locked()) + for (x, y), i in self.test_points: + surf.fill(palette[i]) + arr = array_rgb(surf) + self.assertEqual(arr[x, y], plane[i]) + surf.fill((100, 100, 100, 250)) + self.assertEqual(arr[x, y], plane[i]) + self.assertFalse(surf.get_locked()) + del arr + + def test_blit_array(self): + s = pygame.Surface((10, 10), 0, 24) + a = pygame.surfarray.array3d(s) + pygame.surfarray.blit_array(s, a) + + # target surfaces + targets = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] + + # source arrays + arrays3d = [] + dtypes = [(8, uint8), (16, uint16), (32, uint32)] + try: + dtypes.append((64, uint64)) + except NameError: + pass + arrays3d = [(self._make_src_array3d(dtype), None) for __, dtype in dtypes] + for bitsize in [8, 16, 24, 32]: + palette = None + if bitsize == 16: + s = pygame.Surface((1, 1), 0, 16) + palette = [s.unmap_rgb(s.map_rgb(c)) for c in self.test_palette] + if self.pixels3d[bitsize]: + surf = self._make_src_surface(bitsize) + arr = pygame.surfarray.pixels3d(surf) + arrays3d.append((arr, palette)) + if self.array3d[bitsize]: + surf = self._make_src_surface(bitsize) + arr = pygame.surfarray.array3d(surf) + arrays3d.append((arr, palette)) + for sz, dtype in dtypes: + arrays3d.append((arr.astype(dtype), palette)) + + # tests on arrays + def do_blit(surf, arr): + pygame.surfarray.blit_array(surf, arr) + + for surf in targets: + bitsize = surf.get_bitsize() + for arr, palette in arrays3d: + surf.fill((0, 0, 0, 0)) + if bitsize == 8: + self.assertRaises(ValueError, do_blit, surf, arr) + else: + pygame.surfarray.blit_array(surf, arr) + self._assert_surface(surf, palette) + + if self.pixels2d[bitsize]: + surf.fill((0, 0, 0, 0)) + s = self._make_src_surface(bitsize, surf.get_flags() & SRCALPHA) + arr = pygame.surfarray.pixels2d(s) + pygame.surfarray.blit_array(surf, arr) + self._assert_surface(surf) + + if self.array2d[bitsize]: + s = self._make_src_surface(bitsize, surf.get_flags() & SRCALPHA) + arr = pygame.surfarray.array2d(s) + for sz, dtype in dtypes: + surf.fill((0, 0, 0, 0)) + if sz >= bitsize: + pygame.surfarray.blit_array(surf, arr.astype(dtype)) + self._assert_surface(surf) + else: + self.assertRaises( + ValueError, do_blit, surf, self._make_array2d(dtype) + ) + + # Check alpha for 2D arrays + surf = self._make_surface(16, srcalpha=True) + arr = zeros(surf.get_size(), uint16) + arr[...] = surf.map_rgb((0, 128, 255, 64)) + color = surf.unmap_rgb(arr[0, 0]) + pygame.surfarray.blit_array(surf, arr) + self.assertEqual(surf.get_at((5, 5)), color) + + surf = self._make_surface(32, srcalpha=True) + arr = zeros(surf.get_size(), uint32) + color = (0, 111, 255, 63) + arr[...] = surf.map_rgb(color) + pygame.surfarray.blit_array(surf, arr) + self.assertEqual(surf.get_at((5, 5)), color) + + # Check shifts + arr3d = self._make_src_array3d(uint8) + + shift_tests = [ + (16, [12, 0, 8, 4], [0xF000, 0xF, 0xF00, 0xF0]), + (24, [16, 0, 8, 0], [0xFF0000, 0xFF, 0xFF00, 0]), + (32, [0, 16, 24, 8], [0xFF, 0xFF0000, 0xFF000000, 0xFF00]), + ] + + for bitsize, shifts, masks in shift_tests: + surf = self._make_surface(bitsize, srcalpha=(shifts[3] != 0)) + palette = None + if bitsize == 16: + palette = [surf.unmap_rgb(surf.map_rgb(c)) for c in self.test_palette] + + self.assertRaises(TypeError, surf.set_shifts, shifts) + self.assertRaises(TypeError, surf.set_masks, masks) + + # Invalid arrays + surf = pygame.Surface((1, 1), 0, 32) + t = "abcd" + self.assertRaises(ValueError, do_blit, surf, t) + + surf_size = self.surf_size + surf = pygame.Surface(surf_size, 0, 32) + arr = zeros([surf_size[0], surf_size[1] + 1, 3], uint32) + self.assertRaises(ValueError, do_blit, surf, arr) + arr = zeros([surf_size[0] + 1, surf_size[1], 3], uint32) + self.assertRaises(ValueError, do_blit, surf, arr) + + surf = pygame.Surface((1, 4), 0, 32) + arr = zeros((4,), uint32) + self.assertRaises(ValueError, do_blit, surf, arr) + arr.shape = (1, 1, 1, 4) + self.assertRaises(ValueError, do_blit, surf, arr) + + # Issue #81: round from float to int + try: + rint + except NameError: + pass + else: + surf = pygame.Surface((10, 10), pygame.SRCALPHA, 32) + w, h = surf.get_size() + length = w * h + for dtype in [float32, float64]: + surf.fill((255, 255, 255, 0)) + farr = arange(0, length, dtype=dtype) + farr.shape = w, h + pygame.surfarray.blit_array(surf, farr) + for x in range(w): + for y in range(h): + self.assertEqual( + surf.get_at_mapped((x, y)), int(rint(farr[x, y])) + ) + + # this test should be removed soon, when the function is deleted + def test_get_arraytype(self): + array_type = pygame.surfarray.get_arraytype() + + self.assertEqual(array_type, "numpy", f"unknown array type {array_type}") + + # this test should be removed soon, when the function is deleted + def test_get_arraytypes(self): + arraytypes = pygame.surfarray.get_arraytypes() + self.assertIn("numpy", arraytypes) + + for atype in arraytypes: + self.assertEqual(atype, "numpy", f"unknown array type {atype}") + + def test_make_surface(self): + # How does one properly test this with 2d arrays. It makes no sense + # since the pixel format is not entirely dependent on element size. + # Just make sure the surface pixel size is at least as large as the + # array element size I guess. + # + for bitsize, dtype in [(8, uint8), (16, uint16), (24, uint32)]: + ## Even this simple assertion fails for 2d arrays. Where's the problem? + ## surf = pygame.surfarray.make_surface(self._make_array2d(dtype)) + ## self.assertGreaterEqual(surf.get_bitsize(), bitsize, + ## "not %i >= %i)" % (surf.get_bitsize(), bitsize)) + ## + surf = pygame.surfarray.make_surface(self._make_src_array3d(dtype)) + self._assert_surface(surf) + + # Issue #81: round from float to int + try: + rint + except NameError: + pass + else: + w = 9 + h = 11 + length = w * h + for dtype in [float32, float64]: + farr = arange(0, length, dtype=dtype) + farr.shape = w, h + surf = pygame.surfarray.make_surface(farr) + for x in range(w): + for y in range(h): + self.assertEqual( + surf.get_at_mapped((x, y)), int(rint(farr[x, y])) + ) + + def test_map_array(self): + arr3d = self._make_src_array3d(uint8) + targets = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] + palette = self.test_palette + + for surf in targets: + arr2d = pygame.surfarray.map_array(surf, arr3d) + for posn, i in self.test_points: + self.assertEqual( + arr2d[posn], + surf.map_rgb(palette[i]), + "%i != %i, bitsize: %i, flags: %i" + % ( + arr2d[posn], + surf.map_rgb(palette[i]), + surf.get_bitsize(), + surf.get_flags(), + ), + ) + + # Exception checks + self.assertRaises( + ValueError, + pygame.surfarray.map_array, + self._make_surface(32), + self._make_array2d(uint8), + ) + + def test_pixels2d(self): + sources = [ + self._make_surface(8), + self._make_surface(16, srcalpha=True), + self._make_surface(32, srcalpha=True), + ] + + for surf in sources: + self.assertFalse(surf.get_locked()) + arr = pygame.surfarray.pixels2d(surf) + self.assertTrue(surf.get_locked()) + self._fill_array2d(arr, surf) + surf.unlock() + self.assertTrue(surf.get_locked()) + del arr + self.assertFalse(surf.get_locked()) + self.assertEqual(surf.get_locks(), ()) + self._assert_surface(surf) + + # Error checks + self.assertRaises(ValueError, pygame.surfarray.pixels2d, self._make_surface(24)) + + def test_pixels3d(self): + sources = [self._make_surface(24), self._make_surface(32)] + + for surf in sources: + self.assertFalse(surf.get_locked()) + arr = pygame.surfarray.pixels3d(surf) + self.assertTrue(surf.get_locked()) + self._fill_array3d(arr) + surf.unlock() + self.assertTrue(surf.get_locked()) + del arr + self.assertFalse(surf.get_locked()) + self.assertEqual(surf.get_locks(), ()) + self._assert_surface(surf) + + # Alpha check + color = (1, 2, 3, 0) + surf = self._make_surface(32, srcalpha=True) + arr = pygame.surfarray.pixels3d(surf) + arr[0, 0] = color[:3] + self.assertEqual(surf.get_at((0, 0)), color) + + # Error checks + def do_pixels3d(surf): + pygame.surfarray.pixels3d(surf) + + self.assertRaises(ValueError, do_pixels3d, self._make_surface(8)) + self.assertRaises(ValueError, do_pixels3d, self._make_surface(16)) + + def test_pixels_alpha(self): + palette = [ + (0, 0, 0, 0), + (127, 127, 127, 0), + (127, 127, 127, 85), + (127, 127, 127, 170), + (127, 127, 127, 255), + ] + alphas = [0, 45, 86, 99, 180] + + surf = self._make_src_surface(32, srcalpha=True, palette=palette) + + self.assertFalse(surf.get_locked()) + arr = pygame.surfarray.pixels_alpha(surf) + self.assertTrue(surf.get_locked()) + surf.unlock() + self.assertTrue(surf.get_locked()) + + for (x, y), i in self.test_points: + self.assertEqual(arr[x, y], palette[i][3]) + + for (x, y), i in self.test_points: + alpha = alphas[i] + arr[x, y] = alpha + color = (127, 127, 127, alpha) + self.assertEqual(surf.get_at((x, y)), color, "posn: (%i, %i)" % (x, y)) + + del arr + self.assertFalse(surf.get_locked()) + self.assertEqual(surf.get_locks(), ()) + + # Check exceptions. + def do_pixels_alpha(surf): + pygame.surfarray.pixels_alpha(surf) + + targets = [(8, False), (16, False), (16, True), (24, False), (32, False)] + + for bitsize, srcalpha in targets: + self.assertRaises( + ValueError, do_pixels_alpha, self._make_surface(bitsize, srcalpha) + ) + + def test_pixels_red(self): + self._test_pixels_rgb("red", 0) + + def test_pixels_green(self): + self._test_pixels_rgb("green", 1) + + def test_pixels_blue(self): + self._test_pixels_rgb("blue", 2) + + def _test_pixels_rgb(self, operation, mask_posn): + method_name = "pixels_" + operation + + pixels_rgb = getattr(pygame.surfarray, method_name) + palette = [ + (0, 0, 0, 255), + (5, 13, 23, 255), + (29, 31, 37, 255), + (131, 157, 167, 255), + (179, 191, 251, 255), + ] + plane = [c[mask_posn] for c in palette] + + surf24 = self._make_src_surface(24, srcalpha=False, palette=palette) + surf32 = self._make_src_surface(32, srcalpha=False, palette=palette) + surf32a = self._make_src_surface(32, srcalpha=True, palette=palette) + + for surf in [surf24, surf32, surf32a]: + self.assertFalse(surf.get_locked()) + arr = pixels_rgb(surf) + self.assertTrue(surf.get_locked()) + surf.unlock() + self.assertTrue(surf.get_locked()) + + for (x, y), i in self.test_points: + self.assertEqual(arr[x, y], plane[i]) + + del arr + self.assertFalse(surf.get_locked()) + self.assertEqual(surf.get_locks(), ()) + + # Check exceptions. + targets = [(8, False), (16, False), (16, True)] + + for bitsize, srcalpha in targets: + self.assertRaises( + ValueError, pixels_rgb, self._make_surface(bitsize, srcalpha) + ) + + def test_use_arraytype(self): + def do_use_arraytype(atype): + pygame.surfarray.use_arraytype(atype) + + pygame.surfarray.use_arraytype("numpy") + self.assertEqual(pygame.surfarray.get_arraytype(), "numpy") + self.assertRaises(ValueError, do_use_arraytype, "not an option") + + def test_surf_lock(self): + sf = pygame.Surface((5, 5), 0, 32) + for atype in pygame.surfarray.get_arraytypes(): + pygame.surfarray.use_arraytype(atype) + + ar = pygame.surfarray.pixels2d(sf) + self.assertTrue(sf.get_locked()) + + sf.unlock() + self.assertTrue(sf.get_locked()) + + del ar + self.assertFalse(sf.get_locked()) + self.assertEqual(sf.get_locks(), ()) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/surflock_test.py b/.venv/Lib/site-packages/pygame/tests/surflock_test.py new file mode 100644 index 00000000..19f354bf --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/surflock_test.py @@ -0,0 +1,144 @@ +import unittest +import sys +import platform + +import pygame + +IS_PYPY = "PyPy" == platform.python_implementation() + + +@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO +class SurfaceLockTest(unittest.TestCase): + def test_lock(self): + sf = pygame.Surface((5, 5)) + + sf.lock() + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (sf,)) + + sf.lock() + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (sf, sf)) + + sf.unlock() + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (sf,)) + + sf.unlock() + self.assertEqual(sf.get_locked(), False) + self.assertEqual(sf.get_locks(), ()) + + def test_subsurface_lock(self): + sf = pygame.Surface((5, 5)) + subsf = sf.subsurface((1, 1, 2, 2)) + sf2 = pygame.Surface((5, 5)) + + # Simple blits, nothing should happen here. + sf2.blit(subsf, (0, 0)) + sf2.blit(sf, (0, 0)) + + # Test blitting on self: + self.assertRaises(pygame.error, sf.blit, subsf, (0, 0)) + # self.assertRaises(pygame.error, subsf.blit, sf, (0, 0)) + # ^ Fails although it should not in my opinion. If I cannot + # blit the subsurface to the surface, it should not be allowed + # the other way around as well. + + # Test additional locks. + sf.lock() + sf2.blit(subsf, (0, 0)) + self.assertRaises(pygame.error, sf2.blit, sf, (0, 0)) + + subsf.lock() + self.assertRaises(pygame.error, sf2.blit, subsf, (0, 0)) + self.assertRaises(pygame.error, sf2.blit, sf, (0, 0)) + + # sf and subsf are now explicitly locked. Unlock sf, so we can + # (assume) to blit it. + # It will fail though as the subsurface still has a lock around, + # which is okay and correct behaviour. + sf.unlock() + self.assertRaises(pygame.error, sf2.blit, subsf, (0, 0)) + self.assertRaises(pygame.error, sf2.blit, sf, (0, 0)) + + # Run a second unlock on the surface. This should ideally have + # no effect as the subsurface is the locking reason! + sf.unlock() + self.assertRaises(pygame.error, sf2.blit, sf, (0, 0)) + self.assertRaises(pygame.error, sf2.blit, subsf, (0, 0)) + subsf.unlock() + + sf.lock() + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (sf,)) + self.assertEqual(subsf.get_locked(), False) + self.assertEqual(subsf.get_locks(), ()) + + subsf.lock() + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (sf, subsf)) + self.assertEqual(subsf.get_locked(), True) + self.assertEqual(subsf.get_locks(), (subsf,)) + + sf.unlock() + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (subsf,)) + self.assertEqual(subsf.get_locked(), True) + self.assertEqual(subsf.get_locks(), (subsf,)) + + subsf.unlock() + self.assertEqual(sf.get_locked(), False) + self.assertEqual(sf.get_locks(), ()) + self.assertEqual(subsf.get_locked(), False) + self.assertEqual(subsf.get_locks(), ()) + + subsf.lock() + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (subsf,)) + self.assertEqual(subsf.get_locked(), True) + self.assertEqual(subsf.get_locks(), (subsf,)) + + subsf.lock() + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (subsf, subsf)) + self.assertEqual(subsf.get_locked(), True) + self.assertEqual(subsf.get_locks(), (subsf, subsf)) + + def test_pxarray_ref(self): + sf = pygame.Surface((5, 5)) + ar = pygame.PixelArray(sf) + ar2 = pygame.PixelArray(sf) + + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (ar, ar2)) + + del ar + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (ar2,)) + + ar = ar2[:] + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (ar2,)) + + del ar + self.assertEqual(sf.get_locked(), True) + self.assertEqual(len(sf.get_locks()), 1) + + def test_buffer(self): + sf = pygame.Surface((5, 5)) + buf = sf.get_buffer() + + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (buf,)) + + sf.unlock() + self.assertEqual(sf.get_locked(), True) + self.assertEqual(sf.get_locks(), (buf,)) + + del buf + self.assertEqual(sf.get_locked(), False) + self.assertEqual(sf.get_locks(), ()) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/sysfont_test.py b/.venv/Lib/site-packages/pygame/tests/sysfont_test.py new file mode 100644 index 00000000..0ae380aa --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/sysfont_test.py @@ -0,0 +1,51 @@ +import unittest +import platform + + +class SysfontModuleTest(unittest.TestCase): + def test_create_aliases(self): + import pygame.sysfont + + pygame.sysfont.initsysfonts() + pygame.sysfont.create_aliases() + self.assertTrue(len(pygame.sysfont.Sysalias) > 0) + + def test_initsysfonts(self): + import pygame.sysfont + + pygame.sysfont.initsysfonts() + self.assertTrue(len(pygame.sysfont.get_fonts()) > 0) + + @unittest.skipIf("Darwin" not in platform.platform(), "Not mac we skip.") + def test_initsysfonts_darwin(self): + import pygame.sysfont + + self.assertTrue(len(pygame.sysfont.get_fonts()) > 10) + + def test_sysfont(self): + import pygame.font + + pygame.font.init() + arial = pygame.font.SysFont("Arial", 40) + self.assertTrue(isinstance(arial, pygame.font.Font)) + + @unittest.skipIf( + ("Darwin" in platform.platform() or "Windows" in platform.platform()), + "Not unix we skip.", + ) + def test_initsysfonts_unix(self): + import pygame.sysfont + + self.assertTrue(len(pygame.sysfont.get_fonts()) > 0) + + @unittest.skipIf("Windows" not in platform.platform(), "Not windows we skip.") + def test_initsysfonts_win32(self): + import pygame.sysfont + + self.assertTrue(len(pygame.sysfont.get_fonts()) > 10) + + +############################################################################### + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/test_utils/__init__.py b/.venv/Lib/site-packages/pygame/tests/test_utils/__init__.py new file mode 100644 index 00000000..a4994f28 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/test_utils/__init__.py @@ -0,0 +1,201 @@ +import os +import pygame +import sys +import tempfile +import time + +is_pygame_pkg = __name__.startswith("pygame.tests.") + +############################################################################### + + +def tostring(row): + """Convert row of bytes to string. Expects `row` to be an + ``array``. + """ + return row.tobytes() + + +def geterror(): + return sys.exc_info()[1] + + +############################################################################### + +this_dir = os.path.dirname(os.path.abspath(__file__)) +trunk_dir = os.path.split(os.path.split(this_dir)[0])[0] +if is_pygame_pkg: + test_module = "tests" +else: + test_module = "test" + + +def trunk_relative_path(relative): + return os.path.normpath(os.path.join(trunk_dir, relative)) + + +def fixture_path(path): + return trunk_relative_path(os.path.join(test_module, "fixtures", path)) + + +def example_path(path): + return trunk_relative_path(os.path.join("examples", path)) + + +sys.path.insert(0, trunk_relative_path(".")) + + +################################## TEMP FILES ################################# + + +def get_tmp_dir(): + return tempfile.mkdtemp() + + +############################################################################### + + +def question(q): + return input(f"\n{q.rstrip(' ')} (y/n): ").lower().strip() == "y" + + +def prompt(p): + return input(f"\n{p.rstrip(' ')} (press enter to continue): ") + + +#################################### HELPERS ################################## + + +def rgba_between(value, minimum=0, maximum=255): + if value < minimum: + return minimum + elif value > maximum: + return maximum + else: + return value + + +def combinations(seqs): + """ + + Recipe 496807 from ActiveState Python CookBook + + Non recursive technique for getting all possible combinations of a sequence + of sequences. + + """ + + r = [[]] + for x in seqs: + r = [i + [y] for y in x for i in r] + return r + + +def gradient(width, height): + """ + + Yields a pt and corresponding RGBA tuple, for every (width, height) combo. + Useful for generating gradients. + + Actual gradient may be changed, no tests rely on specific values. + + Used in transform.rotate lossless tests to generate a fixture. + + """ + + for l in range(width): + for t in range(height): + yield (l, t), tuple(map(rgba_between, (l, t, l, l + t))) + + +def rect_area_pts(rect): + for l in range(rect.left, rect.right): + for t in range(rect.top, rect.bottom): + yield l, t + + +def rect_perimeter_pts(rect): + """ + + Returns pts ((L, T) tuples) encompassing the perimeter of a rect. + + The order is clockwise: + + topleft to topright + topright to bottomright + bottomright to bottomleft + bottomleft to topleft + + Duplicate pts are not returned + + """ + clock_wise_from_top_left = ( + [(l, rect.top) for l in range(rect.left, rect.right)], + [(rect.right - 1, t) for t in range(rect.top + 1, rect.bottom)], + [(l, rect.bottom - 1) for l in range(rect.right - 2, rect.left - 1, -1)], + [(rect.left, t) for t in range(rect.bottom - 2, rect.top, -1)], + ) + + for line in clock_wise_from_top_left: + yield from line + + +def rect_outer_bounds(rect): + """ + + Returns topleft outerbound if possible and then the other pts, that are + "exclusive" bounds of the rect + + ?------O + |RECT| ?|0)uterbound + |----| + O O + + """ + return ([(rect.left - 1, rect.top)] if rect.left else []) + [ + rect.topright, + rect.bottomleft, + rect.bottomright, + ] + + +def import_submodule(module): + m = __import__(module) + for n in module.split(".")[1:]: + m = getattr(m, n) + return m + + +class SurfaceSubclass(pygame.Surface): + """A subclassed Surface to test inheritance.""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.test_attribute = True + + +def test(): + """ + + Lightweight test for helpers + + """ + + r = pygame.Rect(0, 0, 10, 10) + assert rect_outer_bounds(r) == [(10, 0), (0, 10), (10, 10)] # tr # bl # br + + assert len(list(rect_area_pts(r))) == 100 + + r = pygame.Rect(0, 0, 3, 3) + assert list(rect_perimeter_pts(r)) == [ + (0, 0), + (1, 0), + (2, 0), # tl -> tr + (2, 1), + (2, 2), # tr -> br + (1, 2), + (0, 2), # br -> bl + (0, 1), # bl -> tl + ] + + print("Tests: OK") diff --git a/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 00000000..0336fd68 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/arrinter.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/arrinter.cpython-311.pyc new file mode 100644 index 00000000..9094b2e2 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/arrinter.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/async_sub.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/async_sub.cpython-311.pyc new file mode 100644 index 00000000..45c3a02d Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/async_sub.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/buftools.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/buftools.cpython-311.pyc new file mode 100644 index 00000000..45062230 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/buftools.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/endian.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/endian.cpython-311.pyc new file mode 100644 index 00000000..eb4111d8 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/endian.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/png.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/png.cpython-311.pyc new file mode 100644 index 00000000..a802c8ae Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/png.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/run_tests.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/run_tests.cpython-311.pyc new file mode 100644 index 00000000..d61a7b6f Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/run_tests.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/test_machinery.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/test_machinery.cpython-311.pyc new file mode 100644 index 00000000..0655279f Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/test_machinery.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/test_runner.cpython-311.pyc b/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/test_runner.cpython-311.pyc new file mode 100644 index 00000000..59bafa04 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/tests/test_utils/__pycache__/test_runner.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/tests/test_utils/arrinter.py b/.venv/Lib/site-packages/pygame/tests/test_utils/arrinter.py new file mode 100644 index 00000000..626913c9 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/test_utils/arrinter.py @@ -0,0 +1,438 @@ +import sys +import ctypes +from ctypes import * +import unittest + +__all__ = [ + "PAI_CONTIGUOUS", + "PAI_FORTRAN", + "PAI_ALIGNED", + "PAI_NOTSWAPPED", + "PAI_WRITEABLE", + "PAI_ARR_HAS_DESCR", + "ArrayInterface", +] + +if sizeof(c_uint) == sizeof(c_void_p): + c_size_t = c_uint + c_ssize_t = c_int +elif sizeof(c_ulong) == sizeof(c_void_p): + c_size_t = c_ulong + c_ssize_t = c_long +elif sizeof(c_ulonglong) == sizeof(c_void_p): + c_size_t = c_ulonglong + c_ssize_t = c_longlong + + +SIZEOF_VOID_P = sizeof(c_void_p) +if SIZEOF_VOID_P <= sizeof(c_int): + Py_intptr_t = c_int +elif SIZEOF_VOID_P <= sizeof(c_long): + Py_intptr_t = c_long +elif "c_longlong" in globals() and SIZEOF_VOID_P <= sizeof(c_longlong): + Py_intptr_t = c_longlong +else: + raise RuntimeError("Unrecognized pointer size %i" % (SIZEOF_VOID_P,)) + + +class PyArrayInterface(Structure): + _fields_ = [ + ("two", c_int), + ("nd", c_int), + ("typekind", c_char), + ("itemsize", c_int), + ("flags", c_int), + ("shape", POINTER(Py_intptr_t)), + ("strides", POINTER(Py_intptr_t)), + ("data", c_void_p), + ("descr", py_object), + ] + + +PAI_Ptr = POINTER(PyArrayInterface) + +try: + PyCObject_AsVoidPtr = pythonapi.PyCObject_AsVoidPtr +except AttributeError: + + def PyCObject_AsVoidPtr(o): + raise TypeError("Not available") + +else: + PyCObject_AsVoidPtr.restype = c_void_p + PyCObject_AsVoidPtr.argtypes = [py_object] + PyCObject_GetDesc = pythonapi.PyCObject_GetDesc + PyCObject_GetDesc.restype = c_void_p + PyCObject_GetDesc.argtypes = [py_object] + +try: + PyCapsule_IsValid = pythonapi.PyCapsule_IsValid +except AttributeError: + + def PyCapsule_IsValid(capsule, name): + return 0 + +else: + PyCapsule_IsValid.restype = c_int + PyCapsule_IsValid.argtypes = [py_object, c_char_p] + PyCapsule_GetPointer = pythonapi.PyCapsule_GetPointer + PyCapsule_GetPointer.restype = c_void_p + PyCapsule_GetPointer.argtypes = [py_object, c_char_p] + PyCapsule_GetContext = pythonapi.PyCapsule_GetContext + PyCapsule_GetContext.restype = c_void_p + PyCapsule_GetContext.argtypes = [py_object] + +PyCapsule_Destructor = CFUNCTYPE(None, py_object) +PyCapsule_New = pythonapi.PyCapsule_New +PyCapsule_New.restype = py_object +PyCapsule_New.argtypes = [c_void_p, c_char_p, POINTER(PyCapsule_Destructor)] + + +def capsule_new(p): + return PyCapsule_New(addressof(p), None, None) + + +PAI_CONTIGUOUS = 0x01 +PAI_FORTRAN = 0x02 +PAI_ALIGNED = 0x100 +PAI_NOTSWAPPED = 0x200 +PAI_WRITEABLE = 0x400 +PAI_ARR_HAS_DESCR = 0x800 + + +class ArrayInterface: + def __init__(self, arr): + try: + self._cobj = arr.__array_struct__ + except AttributeError: + raise TypeError("The array object lacks an array structure") + if not self._cobj: + raise TypeError("The array object has a NULL array structure value") + try: + vp = PyCObject_AsVoidPtr(self._cobj) + except TypeError: + if PyCapsule_IsValid(self._cobj, None): + vp = PyCapsule_GetPointer(self._cobj, None) + else: + raise TypeError("The array object has an invalid array structure") + self.desc = PyCapsule_GetContext(self._cobj) + else: + self.desc = PyCObject_GetDesc(self._cobj) + self._inter = cast(vp, PAI_Ptr)[0] + + def __getattr__(self, name): + if name == "typekind": + return self._inter.typekind.decode("latin-1") + return getattr(self._inter, name) + + def __str__(self): + if isinstance(self.desc, tuple): + ver = self.desc[0] + else: + ver = "N/A" + return ( + "nd: %i\n" + "typekind: %s\n" + "itemsize: %i\n" + "flags: %s\n" + "shape: %s\n" + "strides: %s\n" + "ver: %s\n" + % ( + self.nd, + self.typekind, + self.itemsize, + format_flags(self.flags), + format_shape(self.nd, self.shape), + format_strides(self.nd, self.strides), + ver, + ) + ) + + +def format_flags(flags): + names = [] + for flag, name in [ + (PAI_CONTIGUOUS, "CONTIGUOUS"), + (PAI_FORTRAN, "FORTRAN"), + (PAI_ALIGNED, "ALIGNED"), + (PAI_NOTSWAPPED, "NOTSWAPPED"), + (PAI_WRITEABLE, "WRITEABLE"), + (PAI_ARR_HAS_DESCR, "ARR_HAS_DESCR"), + ]: + if flag & flags: + names.append(name) + return ", ".join(names) + + +def format_shape(nd, shape): + return ", ".join([str(shape[i]) for i in range(nd)]) + + +def format_strides(nd, strides): + return ", ".join([str(strides[i]) for i in range(nd)]) + + +class Exporter: + def __init__( + self, shape, typekind=None, itemsize=None, strides=None, descr=None, flags=None + ): + if typekind is None: + typekind = "u" + if itemsize is None: + itemsize = 1 + if flags is None: + flags = PAI_WRITEABLE | PAI_ALIGNED | PAI_NOTSWAPPED + if descr is not None: + flags |= PAI_ARR_HAS_DESCR + if len(typekind) != 1: + raise ValueError("Argument 'typekind' must be length 1 string") + nd = len(shape) + self.typekind = typekind + self.itemsize = itemsize + self.nd = nd + self.shape = tuple(shape) + self._shape = (c_ssize_t * self.nd)(*self.shape) + if strides is None: + self._strides = (c_ssize_t * self.nd)() + self._strides[self.nd - 1] = self.itemsize + for i in range(self.nd - 1, 0, -1): + self._strides[i - 1] = self.shape[i] * self._strides[i] + strides = tuple(self._strides) + self.strides = strides + elif len(strides) == nd: + self.strides = tuple(strides) + self._strides = (c_ssize_t * self.nd)(*self.strides) + else: + raise ValueError("Mismatch in length of strides and shape") + self.descr = descr + if self.is_contiguous("C"): + flags |= PAI_CONTIGUOUS + if self.is_contiguous("F"): + flags |= PAI_FORTRAN + self.flags = flags + sz = max(shape[i] * strides[i] for i in range(nd)) + self._data = (c_ubyte * sz)() + self.data = addressof(self._data) + self._inter = PyArrayInterface( + 2, + nd, + typekind.encode("latin_1"), + itemsize, + flags, + self._shape, + self._strides, + self.data, + descr, + ) + self.len = itemsize + for i in range(nd): + self.len *= self.shape[i] + + __array_struct__ = property(lambda self: capsule_new(self._inter)) + + def is_contiguous(self, fortran): + if fortran in "CA": + if self.strides[-1] == self.itemsize: + for i in range(self.nd - 1, 0, -1): + if self.strides[i - 1] != self.shape[i] * self.strides[i]: + break + else: + return True + if fortran in "FA": + if self.strides[0] == self.itemsize: + for i in range(0, self.nd - 1): + if self.strides[i + 1] != self.shape[i] * self.strides[i]: + break + else: + return True + return False + + +class Array(Exporter): + _ctypes = { + ("u", 1): c_uint8, + ("u", 2): c_uint16, + ("u", 4): c_uint32, + ("u", 8): c_uint64, + ("i", 1): c_int8, + ("i", 2): c_int16, + ("i", 4): c_int32, + ("i", 8): c_int64, + } + + def __init__(self, *args, **kwds): + super().__init__(*args, **kwds) + try: + if self.flags & PAI_NOTSWAPPED: + ct = self._ctypes[self.typekind, self.itemsize] + elif c_int.__ctype_le__ is c_int: + ct = self._ctypes[self.typekind, self.itemsize].__ctype_be__ + else: + ct = self._ctypes[self.typekind, self.itemsize].__ctype_le__ + except KeyError: + ct = c_uint8 * self.itemsize + self._ctype = ct + self._ctype_p = POINTER(ct) + + def __getitem__(self, key): + return cast(self._addr_at(key), self._ctype_p)[0] + + def __setitem__(self, key, value): + cast(self._addr_at(key), self._ctype_p)[0] = value + + def _addr_at(self, key): + if not isinstance(key, tuple): + key = (key,) + if len(key) != self.nd: + raise ValueError("wrong number of indexes") + for i in range(self.nd): + if not (0 <= key[i] < self.shape[i]): + raise IndexError(f"index {i} out of range") + return self.data + sum(i * s for i, s in zip(key, self.strides)) + + +class ExporterTest(unittest.TestCase): + def test_strides(self): + self.check_args(0, (10,), "u", (2,), 20, 20, 2) + self.check_args(0, (5, 3), "u", (6, 2), 30, 30, 2) + self.check_args(0, (7, 3, 5), "u", (30, 10, 2), 210, 210, 2) + self.check_args(0, (13, 5, 11, 3), "u", (330, 66, 6, 2), 4290, 4290, 2) + self.check_args(3, (7, 3, 5), "i", (2, 14, 42), 210, 210, 2) + self.check_args(3, (7, 3, 5), "x", (2, 16, 48), 210, 240, 2) + self.check_args(3, (13, 5, 11, 3), "%", (440, 88, 8, 2), 4290, 5720, 2) + self.check_args(3, (7, 5), "-", (15, 3), 105, 105, 3) + self.check_args(3, (7, 5), "*", (3, 21), 105, 105, 3) + self.check_args(3, (7, 5), " ", (3, 24), 105, 120, 3) + + def test_is_contiguous(self): + a = Exporter((10,), itemsize=2) + self.assertTrue(a.is_contiguous("C")) + self.assertTrue(a.is_contiguous("F")) + self.assertTrue(a.is_contiguous("A")) + a = Exporter((10, 4), itemsize=2) + self.assertTrue(a.is_contiguous("C")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("F")) + a = Exporter((13, 5, 11, 3), itemsize=2, strides=(330, 66, 6, 2)) + self.assertTrue(a.is_contiguous("C")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("F")) + a = Exporter((10, 4), itemsize=2, strides=(2, 20)) + self.assertTrue(a.is_contiguous("F")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("C")) + a = Exporter((13, 5, 11, 3), itemsize=2, strides=(2, 26, 130, 1430)) + self.assertTrue(a.is_contiguous("F")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("C")) + a = Exporter((2, 11, 6, 4), itemsize=2, strides=(576, 48, 8, 2)) + self.assertFalse(a.is_contiguous("A")) + a = Exporter((2, 11, 6, 4), itemsize=2, strides=(2, 4, 48, 288)) + self.assertFalse(a.is_contiguous("A")) + a = Exporter((3, 2, 2), itemsize=2, strides=(16, 8, 4)) + self.assertFalse(a.is_contiguous("A")) + a = Exporter((3, 2, 2), itemsize=2, strides=(4, 12, 24)) + self.assertFalse(a.is_contiguous("A")) + + def check_args( + self, call_flags, shape, typekind, strides, length, bufsize, itemsize, offset=0 + ): + if call_flags & 1: + typekind_arg = typekind + else: + typekind_arg = None + if call_flags & 2: + strides_arg = strides + else: + strides_arg = None + a = Exporter(shape, itemsize=itemsize, strides=strides_arg) + self.assertEqual(sizeof(a._data), bufsize) + self.assertEqual(a.data, ctypes.addressof(a._data) + offset) + m = ArrayInterface(a) + self.assertEqual(m.data, a.data) + self.assertEqual(m.itemsize, itemsize) + self.assertEqual(tuple(m.shape[0 : m.nd]), shape) + self.assertEqual(tuple(m.strides[0 : m.nd]), strides) + + +class ArrayTest(unittest.TestCase): + def __init__(self, *args, **kwds): + unittest.TestCase.__init__(self, *args, **kwds) + self.a = Array((20, 15), "i", 4) + + def setUp(self): + # Every test starts with a zeroed array. + memset(self.a.data, 0, sizeof(self.a._data)) + + def test__addr_at(self): + a = self.a + self.assertEqual(a._addr_at((0, 0)), a.data) + self.assertEqual(a._addr_at((0, 1)), a.data + 4) + self.assertEqual(a._addr_at((1, 0)), a.data + 60) + self.assertEqual(a._addr_at((1, 1)), a.data + 64) + + def test_indices(self): + a = self.a + self.assertEqual(a[0, 0], 0) + self.assertEqual(a[19, 0], 0) + self.assertEqual(a[0, 14], 0) + self.assertEqual(a[19, 14], 0) + self.assertEqual(a[5, 8], 0) + a[0, 0] = 12 + a[5, 8] = 99 + self.assertEqual(a[0, 0], 12) + self.assertEqual(a[5, 8], 99) + self.assertRaises(IndexError, a.__getitem__, (-1, 0)) + self.assertRaises(IndexError, a.__getitem__, (0, -1)) + self.assertRaises(IndexError, a.__getitem__, (20, 0)) + self.assertRaises(IndexError, a.__getitem__, (0, 15)) + self.assertRaises(ValueError, a.__getitem__, 0) + self.assertRaises(ValueError, a.__getitem__, (0, 0, 0)) + a = Array((3,), "i", 4) + a[1] = 333 + self.assertEqual(a[1], 333) + + def test_typekind(self): + a = Array((1,), "i", 4) + self.assertTrue(a._ctype is c_int32) + self.assertTrue(a._ctype_p is POINTER(c_int32)) + a = Array((1,), "u", 4) + self.assertTrue(a._ctype is c_uint32) + self.assertTrue(a._ctype_p is POINTER(c_uint32)) + a = Array((1,), "f", 4) # float types unsupported: size system dependent + ct = a._ctype + self.assertTrue(issubclass(ct, ctypes.Array)) + self.assertEqual(sizeof(ct), 4) + + def test_itemsize(self): + for size in [1, 2, 4, 8]: + a = Array((1,), "i", size) + ct = a._ctype + self.assertTrue(issubclass(ct, ctypes._SimpleCData)) + self.assertEqual(sizeof(ct), size) + + def test_oddball_itemsize(self): + for size in [3, 5, 6, 7, 9]: + a = Array((1,), "i", size) + ct = a._ctype + self.assertTrue(issubclass(ct, ctypes.Array)) + self.assertEqual(sizeof(ct), size) + + def test_byteswapped(self): + a = Array((1,), "u", 4, flags=(PAI_ALIGNED | PAI_WRITEABLE)) + ct = a._ctype + self.assertTrue(ct is not c_uint32) + if sys.byteorder == "little": + self.assertTrue(ct is c_uint32.__ctype_be__) + else: + self.assertTrue(ct is c_uint32.__ctype_le__) + i = 0xA0B0C0D + n = c_uint32(i) + a[0] = i + self.assertEqual(a[0], i) + self.assertEqual(a._data[0:4], cast(addressof(n), POINTER(c_uint8))[3:-1:-1]) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/test_utils/async_sub.py b/.venv/Lib/site-packages/pygame/tests/test_utils/async_sub.py new file mode 100644 index 00000000..560d377b --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/test_utils/async_sub.py @@ -0,0 +1,301 @@ +################################################################################ +""" + +Modification of http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440554 + +""" + +#################################### IMPORTS ################################### + +import os +import platform +import subprocess +import errno +import time +import sys +import unittest +import tempfile + + +def geterror(): + return sys.exc_info()[1] + + +null_byte = "\x00".encode("ascii") + +if platform.system() == "Windows": + + def encode(s): + return s.encode("ascii") + + def decode(b): + return b.decode("ascii") + + try: + import ctypes + from ctypes.wintypes import DWORD + + kernel32 = ctypes.windll.kernel32 + TerminateProcess = ctypes.windll.kernel32.TerminateProcess + + def WriteFile(handle, data, ol=None): + c_written = DWORD() + success = ctypes.windll.kernel32.WriteFile( + handle, + ctypes.create_string_buffer(encode(data)), + len(data), + ctypes.byref(c_written), + ol, + ) + return ctypes.windll.kernel32.GetLastError(), c_written.value + + def ReadFile(handle, desired_bytes, ol=None): + c_read = DWORD() + buffer = ctypes.create_string_buffer(desired_bytes + 1) + success = ctypes.windll.kernel32.ReadFile( + handle, buffer, desired_bytes, ctypes.byref(c_read), ol + ) + buffer[c_read.value] = null_byte + return ctypes.windll.kernel32.GetLastError(), decode(buffer.value) + + def PeekNamedPipe(handle, desired_bytes): + c_avail = DWORD() + c_message = DWORD() + if desired_bytes > 0: + c_read = DWORD() + buffer = ctypes.create_string_buffer(desired_bytes + 1) + success = ctypes.windll.kernel32.PeekNamedPipe( + handle, + buffer, + desired_bytes, + ctypes.byref(c_read), + ctypes.byref(c_avail), + ctypes.byref(c_message), + ) + buffer[c_read.value] = null_byte + return decode(buffer.value), c_avail.value, c_message.value + else: + success = ctypes.windll.kernel32.PeekNamedPipe( + handle, + None, + desired_bytes, + None, + ctypes.byref(c_avail), + ctypes.byref(c_message), + ) + return "", c_avail.value, c_message.value + + except ImportError: + from win32file import ReadFile, WriteFile + from win32pipe import PeekNamedPipe + from win32api import TerminateProcess + import msvcrt + +else: + from signal import SIGINT, SIGTERM, SIGKILL + import select + import fcntl + +################################### CONSTANTS ################################## + +PIPE = subprocess.PIPE + +################################################################################ + + +class Popen(subprocess.Popen): + def recv(self, maxsize=None): + return self._recv("stdout", maxsize) + + def recv_err(self, maxsize=None): + return self._recv("stderr", maxsize) + + def send_recv(self, input="", maxsize=None): + return self.send(input), self.recv(maxsize), self.recv_err(maxsize) + + def read_async(self, wait=0.1, e=1, tr=5, stderr=0): + if tr < 1: + tr = 1 + x = time.time() + wait + y = [] + r = "" + pr = self.recv + if stderr: + pr = self.recv_err + while time.time() < x or r: + r = pr() + if r is None: + if e: + raise Exception("Other end disconnected!") + else: + break + elif r: + y.append(r) + else: + time.sleep(max((x - time.time()) / tr, 0)) + return "".join(y) + + def send_all(self, data): + while len(data): + sent = self.send(data) + if sent is None: + raise Exception("Other end disconnected!") + data = memoryview(data, sent) + + def get_conn_maxsize(self, which, maxsize): + if maxsize is None: + maxsize = 1024 + elif maxsize < 1: + maxsize = 1 + return getattr(self, which), maxsize + + def _close(self, which): + getattr(self, which).close() + setattr(self, which, None) + + if platform.system() == "Windows": + + def kill(self): + # Recipes + # http://me.in-berlin.de/doc/python/faq/windows.html#how-do-i-emulate-os-kill-in-windows + # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/347462 + + """kill function for Win32""" + TerminateProcess(int(self._handle), 0) # returns None + + def send(self, input): + if not self.stdin: + return None + + try: + x = msvcrt.get_osfhandle(self.stdin.fileno()) + (errCode, written) = WriteFile(x, input) + except ValueError: + return self._close("stdin") + except (subprocess.pywintypes.error, Exception): + if geterror()[0] in (109, errno.ESHUTDOWN): + return self._close("stdin") + raise + + return written + + def _recv(self, which, maxsize): + conn, maxsize = self.get_conn_maxsize(which, maxsize) + if conn is None: + return None + + try: + x = msvcrt.get_osfhandle(conn.fileno()) + (read, nAvail, nMessage) = PeekNamedPipe(x, 0) + if maxsize < nAvail: + nAvail = maxsize + if nAvail > 0: + (errCode, read) = ReadFile(x, nAvail, None) + except ValueError: + return self._close(which) + except (subprocess.pywintypes.error, Exception): + if geterror()[0] in (109, errno.ESHUTDOWN): + return self._close(which) + raise + + if self.universal_newlines: + # Translate newlines. For Python 3.x assume read is text. + # If bytes then another solution is needed. + read = read.replace("\r\n", "\n").replace("\r", "\n") + return read + + else: + + def kill(self): + for i, sig in enumerate([SIGTERM, SIGKILL] * 2): + if i % 2 == 0: + os.kill(self.pid, sig) + time.sleep((i * (i % 2) / 5.0) + 0.01) + + killed_pid, stat = os.waitpid(self.pid, os.WNOHANG) + if killed_pid != 0: + return + + def send(self, input): + if not self.stdin: + return None + + if not select.select([], [self.stdin], [], 0)[1]: + return 0 + + try: + written = os.write(self.stdin.fileno(), input) + except OSError: + if geterror()[0] == errno.EPIPE: # broken pipe + return self._close("stdin") + raise + + return written + + def _recv(self, which, maxsize): + conn, maxsize = self.get_conn_maxsize(which, maxsize) + if conn is None: + return None + + if not select.select([conn], [], [], 0)[0]: + return "" + + r = conn.read(maxsize) + if not r: + return self._close(which) + + if self.universal_newlines: + r = r.replace("\r\n", "\n").replace("\r", "\n") + return r + + +################################################################################ + + +def proc_in_time_or_kill(cmd, time_out, wd=None, env=None): + proc = Popen( + cmd, + cwd=wd, + env=env, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=1, + ) + + ret_code = None + response = [] + + t = time.time() + while ret_code is None and ((time.time() - t) < time_out): + ret_code = proc.poll() + response += [proc.read_async(wait=0.1, e=0)] + + if ret_code is None: + ret_code = f'"Process timed out (time_out = {time_out} secs) ' + try: + proc.kill() + ret_code += 'and was successfully terminated"' + except Exception: + ret_code += f'and termination failed (exception: {geterror()})"' + + return ret_code, "".join(response) + + +################################################################################ + + +class AsyncTest(unittest.TestCase): + def test_proc_in_time_or_kill(self): + ret_code, response = proc_in_time_or_kill( + [sys.executable, "-c", "while True: pass"], time_out=1 + ) + + self.assertIn("rocess timed out", ret_code) + self.assertIn("successfully terminated", ret_code) + + +################################################################################ + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/test_utils/buftools.py b/.venv/Lib/site-packages/pygame/tests/test_utils/buftools.py new file mode 100644 index 00000000..19ae8905 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/test_utils/buftools.py @@ -0,0 +1,606 @@ +"""Module pygame.tests.test_utils.array + +Export the Exporter and Importer classes. + +Class Exporter has configurable shape and strides. Exporter objects +provide a convient target for unit tests on Pygame objects and functions that +import a new buffer interface. + +Class Importer imports a buffer interface with the given PyBUF_* flags. +It returns NULL Py_buffer fields as None. The shape, strides, and suboffsets +arrays are returned as tuples of ints. All Py_buffer field properties are +read-only. This class is useful in comparing exported buffer interfaces +with the actual request. The simular Python builtin memoryview currently +does not support configurable PyBUF_* flags. + +This module contains its own unit tests. When Pygame is installed, these tests +can be run with the following command line statement: + +python -m pygame.tests.test_utils.array + +""" +import pygame + +if not pygame.HAVE_NEWBUF: + emsg = "This Pygame build does not support the new buffer protocol" + raise ImportError(emsg) +import pygame.newbuffer +from pygame.newbuffer import ( + PyBUF_SIMPLE, + PyBUF_FORMAT, + PyBUF_ND, + PyBUF_WRITABLE, + PyBUF_STRIDES, + PyBUF_C_CONTIGUOUS, + PyBUF_F_CONTIGUOUS, + PyBUF_ANY_CONTIGUOUS, + PyBUF_INDIRECT, + PyBUF_STRIDED, + PyBUF_STRIDED_RO, + PyBUF_RECORDS, + PyBUF_RECORDS_RO, + PyBUF_FULL, + PyBUF_FULL_RO, + PyBUF_CONTIG, + PyBUF_CONTIG_RO, +) + +import unittest +import ctypes +import operator +from functools import reduce + +__all__ = ["Exporter", "Importer"] + +try: + ctypes.c_ssize_t +except AttributeError: + void_p_sz = ctypes.sizeof(ctypes.c_void_p) + if ctypes.sizeof(ctypes.c_short) == void_p_sz: + ctypes.c_ssize_t = ctypes.c_short + elif ctypes.sizeof(ctypes.c_int) == void_p_sz: + ctypes.c_ssize_t = ctypes.c_int + elif ctypes.sizeof(ctypes.c_long) == void_p_sz: + ctypes.c_ssize_t = ctypes.c_long + elif ctypes.sizeof(ctypes.c_longlong) == void_p_sz: + ctypes.c_ssize_t = ctypes.c_longlong + else: + raise RuntimeError("Cannot set c_ssize_t: sizeof(void *) is %i" % void_p_sz) + + +def _prop_get(fn): + return property(fn) + + +class Exporter(pygame.newbuffer.BufferMixin): + """An object that exports a multi-dimension new buffer interface + + The only array operation this type supports is to export a buffer. + """ + + prefixes = { + "@": "", + "=": "=", + "<": "=", + ">": "=", + "!": "=", + "2": "2", + "3": "3", + "4": "4", + "5": "5", + "6": "6", + "7": "7", + "8": "8", + "9": "9", + } + types = { + "c": ctypes.c_char, + "b": ctypes.c_byte, + "B": ctypes.c_ubyte, + "=c": ctypes.c_int8, + "=b": ctypes.c_int8, + "=B": ctypes.c_uint8, + "?": ctypes.c_bool, + "=?": ctypes.c_int8, + "h": ctypes.c_short, + "H": ctypes.c_ushort, + "=h": ctypes.c_int16, + "=H": ctypes.c_uint16, + "i": ctypes.c_int, + "I": ctypes.c_uint, + "=i": ctypes.c_int32, + "=I": ctypes.c_uint32, + "l": ctypes.c_long, + "L": ctypes.c_ulong, + "=l": ctypes.c_int32, + "=L": ctypes.c_uint32, + "q": ctypes.c_longlong, + "Q": ctypes.c_ulonglong, + "=q": ctypes.c_int64, + "=Q": ctypes.c_uint64, + "f": ctypes.c_float, + "d": ctypes.c_double, + "P": ctypes.c_void_p, + "x": ctypes.c_ubyte * 1, + "2x": ctypes.c_ubyte * 2, + "3x": ctypes.c_ubyte * 3, + "4x": ctypes.c_ubyte * 4, + "5x": ctypes.c_ubyte * 5, + "6x": ctypes.c_ubyte * 6, + "7x": ctypes.c_ubyte * 7, + "8x": ctypes.c_ubyte * 8, + "9x": ctypes.c_ubyte * 9, + } + + def __init__(self, shape, format=None, strides=None, readonly=None, itemsize=None): + if format is None: + format = "B" + if readonly is None: + readonly = False + prefix = "" + typecode = "" + i = 0 + if i < len(format): + try: + prefix = self.prefixes[format[i]] + i += 1 + except LookupError: + pass + if i < len(format) and format[i] == "1": + i += 1 + if i == len(format) - 1: + typecode = format[i] + if itemsize is None: + try: + itemsize = ctypes.sizeof(self.types[prefix + typecode]) + except KeyError: + raise ValueError("Unknown item format '" + format + "'") + self.readonly = bool(readonly) + self.format = format + self._format = ctypes.create_string_buffer(format.encode("latin_1")) + self.ndim = len(shape) + self.itemsize = itemsize + self.len = reduce(operator.mul, shape, 1) * self.itemsize + self.shape = tuple(shape) + self._shape = (ctypes.c_ssize_t * self.ndim)(*self.shape) + if strides is None: + self._strides = (ctypes.c_ssize_t * self.ndim)() + self._strides[self.ndim - 1] = itemsize + for i in range(self.ndim - 1, 0, -1): + self._strides[i - 1] = self.shape[i] * self._strides[i] + self.strides = tuple(self._strides) + elif len(strides) == self.ndim: + self.strides = tuple(strides) + self._strides = (ctypes.c_ssize_t * self.ndim)(*self.strides) + else: + raise ValueError("Mismatch in length of strides and shape") + buflen = max(d * abs(s) for d, s in zip(self.shape, self.strides)) + self.buflen = buflen + self._buf = (ctypes.c_ubyte * buflen)() + offset = sum( + (d - 1) * abs(s) for d, s in zip(self.shape, self.strides) if s < 0 + ) + self.buf = ctypes.addressof(self._buf) + offset + + def buffer_info(self): + return (ctypes.addressof(self.buffer), self.shape[0]) + + def tobytes(self): + return ctypes.cast(self.buffer, ctypes.POINTER(ctypes.c_char))[0 : self._len] + + def __len__(self): + return self.shape[0] + + def _get_buffer(self, view, flags): + from ctypes import addressof + + if (flags & PyBUF_WRITABLE) == PyBUF_WRITABLE and self.readonly: + raise BufferError("buffer is read-only") + if ( + flags & PyBUF_C_CONTIGUOUS + ) == PyBUF_C_CONTIGUOUS and not self.is_contiguous("C"): + raise BufferError("data is not C contiguous") + if ( + flags & PyBUF_F_CONTIGUOUS + ) == PyBUF_F_CONTIGUOUS and not self.is_contiguous("F"): + raise BufferError("data is not F contiguous") + if ( + flags & PyBUF_ANY_CONTIGUOUS + ) == PyBUF_ANY_CONTIGUOUS and not self.is_contiguous("A"): + raise BufferError("data is not contiguous") + view.buf = self.buf + view.readonly = self.readonly + view.len = self.len + if flags | PyBUF_WRITABLE == PyBUF_WRITABLE: + view.ndim = 0 + else: + view.ndim = self.ndim + view.itemsize = self.itemsize + if (flags & PyBUF_FORMAT) == PyBUF_FORMAT: + view.format = addressof(self._format) + else: + view.format = None + if (flags & PyBUF_ND) == PyBUF_ND: + view.shape = addressof(self._shape) + elif self.is_contiguous("C"): + view.shape = None + else: + raise BufferError(f"shape required for {self.ndim} dimensional data") + if (flags & PyBUF_STRIDES) == PyBUF_STRIDES: + view.strides = ctypes.addressof(self._strides) + elif view.shape is None or self.is_contiguous("C"): + view.strides = None + else: + raise BufferError("strides required for none C contiguous data") + view.suboffsets = None + view.internal = None + view.obj = self + + def is_contiguous(self, fortran): + if fortran in "CA": + if self.strides[-1] == self.itemsize: + for i in range(self.ndim - 1, 0, -1): + if self.strides[i - 1] != self.shape[i] * self.strides[i]: + break + else: + return True + if fortran in "FA": + if self.strides[0] == self.itemsize: + for i in range(0, self.ndim - 1): + if self.strides[i + 1] != self.shape[i] * self.strides[i]: + break + else: + return True + return False + + +class Importer: + """An object that imports a new buffer interface + + The fields of the Py_buffer C struct are exposed by identically + named Importer read-only properties. + """ + + def __init__(self, obj, flags): + self._view = pygame.newbuffer.Py_buffer() + self._view.get_buffer(obj, flags) + + @property + def obj(self): + """return object or None for NULL field""" + return self._view.obj + + @property + def buf(self): + """return int or None for NULL field""" + return self._view.buf + + @property + def len(self): + """return int""" + return self._view.len + + @property + def readonly(self): + """return bool""" + return self._view.readonly + + @property + def format(self): + """return bytes or None for NULL field""" + format_addr = self._view.format + if format_addr is None: + return None + return ctypes.cast(format_addr, ctypes.c_char_p).value.decode("ascii") + + @property + def itemsize(self): + """return int""" + return self._view.itemsize + + @property + def ndim(self): + """return int""" + return self._view.ndim + + @property + def shape(self): + """return int tuple or None for NULL field""" + return self._to_ssize_tuple(self._view.shape) + + @property + def strides(self): + """return int tuple or None for NULL field""" + return self._to_ssize_tuple(self._view.strides) + + @property + def suboffsets(self): + """return int tuple or None for NULL field""" + return self._to_ssize_tuple(self._view.suboffsets) + + @property + def internal(self): + """return int or None for NULL field""" + return self._view.internal + + def _to_ssize_tuple(self, addr): + from ctypes import cast, POINTER, c_ssize_t + + if addr is None: + return None + return tuple(cast(addr, POINTER(c_ssize_t))[0 : self._view.ndim]) + + +class ExporterTest(unittest.TestCase): + """Class Exporter unit tests""" + + def test_formats(self): + char_sz = ctypes.sizeof(ctypes.c_char) + short_sz = ctypes.sizeof(ctypes.c_short) + int_sz = ctypes.sizeof(ctypes.c_int) + long_sz = ctypes.sizeof(ctypes.c_long) + longlong_sz = ctypes.sizeof(ctypes.c_longlong) + float_sz = ctypes.sizeof(ctypes.c_float) + double_sz = ctypes.sizeof(ctypes.c_double) + voidp_sz = ctypes.sizeof(ctypes.c_void_p) + bool_sz = ctypes.sizeof(ctypes.c_bool) + + self.check_args(0, (1,), "B", (1,), 1, 1, 1) + self.check_args(1, (1,), "b", (1,), 1, 1, 1) + self.check_args(1, (1,), "B", (1,), 1, 1, 1) + self.check_args(1, (1,), "c", (char_sz,), char_sz, char_sz, char_sz) + self.check_args(1, (1,), "h", (short_sz,), short_sz, short_sz, short_sz) + self.check_args(1, (1,), "H", (short_sz,), short_sz, short_sz, short_sz) + self.check_args(1, (1,), "i", (int_sz,), int_sz, int_sz, int_sz) + self.check_args(1, (1,), "I", (int_sz,), int_sz, int_sz, int_sz) + self.check_args(1, (1,), "l", (long_sz,), long_sz, long_sz, long_sz) + self.check_args(1, (1,), "L", (long_sz,), long_sz, long_sz, long_sz) + self.check_args( + 1, (1,), "q", (longlong_sz,), longlong_sz, longlong_sz, longlong_sz + ) + self.check_args( + 1, (1,), "Q", (longlong_sz,), longlong_sz, longlong_sz, longlong_sz + ) + self.check_args(1, (1,), "f", (float_sz,), float_sz, float_sz, float_sz) + self.check_args(1, (1,), "d", (double_sz,), double_sz, double_sz, double_sz) + self.check_args(1, (1,), "x", (1,), 1, 1, 1) + self.check_args(1, (1,), "P", (voidp_sz,), voidp_sz, voidp_sz, voidp_sz) + self.check_args(1, (1,), "?", (bool_sz,), bool_sz, bool_sz, bool_sz) + self.check_args(1, (1,), "@b", (1,), 1, 1, 1) + self.check_args(1, (1,), "@B", (1,), 1, 1, 1) + self.check_args(1, (1,), "@c", (char_sz,), char_sz, char_sz, char_sz) + self.check_args(1, (1,), "@h", (short_sz,), short_sz, short_sz, short_sz) + self.check_args(1, (1,), "@H", (short_sz,), short_sz, short_sz, short_sz) + self.check_args(1, (1,), "@i", (int_sz,), int_sz, int_sz, int_sz) + self.check_args(1, (1,), "@I", (int_sz,), int_sz, int_sz, int_sz) + self.check_args(1, (1,), "@l", (long_sz,), long_sz, long_sz, long_sz) + self.check_args(1, (1,), "@L", (long_sz,), long_sz, long_sz, long_sz) + self.check_args( + 1, (1,), "@q", (longlong_sz,), longlong_sz, longlong_sz, longlong_sz + ) + self.check_args( + 1, (1,), "@Q", (longlong_sz,), longlong_sz, longlong_sz, longlong_sz + ) + self.check_args(1, (1,), "@f", (float_sz,), float_sz, float_sz, float_sz) + self.check_args(1, (1,), "@d", (double_sz,), double_sz, double_sz, double_sz) + self.check_args(1, (1,), "@?", (bool_sz,), bool_sz, bool_sz, bool_sz) + self.check_args(1, (1,), "=b", (1,), 1, 1, 1) + self.check_args(1, (1,), "=B", (1,), 1, 1, 1) + self.check_args(1, (1,), "=c", (1,), 1, 1, 1) + self.check_args(1, (1,), "=h", (2,), 2, 2, 2) + self.check_args(1, (1,), "=H", (2,), 2, 2, 2) + self.check_args(1, (1,), "=i", (4,), 4, 4, 4) + self.check_args(1, (1,), "=I", (4,), 4, 4, 4) + self.check_args(1, (1,), "=l", (4,), 4, 4, 4) + self.check_args(1, (1,), "=L", (4,), 4, 4, 4) + self.check_args(1, (1,), "=q", (8,), 8, 8, 8) + self.check_args(1, (1,), "=Q", (8,), 8, 8, 8) + self.check_args(1, (1,), "=?", (1,), 1, 1, 1) + self.check_args(1, (1,), "h", (2,), 2, 2, 2) + self.check_args(1, (1,), "!h", (2,), 2, 2, 2) + self.check_args(1, (1,), "q", (8,), 8, 8, 8) + self.check_args(1, (1,), "!q", (8,), 8, 8, 8) + self.check_args(1, (1,), "1x", (1,), 1, 1, 1) + self.check_args(1, (1,), "2x", (2,), 2, 2, 2) + self.check_args(1, (1,), "3x", (3,), 3, 3, 3) + self.check_args(1, (1,), "4x", (4,), 4, 4, 4) + self.check_args(1, (1,), "5x", (5,), 5, 5, 5) + self.check_args(1, (1,), "6x", (6,), 6, 6, 6) + self.check_args(1, (1,), "7x", (7,), 7, 7, 7) + self.check_args(1, (1,), "8x", (8,), 8, 8, 8) + self.check_args(1, (1,), "9x", (9,), 9, 9, 9) + self.check_args(1, (1,), "1h", (2,), 2, 2, 2) + self.check_args(1, (1,), "=1h", (2,), 2, 2, 2) + self.assertRaises(ValueError, Exporter, (2, 1), "") + self.assertRaises(ValueError, Exporter, (2, 1), "W") + self.assertRaises(ValueError, Exporter, (2, 1), "^Q") + self.assertRaises(ValueError, Exporter, (2, 1), "=W") + self.assertRaises(ValueError, Exporter, (2, 1), "=f") + self.assertRaises(ValueError, Exporter, (2, 1), "=d") + self.assertRaises(ValueError, Exporter, (2, 1), "f") + self.assertRaises(ValueError, Exporter, (2, 1), ">d") + self.assertRaises(ValueError, Exporter, (2, 1), "!f") + self.assertRaises(ValueError, Exporter, (2, 1), "!d") + self.assertRaises(ValueError, Exporter, (2, 1), "0x") + self.assertRaises(ValueError, Exporter, (2, 1), "11x") + self.assertRaises(ValueError, Exporter, (2, 1), "BB") + + def test_strides(self): + self.check_args(1, (10,), "=h", (2,), 20, 20, 2) + self.check_args(1, (5, 3), "=h", (6, 2), 30, 30, 2) + self.check_args(1, (7, 3, 5), "=h", (30, 10, 2), 210, 210, 2) + self.check_args(1, (13, 5, 11, 3), "=h", (330, 66, 6, 2), 4290, 4290, 2) + self.check_args(3, (7, 3, 5), "=h", (2, 14, 42), 210, 210, 2) + self.check_args(3, (7, 3, 5), "=h", (2, 16, 48), 210, 240, 2) + self.check_args(3, (13, 5, 11, 3), "=h", (440, 88, 8, 2), 4290, 5720, 2) + self.check_args(3, (7, 5), "3x", (15, 3), 105, 105, 3) + self.check_args(3, (7, 5), "3x", (3, 21), 105, 105, 3) + self.check_args(3, (7, 5), "3x", (3, 24), 105, 120, 3) + + def test_readonly(self): + a = Exporter((2,), "h", readonly=True) + self.assertTrue(a.readonly) + b = Importer(a, PyBUF_STRIDED_RO) + self.assertRaises(BufferError, Importer, a, PyBUF_STRIDED) + b = Importer(a, PyBUF_STRIDED_RO) + + def test_is_contiguous(self): + a = Exporter((10,), "=h") + self.assertTrue(a.is_contiguous("C")) + self.assertTrue(a.is_contiguous("F")) + self.assertTrue(a.is_contiguous("A")) + a = Exporter((10, 4), "=h") + self.assertTrue(a.is_contiguous("C")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("F")) + a = Exporter((13, 5, 11, 3), "=h", (330, 66, 6, 2)) + self.assertTrue(a.is_contiguous("C")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("F")) + a = Exporter((10, 4), "=h", (2, 20)) + self.assertTrue(a.is_contiguous("F")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("C")) + a = Exporter((13, 5, 11, 3), "=h", (2, 26, 130, 1430)) + self.assertTrue(a.is_contiguous("F")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("C")) + a = Exporter((2, 11, 6, 4), "=h", (576, 48, 8, 2)) + self.assertFalse(a.is_contiguous("A")) + a = Exporter((2, 11, 6, 4), "=h", (2, 4, 48, 288)) + self.assertFalse(a.is_contiguous("A")) + a = Exporter((3, 2, 2), "=h", (16, 8, 4)) + self.assertFalse(a.is_contiguous("A")) + a = Exporter((3, 2, 2), "=h", (4, 12, 24)) + self.assertFalse(a.is_contiguous("A")) + + def test_PyBUF_flags(self): + a = Exporter((10, 2), "d") + b = Importer(a, PyBUF_SIMPLE) + self.assertTrue(b.obj is a) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.len) + self.assertEqual(b.itemsize, a.itemsize) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertTrue(b.internal is None) + self.assertFalse(b.readonly) + b = Importer(a, PyBUF_WRITABLE) + self.assertTrue(b.obj is a) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.len) + self.assertEqual(b.itemsize, a.itemsize) + self.assertTrue(b.shape is None) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertTrue(b.internal is None) + self.assertFalse(b.readonly) + b = Importer(a, PyBUF_ND) + self.assertTrue(b.obj is a) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.len) + self.assertEqual(b.itemsize, a.itemsize) + self.assertEqual(b.shape, a.shape) + self.assertTrue(b.strides is None) + self.assertTrue(b.suboffsets is None) + self.assertTrue(b.internal is None) + self.assertFalse(b.readonly) + a = Exporter((5, 10), "=h", (24, 2)) + b = Importer(a, PyBUF_STRIDES) + self.assertTrue(b.obj is a) + self.assertTrue(b.format is None) + self.assertEqual(b.len, a.len) + self.assertEqual(b.itemsize, a.itemsize) + self.assertEqual(b.shape, a.shape) + self.assertEqual(b.strides, a.strides) + self.assertTrue(b.suboffsets is None) + self.assertTrue(b.internal is None) + self.assertFalse(b.readonly) + b = Importer(a, PyBUF_FULL) + self.assertTrue(b.obj is a) + self.assertEqual(b.format, "=h") + self.assertEqual(b.len, a.len) + self.assertEqual(b.itemsize, a.itemsize) + self.assertEqual(b.shape, a.shape) + self.assertEqual(b.strides, a.strides) + self.assertTrue(b.suboffsets is None) + self.assertTrue(b.internal is None) + self.assertFalse(b.readonly) + self.assertRaises(BufferError, Importer, a, PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, a, PyBUF_WRITABLE) + self.assertRaises(BufferError, Importer, a, PyBUF_ND) + self.assertRaises(BufferError, Importer, a, PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, PyBUF_ANY_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, PyBUF_CONTIG) + + def test_negative_strides(self): + self.check_args(3, (3, 5, 4), "B", (20, 4, -1), 60, 60, 1, 3) + self.check_args(3, (3, 5, 3), "B", (20, 4, -1), 45, 60, 1, 2) + self.check_args(3, (3, 5, 4), "B", (20, -4, 1), 60, 60, 1, 16) + self.check_args(3, (3, 5, 4), "B", (-20, -4, -1), 60, 60, 1, 59) + self.check_args(3, (3, 5, 3), "B", (-20, -4, -1), 45, 60, 1, 58) + + def test_attributes(self): + a = Exporter((13, 5, 11, 3), "=h", (440, 88, 8, 2)) + self.assertEqual(a.ndim, 4) + self.assertEqual(a.itemsize, 2) + self.assertFalse(a.readonly) + self.assertEqual(a.shape, (13, 5, 11, 3)) + self.assertEqual(a.format, "=h") + self.assertEqual(a.strides, (440, 88, 8, 2)) + self.assertEqual(a.len, 4290) + self.assertEqual(a.buflen, 5720) + self.assertEqual(a.buf, ctypes.addressof(a._buf)) + a = Exporter((8,)) + self.assertEqual(a.ndim, 1) + self.assertEqual(a.itemsize, 1) + self.assertFalse(a.readonly) + self.assertEqual(a.shape, (8,)) + self.assertEqual(a.format, "B") + self.assertTrue(isinstance(a.strides, tuple)) + self.assertEqual(a.strides, (1,)) + self.assertEqual(a.len, 8) + self.assertEqual(a.buflen, 8) + a = Exporter([13, 5, 11, 3], "=h", [440, 88, 8, 2]) + self.assertTrue(isinstance(a.shape, tuple)) + self.assertTrue(isinstance(a.strides, tuple)) + self.assertEqual(a.shape, (13, 5, 11, 3)) + self.assertEqual(a.strides, (440, 88, 8, 2)) + + def test_itemsize(self): + exp = Exporter((4, 5), format="B", itemsize=8) + imp = Importer(exp, PyBUF_RECORDS) + self.assertEqual(imp.itemsize, 8) + self.assertEqual(imp.format, "B") + self.assertEqual(imp.strides, (40, 8)) + exp = Exporter((4, 5), format="weird", itemsize=5) + imp = Importer(exp, PyBUF_RECORDS) + self.assertEqual(imp.itemsize, 5) + self.assertEqual(imp.format, "weird") + self.assertEqual(imp.strides, (25, 5)) + + def check_args( + self, call_flags, shape, format, strides, length, bufsize, itemsize, offset=0 + ): + format_arg = format if call_flags & 1 else None + strides_arg = strides if call_flags & 2 else None + a = Exporter(shape, format_arg, strides_arg) + self.assertEqual(a.buflen, bufsize) + self.assertEqual(a.buf, ctypes.addressof(a._buf) + offset) + m = Importer(a, PyBUF_RECORDS_RO) + self.assertEqual(m.buf, a.buf) + self.assertEqual(m.len, length) + self.assertEqual(m.format, format) + self.assertEqual(m.itemsize, itemsize) + self.assertEqual(m.shape, shape) + self.assertEqual(m.strides, strides) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/test_utils/endian.py b/.venv/Lib/site-packages/pygame/tests/test_utils/endian.py new file mode 100644 index 00000000..64ba1b32 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/test_utils/endian.py @@ -0,0 +1,20 @@ +# Module pygame.tests.test_utils.endian +# +# Machine independent conversion to little-endian and big-endian Python +# integer values. + +import struct + + +def little_endian_uint32(i): + """Return the 32 bit unsigned integer little-endian representation of i""" + + s = struct.pack("I", i) + return struct.unpack("=I", s)[0] diff --git a/.venv/Lib/site-packages/pygame/tests/test_utils/png.py b/.venv/Lib/site-packages/pygame/tests/test_utils/png.py new file mode 100644 index 00000000..fd781420 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/test_utils/png.py @@ -0,0 +1,4005 @@ +#!/usr/bin/env python + +# $URL: http://pypng.googlecode.com/svn/trunk/code/png.py $ +# $Rev: 228 $ + +# png.py - PNG encoder/decoder in pure Python +# +# Modified for Pygame in Oct., 2012 to work with Python 3.x. +# +# Copyright (C) 2006 Johann C. Rocholl +# Portions Copyright (C) 2009 David Jones +# And probably portions Copyright (C) 2006 Nicko van Someren +# +# Original concept by Johann C. Rocholl. +# +# LICENSE (The MIT License) +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# Changelog (recent first): +# 2009-03-11 David: interlaced bit depth < 8 (writing). +# 2009-03-10 David: interlaced bit depth < 8 (reading). +# 2009-03-04 David: Flat and Boxed pixel formats. +# 2009-02-26 David: Palette support (writing). +# 2009-02-23 David: Bit-depths < 8; better PNM support. +# 2006-06-17 Nicko: Reworked into a class, faster interlacing. +# 2006-06-17 Johann: Very simple prototype PNG decoder. +# 2006-06-17 Nicko: Test suite with various image generators. +# 2006-06-17 Nicko: Alpha-channel, grey-scale, 16-bit/plane support. +# 2006-06-15 Johann: Scanline iterator interface for large input files. +# 2006-06-09 Johann: Very simple prototype PNG encoder. + +# Incorporated into Bangai-O Development Tools by drj on 2009-02-11 from +# http://trac.browsershots.org/browser/trunk/pypng/lib/png.py?rev=2885 + +# Incorporated into pypng by drj on 2009-03-12 from +# //depot/prj/bangaio/master/code/png.py#67 + + +""" +Pure Python PNG Reader/Writer + +This Python module implements support for PNG images (see PNG +specification at http://www.w3.org/TR/2003/REC-PNG-20031110/ ). It reads +and writes PNG files with all allowable bit depths (1/2/4/8/16/24/32/48/64 +bits per pixel) and colour combinations: greyscale (1/2/4/8/16 bit); RGB, +RGBA, LA (greyscale with alpha) with 8/16 bits per channel; colour mapped +images (1/2/4/8 bit). Adam7 interlacing is supported for reading and +writing. A number of optional chunks can be specified (when writing) +and understood (when reading): ``tRNS``, ``bKGD``, ``gAMA``. + +For help, type ``import png; help(png)`` in your python interpreter. + +A good place to start is the :class:`Reader` and :class:`Writer` classes. + +This file can also be used as a command-line utility to convert +`Netpbm `_ PNM files to PNG, and the reverse conversion from PNG to +PNM. The interface is similar to that of the ``pnmtopng`` program from +Netpbm. Type ``python png.py --help`` at the shell prompt +for usage and a list of options. + +A note on spelling and terminology +---------------------------------- + +Generally British English spelling is used in the documentation. So +that's "greyscale" and "colour". This not only matches the author's +native language, it's also used by the PNG specification. + +The major colour models supported by PNG (and hence by PyPNG) are: +greyscale, RGB, greyscale--alpha, RGB--alpha. These are sometimes +referred to using the abbreviations: L, RGB, LA, RGBA. In this case +each letter abbreviates a single channel: *L* is for Luminance or Luma or +Lightness which is the channel used in greyscale images; *R*, *G*, *B* stand +for Red, Green, Blue, the components of a colour image; *A* stands for +Alpha, the opacity channel (used for transparency effects, but higher +values are more opaque, so it makes sense to call it opacity). + +A note on formats +----------------- + +When getting pixel data out of this module (reading) and presenting +data to this module (writing) there are a number of ways the data could +be represented as a Python value. Generally this module uses one of +three formats called "flat row flat pixel", "boxed row flat pixel", and +"boxed row boxed pixel". Basically the concern is whether each pixel +and each row comes in its own little tuple (box), or not. + +Consider an image that is 3 pixels wide by 2 pixels high, and each pixel +has RGB components: + +Boxed row flat pixel:: + + list([R,G,B, R,G,B, R,G,B], + [R,G,B, R,G,B, R,G,B]) + +Each row appears as its own list, but the pixels are flattened so that +three values for one pixel simply follow the three values for the previous +pixel. This is the most common format used, because it provides a good +compromise between space and convenience. PyPNG regards itself as +at liberty to replace any sequence type with any sufficiently compatible +other sequence type; in practice each row is an array (from the array +module), and the outer list is sometimes an iterator rather than an +explicit list (so that streaming is possible). + +Flat row flat pixel:: + + [R,G,B, R,G,B, R,G,B, + R,G,B, R,G,B, R,G,B] + +The entire image is one single giant sequence of colour values. +Generally an array will be used (to save space), not a list. + +Boxed row boxed pixel:: + + list([ (R,G,B), (R,G,B), (R,G,B) ], + [ (R,G,B), (R,G,B), (R,G,B) ]) + +Each row appears in its own list, but each pixel also appears in its own +tuple. A serious memory burn in Python. + +In all cases the top row comes first, and for each row the pixels are +ordered from left-to-right. Within a pixel the values appear in the +order, R-G-B-A (or L-A for greyscale--alpha). + +There is a fourth format, mentioned because it is used internally, +is close to what lies inside a PNG file itself, and has some support +from the public API. This format is called packed. When packed, +each row is a sequence of bytes (integers from 0 to 255), just as +it is before PNG scanline filtering is applied. When the bit depth +is 8 this is essentially the same as boxed row flat pixel; when the +bit depth is less than 8, several pixels are packed into each byte; +when the bit depth is 16 (the only value more than 8 that is supported +by the PNG image format) each pixel value is decomposed into 2 bytes +(and `packed` is a misnomer). This format is used by the +:meth:`Writer.write_packed` method. It isn't usually a convenient +format, but may be just right if the source data for the PNG image +comes from something that uses a similar format (for example, 1-bit +BMPs, or another PNG file). + +And now, my famous members +-------------------------- +""" + +__version__ = "$URL: http://pypng.googlecode.com/svn/trunk/code/png.py $ $Rev: 228 $" + +import io +import itertools +import math +import operator +import struct +import sys +import zlib +import warnings +from array import array +from functools import reduce + +from pygame.tests.test_utils import tostring + +__all__ = ["Image", "Reader", "Writer", "write_chunks", "from_array"] + + +# The PNG signature. +# http://www.w3.org/TR/PNG/#5PNG-file-signature +_signature = struct.pack("8B", 137, 80, 78, 71, 13, 10, 26, 10) + +_adam7 = ( + (0, 0, 8, 8), + (4, 0, 8, 8), + (0, 4, 4, 8), + (2, 0, 4, 4), + (0, 2, 2, 4), + (1, 0, 2, 2), + (0, 1, 1, 2), +) + + +def group(s, n): + # See + # http://www.python.org/doc/2.6/library/functions.html#zip + return zip(*[iter(s)] * n) + + +def isarray(x): + """Same as ``isinstance(x, array)``.""" + return isinstance(x, array) + + +# Conditionally convert to bytes. Works on Python 2 and Python 3. +try: + bytes("", "ascii") + + def strtobytes(x): + return bytes(x, "iso8859-1") + + def bytestostr(x): + return str(x, "iso8859-1") + +except: + strtobytes = str + bytestostr = str + + +def interleave_planes(ipixels, apixels, ipsize, apsize): + """ + Interleave (colour) planes, e.g. RGB + A = RGBA. + + Return an array of pixels consisting of the `ipsize` elements of data + from each pixel in `ipixels` followed by the `apsize` elements of data + from each pixel in `apixels`. Conventionally `ipixels` and + `apixels` are byte arrays so the sizes are bytes, but it actually + works with any arrays of the same type. The returned array is the + same type as the input arrays which should be the same type as each other. + """ + + itotal = len(ipixels) + atotal = len(apixels) + newtotal = itotal + atotal + newpsize = ipsize + apsize + # Set up the output buffer + # See http://www.python.org/doc/2.4.4/lib/module-array.html#l2h-1356 + out = array(ipixels.typecode) + # It's annoying that there is no cheap way to set the array size :-( + out.extend(ipixels) + out.extend(apixels) + # Interleave in the pixel data + for i in range(ipsize): + out[i:newtotal:newpsize] = ipixels[i:itotal:ipsize] + for i in range(apsize): + out[i + ipsize : newtotal : newpsize] = apixels[i:atotal:apsize] + return out + + +def check_palette(palette): + """Check a palette argument (to the :class:`Writer` class) for validity. + Returns the palette as a list if okay; raises an exception otherwise. + """ + + # None is the default and is allowed. + if palette is None: + return None + + p = list(palette) + if not (0 < len(p) <= 256): + raise ValueError("a palette must have between 1 and 256 entries") + seen_triple = False + for i, t in enumerate(p): + if len(t) not in (3, 4): + raise ValueError("palette entry %d: entries must be 3- or 4-tuples." % i) + if len(t) == 3: + seen_triple = True + if seen_triple and len(t) == 4: + raise ValueError( + "palette entry %d: all 4-tuples must precede all 3-tuples" % i + ) + for x in t: + if int(x) != x or not (0 <= x <= 255): + raise ValueError( + "palette entry %d: values must be integer: 0 <= x <= 255" % i + ) + return p + + +class Error(Exception): + prefix = "Error" + + def __str__(self): + return f'{self.prefix}: {" ".join(self.args)}' + + +class FormatError(Error): + """Problem with input file format. In other words, PNG file does + not conform to the specification in some way and is invalid. + """ + + prefix = "FormatError" + + +class ChunkError(FormatError): + prefix = "ChunkError" + + +class Writer: + """ + PNG encoder in pure Python. + """ + + def __init__( + self, + width=None, + height=None, + size=None, + greyscale=False, + alpha=False, + bitdepth=8, + palette=None, + transparent=None, + background=None, + gamma=None, + compression=None, + interlace=False, + bytes_per_sample=None, # deprecated + planes=None, + colormap=None, + maxval=None, + chunk_limit=2**20, + ): + """ + Create a PNG encoder object. + + Arguments: + + width, height + Image size in pixels, as two separate arguments. + size + Image size (w,h) in pixels, as single argument. + greyscale + Input data is greyscale, not RGB. + alpha + Input data has alpha channel (RGBA or LA). + bitdepth + Bit depth: from 1 to 16. + palette + Create a palette for a colour mapped image (colour type 3). + transparent + Specify a transparent colour (create a ``tRNS`` chunk). + background + Specify a default background colour (create a ``bKGD`` chunk). + gamma + Specify a gamma value (create a ``gAMA`` chunk). + compression + zlib compression level (1-9). + interlace + Create an interlaced image. + chunk_limit + Write multiple ``IDAT`` chunks to save memory. + + The image size (in pixels) can be specified either by using the + `width` and `height` arguments, or with the single `size` + argument. If `size` is used it should be a pair (*width*, + *height*). + + `greyscale` and `alpha` are booleans that specify whether + an image is greyscale (or colour), and whether it has an + alpha channel (or not). + + `bitdepth` specifies the bit depth of the source pixel values. + Each source pixel value must be an integer between 0 and + ``2**bitdepth-1``. For example, 8-bit images have values + between 0 and 255. PNG only stores images with bit depths of + 1,2,4,8, or 16. When `bitdepth` is not one of these values, + the next highest valid bit depth is selected, and an ``sBIT`` + (significant bits) chunk is generated that specifies the original + precision of the source image. In this case the supplied pixel + values will be rescaled to fit the range of the selected bit depth. + + The details of which bit depth / colour model combinations the + PNG file format supports directly, are somewhat arcane + (refer to the PNG specification for full details). Briefly: + "small" bit depths (1,2,4) are only allowed with greyscale and + colour mapped images; colour mapped images cannot have bit depth + 16. + + For colour mapped images (in other words, when the `palette` + argument is specified) the `bitdepth` argument must match one of + the valid PNG bit depths: 1, 2, 4, or 8. (It is valid to have a + PNG image with a palette and an ``sBIT`` chunk, but the meaning + is slightly different; it would be awkward to press the + `bitdepth` argument into service for this.) + + The `palette` option, when specified, causes a colour mapped image + to be created: the PNG colour type is set to 3; greyscale + must not be set; alpha must not be set; transparent must + not be set; the bit depth must be 1,2,4, or 8. When a colour + mapped image is created, the pixel values are palette indexes + and the `bitdepth` argument specifies the size of these indexes + (not the size of the colour values in the palette). + + The palette argument value should be a sequence of 3- or + 4-tuples. 3-tuples specify RGB palette entries; 4-tuples + specify RGBA palette entries. If both 4-tuples and 3-tuples + appear in the sequence then all the 4-tuples must come + before all the 3-tuples. A ``PLTE`` chunk is created; if there + are 4-tuples then a ``tRNS`` chunk is created as well. The + ``PLTE`` chunk will contain all the RGB triples in the same + sequence; the ``tRNS`` chunk will contain the alpha channel for + all the 4-tuples, in the same sequence. Palette entries + are always 8-bit. + + If specified, the `transparent` and `background` parameters must + be a tuple with three integer values for red, green, blue, or + a simple integer (or singleton tuple) for a greyscale image. + + If specified, the `gamma` parameter must be a positive number + (generally, a float). A ``gAMA`` chunk will be created. Note that + this will not change the values of the pixels as they appear in + the PNG file, they are assumed to have already been converted + appropriately for the gamma specified. + + The `compression` argument specifies the compression level + to be used by the ``zlib`` module. Higher values are likely + to compress better, but will be slower to compress. The + default for this argument is ``None``; this does not mean + no compression, rather it means that the default from the + ``zlib`` module is used (which is generally acceptable). + + If `interlace` is true then an interlaced image is created + (using PNG's so far only interlace method, *Adam7*). This does not + affect how the pixels should be presented to the encoder, rather + it changes how they are arranged into the PNG file. On slow + connexions interlaced images can be partially decoded by the + browser to give a rough view of the image that is successively + refined as more image data appears. + + .. note :: + + Enabling the `interlace` option requires the entire image + to be processed in working memory. + + `chunk_limit` is used to limit the amount of memory used whilst + compressing the image. In order to avoid using large amounts of + memory, multiple ``IDAT`` chunks may be created. + """ + + # At the moment the `planes` argument is ignored; + # its purpose is to act as a dummy so that + # ``Writer(x, y, **info)`` works, where `info` is a dictionary + # returned by Reader.read and friends. + # Ditto for `colormap`. + + # A couple of helper functions come first. Best skipped if you + # are reading through. + + def isinteger(x): + try: + return int(x) == x + except: + return False + + def check_color(c, which): + """Checks that a colour argument for transparent or + background options is the right form. Also "corrects" bare + integers to 1-tuples. + """ + + if c is None: + return c + if greyscale: + try: + l = len(c) + except TypeError: + c = (c,) + if len(c) != 1: + raise ValueError(f"{which} for greyscale must be 1-tuple") + if not isinteger(c[0]): + raise ValueError(f"{which} colour for greyscale must be integer") + else: + if not ( + len(c) == 3 + and isinteger(c[0]) + and isinteger(c[1]) + and isinteger(c[2]) + ): + raise ValueError(f"{which} colour must be a triple of integers") + return c + + if size: + if len(size) != 2: + raise ValueError("size argument should be a pair (width, height)") + if width is not None and width != size[0]: + raise ValueError( + "size[0] (%r) and width (%r) should match when both are used." + % (size[0], width) + ) + if height is not None and height != size[1]: + raise ValueError( + "size[1] (%r) and height (%r) should match when both are used." + % (size[1], height) + ) + width, height = size + del size + + if width <= 0 or height <= 0: + raise ValueError("width and height must be greater than zero") + if not isinteger(width) or not isinteger(height): + raise ValueError("width and height must be integers") + # http://www.w3.org/TR/PNG/#7Integers-and-byte-order + if width > 2**32 - 1 or height > 2**32 - 1: + raise ValueError("width and height cannot exceed 2**32-1") + + if alpha and transparent is not None: + raise ValueError("transparent colour not allowed with alpha channel") + + if bytes_per_sample is not None: + warnings.warn( + "please use bitdepth instead of bytes_per_sample", DeprecationWarning + ) + if bytes_per_sample not in (0.125, 0.25, 0.5, 1, 2): + raise ValueError("bytes per sample must be .125, .25, .5, 1, or 2") + bitdepth = int(8 * bytes_per_sample) + del bytes_per_sample + if not isinteger(bitdepth) or bitdepth < 1 or 16 < bitdepth: + raise ValueError( + f"bitdepth ({bitdepth!r}) must be a positive integer <= 16" + ) + + self.rescale = None + if palette: + if bitdepth not in (1, 2, 4, 8): + raise ValueError("with palette, bitdepth must be 1, 2, 4, or 8") + if transparent is not None: + raise ValueError("transparent and palette not compatible") + if alpha: + raise ValueError("alpha and palette not compatible") + if greyscale: + raise ValueError("greyscale and palette not compatible") + else: + # No palette, check for sBIT chunk generation. + if alpha or not greyscale: + if bitdepth not in (8, 16): + targetbitdepth = (8, 16)[bitdepth > 8] + self.rescale = (bitdepth, targetbitdepth) + bitdepth = targetbitdepth + del targetbitdepth + else: + assert greyscale + assert not alpha + if bitdepth not in (1, 2, 4, 8, 16): + if bitdepth > 8: + targetbitdepth = 16 + elif bitdepth == 3: + targetbitdepth = 4 + else: + assert bitdepth in (5, 6, 7) + targetbitdepth = 8 + self.rescale = (bitdepth, targetbitdepth) + bitdepth = targetbitdepth + del targetbitdepth + + if bitdepth < 8 and (alpha or not greyscale and not palette): + raise ValueError("bitdepth < 8 only permitted with greyscale or palette") + if bitdepth > 8 and palette: + raise ValueError("bit depth must be 8 or less for images with palette") + + transparent = check_color(transparent, "transparent") + background = check_color(background, "background") + + # It's important that the true boolean values (greyscale, alpha, + # colormap, interlace) are converted to bool because Iverson's + # convention is relied upon later on. + self.width = width + self.height = height + self.transparent = transparent + self.background = background + self.gamma = gamma + self.greyscale = bool(greyscale) + self.alpha = bool(alpha) + self.colormap = bool(palette) + self.bitdepth = int(bitdepth) + self.compression = compression + self.chunk_limit = chunk_limit + self.interlace = bool(interlace) + self.palette = check_palette(palette) + + self.color_type = 4 * self.alpha + 2 * (not greyscale) + 1 * self.colormap + assert self.color_type in (0, 2, 3, 4, 6) + + self.color_planes = (3, 1)[self.greyscale or self.colormap] + self.planes = self.color_planes + self.alpha + # :todo: fix for bitdepth < 8 + self.psize = (self.bitdepth / 8) * self.planes + + def make_palette(self): + """Create the byte sequences for a ``PLTE`` and if necessary a + ``tRNS`` chunk. Returned as a pair (*p*, *t*). *t* will be + ``None`` if no ``tRNS`` chunk is necessary. + """ + + p = array("B") + t = array("B") + + for x in self.palette: + p.extend(x[0:3]) + if len(x) > 3: + t.append(x[3]) + p = tostring(p) + t = tostring(t) + if t: + return p, t + return p, None + + def write(self, outfile, rows): + """Write a PNG image to the output file. `rows` should be + an iterable that yields each row in boxed row flat pixel format. + The rows should be the rows of the original image, so there + should be ``self.height`` rows of ``self.width * self.planes`` values. + If `interlace` is specified (when creating the instance), then + an interlaced PNG file will be written. Supply the rows in the + normal image order; the interlacing is carried out internally. + + .. note :: + + Interlacing will require the entire image to be in working memory. + """ + + if self.interlace: + fmt = "BH"[self.bitdepth > 8] + a = array(fmt, itertools.chain(*rows)) + return self.write_array(outfile, a) + else: + nrows = self.write_passes(outfile, rows) + if nrows != self.height: + raise ValueError( + "rows supplied (%d) does not match height (%d)" + % (nrows, self.height) + ) + + def write_passes(self, outfile, rows, packed=False): + """ + Write a PNG image to the output file. + + Most users are expected to find the :meth:`write` or + :meth:`write_array` method more convenient. + + The rows should be given to this method in the order that + they appear in the output file. For straightlaced images, + this is the usual top to bottom ordering, but for interlaced + images the rows should have already been interlaced before + passing them to this function. + + `rows` should be an iterable that yields each row. When + `packed` is ``False`` the rows should be in boxed row flat pixel + format; when `packed` is ``True`` each row should be a packed + sequence of bytes. + + """ + + # http://www.w3.org/TR/PNG/#5PNG-file-signature + outfile.write(_signature) + + # http://www.w3.org/TR/PNG/#11IHDR + write_chunk( + outfile, + "IHDR", + struct.pack( + "!2I5B", + self.width, + self.height, + self.bitdepth, + self.color_type, + 0, + 0, + self.interlace, + ), + ) + + # See :chunk:order + # http://www.w3.org/TR/PNG/#11gAMA + if self.gamma is not None: + write_chunk( + outfile, "gAMA", struct.pack("!L", int(round(self.gamma * 1e5))) + ) + + # See :chunk:order + # http://www.w3.org/TR/PNG/#11sBIT + if self.rescale: + write_chunk( + outfile, + "sBIT", + struct.pack("%dB" % self.planes, *[self.rescale[0]] * self.planes), + ) + + # :chunk:order: Without a palette (PLTE chunk), ordering is + # relatively relaxed. With one, gAMA chunk must precede PLTE + # chunk which must precede tRNS and bKGD. + # See http://www.w3.org/TR/PNG/#5ChunkOrdering + if self.palette: + p, t = self.make_palette() + write_chunk(outfile, "PLTE", p) + if t: + # tRNS chunk is optional. Only needed if palette entries + # have alpha. + write_chunk(outfile, "tRNS", t) + + # http://www.w3.org/TR/PNG/#11tRNS + if self.transparent is not None: + if self.greyscale: + write_chunk(outfile, "tRNS", struct.pack("!1H", *self.transparent)) + else: + write_chunk(outfile, "tRNS", struct.pack("!3H", *self.transparent)) + + # http://www.w3.org/TR/PNG/#11bKGD + if self.background is not None: + if self.greyscale: + write_chunk(outfile, "bKGD", struct.pack("!1H", *self.background)) + else: + write_chunk(outfile, "bKGD", struct.pack("!3H", *self.background)) + + # http://www.w3.org/TR/PNG/#11IDAT + if self.compression is not None: + compressor = zlib.compressobj(self.compression) + else: + compressor = zlib.compressobj() + + # Choose an extend function based on the bitdepth. The extend + # function packs/decomposes the pixel values into bytes and + # stuffs them onto the data array. + data = array("B") + if self.bitdepth == 8 or packed: + extend = data.extend + elif self.bitdepth == 16: + # Decompose into bytes + def extend(sl): + fmt = f"!{len(sl)}H" + data.extend(array("B", struct.pack(fmt, *sl))) + + else: + # Pack into bytes + assert self.bitdepth < 8 + # samples per byte + spb = int(8 / self.bitdepth) + + def extend(sl): + a = array("B", sl) + # Adding padding bytes so we can group into a whole + # number of spb-tuples. + l = float(len(a)) + extra = math.ceil(l / float(spb)) * spb - l + a.extend([0] * int(extra)) + # Pack into bytes + l = group(a, spb) + l = map(lambda e: reduce(lambda x, y: (x << self.bitdepth) + y, e), l) + data.extend(l) + + if self.rescale: + oldextend = extend + factor = float(2 ** self.rescale[1] - 1) / float(2 ** self.rescale[0] - 1) + + def extend(sl): + oldextend(map(lambda x: int(round(factor * x)), sl)) + + # Build the first row, testing mostly to see if we need to + # changed the extend function to cope with NumPy integer types + # (they cause our ordinary definition of extend to fail, so we + # wrap it). See + # http://code.google.com/p/pypng/issues/detail?id=44 + enumrows = enumerate(rows) + del rows + + # First row's filter type. + data.append(0) + # :todo: Certain exceptions in the call to ``.next()`` or the + # following try would indicate no row data supplied. + # Should catch. + i, row = next(enumrows) + try: + # If this fails... + extend(row) + except: + # ... try a version that converts the values to int first. + # Not only does this work for the (slightly broken) NumPy + # types, there are probably lots of other, unknown, "nearly" + # int types it works for. + def wrapmapint(f): + return lambda sl: f(map(int, sl)) + + extend = wrapmapint(extend) + del wrapmapint + extend(row) + + for i, row in enumrows: + # Add "None" filter type. Currently, it's essential that + # this filter type be used for every scanline as we do not + # mark the first row of a reduced pass image; that means we + # could accidentally compute the wrong filtered scanline if + # we used "up", "average", or "paeth" on such a line. + data.append(0) + extend(row) + if len(data) > self.chunk_limit: + compressed = compressor.compress(tostring(data)) + if len(compressed): + # print(len(data), len(compressed), file= >> sys.stderr) + write_chunk(outfile, "IDAT", compressed) + # Because of our very witty definition of ``extend``, + # above, we must re-use the same ``data`` object. Hence + # we use ``del`` to empty this one, rather than create a + # fresh one (which would be my natural FP instinct). + del data[:] + if len(data): + compressed = compressor.compress(tostring(data)) + else: + compressed = "" + flushed = compressor.flush() + if len(compressed) or len(flushed): + # print(len(data), len(compressed), len(flushed), file=sys.stderr) + write_chunk(outfile, "IDAT", compressed + flushed) + # http://www.w3.org/TR/PNG/#11IEND + write_chunk(outfile, "IEND") + return i + 1 + + def write_array(self, outfile, pixels): + """ + Write an array in flat row flat pixel format as a PNG file on + the output file. See also :meth:`write` method. + """ + + if self.interlace: + self.write_passes(outfile, self.array_scanlines_interlace(pixels)) + else: + self.write_passes(outfile, self.array_scanlines(pixels)) + + def write_packed(self, outfile, rows): + """ + Write PNG file to `outfile`. The pixel data comes from `rows` + which should be in boxed row packed format. Each row should be + a sequence of packed bytes. + + Technically, this method does work for interlaced images but it + is best avoided. For interlaced images, the rows should be + presented in the order that they appear in the file. + + This method should not be used when the source image bit depth + is not one naturally supported by PNG; the bit depth should be + 1, 2, 4, 8, or 16. + """ + + if self.rescale: + raise Error( + "write_packed method not suitable for bit depth %d" % self.rescale[0] + ) + return self.write_passes(outfile, rows, packed=True) + + def convert_pnm(self, infile, outfile): + """ + Convert a PNM file containing raw pixel data into a PNG file + with the parameters set in the writer object. Works for + (binary) PGM, PPM, and PAM formats. + """ + + if self.interlace: + pixels = array("B") + pixels.fromfile( + infile, + (self.bitdepth / 8) * self.color_planes * self.width * self.height, + ) + self.write_passes(outfile, self.array_scanlines_interlace(pixels)) + else: + self.write_passes(outfile, self.file_scanlines(infile)) + + def convert_ppm_and_pgm(self, ppmfile, pgmfile, outfile): + """ + Convert a PPM and PGM file containing raw pixel data into a + PNG outfile with the parameters set in the writer object. + """ + pixels = array("B") + pixels.fromfile( + ppmfile, (self.bitdepth / 8) * self.color_planes * self.width * self.height + ) + apixels = array("B") + apixels.fromfile(pgmfile, (self.bitdepth / 8) * self.width * self.height) + pixels = interleave_planes( + pixels, + apixels, + (self.bitdepth / 8) * self.color_planes, + (self.bitdepth / 8), + ) + if self.interlace: + self.write_passes(outfile, self.array_scanlines_interlace(pixels)) + else: + self.write_passes(outfile, self.array_scanlines(pixels)) + + def file_scanlines(self, infile): + """ + Generates boxed rows in flat pixel format, from the input file + `infile`. It assumes that the input file is in a "Netpbm-like" + binary format, and is positioned at the beginning of the first + pixel. The number of pixels to read is taken from the image + dimensions (`width`, `height`, `planes`) and the number of bytes + per value is implied by the image `bitdepth`. + """ + + # Values per row + vpr = self.width * self.planes + row_bytes = vpr + if self.bitdepth > 8: + assert self.bitdepth == 16 + row_bytes *= 2 + fmt = ">%dH" % vpr + + def line(): + return array("H", struct.unpack(fmt, infile.read(row_bytes))) + + else: + + def line(): + scanline = array("B", infile.read(row_bytes)) + return scanline + + for y in range(self.height): + yield line() + + def array_scanlines(self, pixels): + """ + Generates boxed rows (flat pixels) from flat rows (flat pixels) + in an array. + """ + + # Values per row + vpr = self.width * self.planes + stop = 0 + for y in range(self.height): + start = stop + stop = start + vpr + yield pixels[start:stop] + + def array_scanlines_interlace(self, pixels): + """ + Generator for interlaced scanlines from an array. `pixels` is + the full source image in flat row flat pixel format. The + generator yields each scanline of the reduced passes in turn, in + boxed row flat pixel format. + """ + + # http://www.w3.org/TR/PNG/#8InterlaceMethods + # Array type. + fmt = "BH"[self.bitdepth > 8] + # Value per row + vpr = self.width * self.planes + for xstart, ystart, xstep, ystep in _adam7: + if xstart >= self.width: + continue + # Pixels per row (of reduced image) + ppr = int(math.ceil((self.width - xstart) / float(xstep))) + # number of values in reduced image row. + row_len = ppr * self.planes + for y in range(ystart, self.height, ystep): + if xstep == 1: + offset = y * vpr + yield pixels[offset : offset + vpr] + else: + row = array(fmt) + # There's no easier way to set the length of an array + row.extend(pixels[0:row_len]) + offset = y * vpr + xstart * self.planes + end_offset = (y + 1) * vpr + skip = self.planes * xstep + for i in range(self.planes): + row[i :: self.planes] = pixels[offset + i : end_offset : skip] + yield row + + +def write_chunk(outfile, tag, data=strtobytes("")): + """ + Write a PNG chunk to the output file, including length and + checksum. + """ + + # http://www.w3.org/TR/PNG/#5Chunk-layout + outfile.write(struct.pack("!I", len(data))) + tag = strtobytes(tag) + outfile.write(tag) + outfile.write(data) + checksum = zlib.crc32(tag) + checksum = zlib.crc32(data, checksum) + checksum &= 2**32 - 1 + outfile.write(struct.pack("!I", checksum)) + + +def write_chunks(out, chunks): + """Create a PNG file by writing out the chunks.""" + + out.write(_signature) + for chunk in chunks: + write_chunk(out, *chunk) + + +def filter_scanline(type, line, fo, prev=None): + """Apply a scanline filter to a scanline. `type` specifies the + filter type (0 to 4); `line` specifies the current (unfiltered) + scanline as a sequence of bytes; `prev` specifies the previous + (unfiltered) scanline as a sequence of bytes. `fo` specifies the + filter offset; normally this is size of a pixel in bytes (the number + of bytes per sample times the number of channels), but when this is + < 1 (for bit depths < 8) then the filter offset is 1. + """ + + assert 0 <= type < 5 + + # The output array. Which, pathetically, we extend one-byte at a + # time (fortunately this is linear). + out = array("B", [type]) + + def sub(): + ai = -fo + for x in line: + if ai >= 0: + x = (x - line[ai]) & 0xFF + out.append(x) + ai += 1 + + def up(): + for i, x in enumerate(line): + x = (x - prev[i]) & 0xFF + out.append(x) + + def average(): + ai = -fo + for i, x in enumerate(line): + if ai >= 0: + x = (x - ((line[ai] + prev[i]) >> 1)) & 0xFF + else: + x = (x - (prev[i] >> 1)) & 0xFF + out.append(x) + ai += 1 + + def paeth(): + # http://www.w3.org/TR/PNG/#9Filter-type-4-Paeth + ai = -fo # also used for ci + for i, x in enumerate(line): + a = 0 + b = prev[i] + c = 0 + + if ai >= 0: + a = line[ai] + c = prev[ai] + p = a + b - c + pa = abs(p - a) + pb = abs(p - b) + pc = abs(p - c) + if pa <= pb and pa <= pc: + Pr = a + elif pb <= pc: + Pr = b + else: + Pr = c + + x = (x - Pr) & 0xFF + out.append(x) + ai += 1 + + if not prev: + # We're on the first line. Some of the filters can be reduced + # to simpler cases which makes handling the line "off the top" + # of the image simpler. "up" becomes "none"; "paeth" becomes + # "left" (non-trivial, but true). "average" needs to be handled + # specially. + if type == 2: # "up" + return line # type = 0 + elif type == 3: + prev = [0] * len(line) + elif type == 4: # "paeth" + type = 1 + if type == 0: + out.extend(line) + elif type == 1: + sub() + elif type == 2: + up() + elif type == 3: + average() + else: # type == 4 + paeth() + return out + + +def from_array(a, mode=None, info={}): + """Create a PNG :class:`Image` object from a 2- or 3-dimensional array. + One application of this function is easy PIL-style saving: + ``png.from_array(pixels, 'L').save('foo.png')``. + + .. note : + + The use of the term *3-dimensional* is for marketing purposes + only. It doesn't actually work. Please bear with us. Meanwhile + enjoy the complimentary snacks (on request) and please use a + 2-dimensional array. + + Unless they are specified using the *info* parameter, the PNG's + height and width are taken from the array size. For a 3 dimensional + array the first axis is the height; the second axis is the width; + and the third axis is the channel number. Thus an RGB image that is + 16 pixels high and 8 wide will use an array that is 16x8x3. For 2 + dimensional arrays the first axis is the height, but the second axis + is ``width*channels``, so an RGB image that is 16 pixels high and 8 + wide will use a 2-dimensional array that is 16x24 (each row will be + 8*3==24 sample values). + + *mode* is a string that specifies the image colour format in a + PIL-style mode. It can be: + + ``'L'`` + greyscale (1 channel) + ``'LA'`` + greyscale with alpha (2 channel) + ``'RGB'`` + colour image (3 channel) + ``'RGBA'`` + colour image with alpha (4 channel) + + The mode string can also specify the bit depth (overriding how this + function normally derives the bit depth, see below). Appending + ``';16'`` to the mode will cause the PNG to be 16 bits per channel; + any decimal from 1 to 16 can be used to specify the bit depth. + + When a 2-dimensional array is used *mode* determines how many + channels the image has, and so allows the width to be derived from + the second array dimension. + + The array is expected to be a ``numpy`` array, but it can be any + suitable Python sequence. For example, a list of lists can be used: + ``png.from_array([[0, 255, 0], [255, 0, 255]], 'L')``. The exact + rules are: ``len(a)`` gives the first dimension, height; + ``len(a[0])`` gives the second dimension; ``len(a[0][0])`` gives the + third dimension, unless an exception is raised in which case a + 2-dimensional array is assumed. It's slightly more complicated than + that because an iterator of rows can be used, and it all still + works. Using an iterator allows data to be streamed efficiently. + + The bit depth of the PNG is normally taken from the array element's + datatype (but if *mode* specifies a bitdepth then that is used + instead). The array element's datatype is determined in a way which + is supposed to work both for ``numpy`` arrays and for Python + ``array.array`` objects. A 1 byte datatype will give a bit depth of + 8, a 2 byte datatype will give a bit depth of 16. If the datatype + does not have an implicit size, for example it is a plain Python + list of lists, as above, then a default of 8 is used. + + The *info* parameter is a dictionary that can be used to specify + metadata (in the same style as the arguments to the + :class:``png.Writer`` class). For this function the keys that are + useful are: + + height + overrides the height derived from the array dimensions and allows + *a* to be an iterable. + width + overrides the width derived from the array dimensions. + bitdepth + overrides the bit depth derived from the element datatype (but + must match *mode* if that also specifies a bit depth). + + Generally anything specified in the + *info* dictionary will override any implicit choices that this + function would otherwise make, but must match any explicit ones. + For example, if the *info* dictionary has a ``greyscale`` key then + this must be true when mode is ``'L'`` or ``'LA'`` and false when + mode is ``'RGB'`` or ``'RGBA'``. + """ + + # We abuse the *info* parameter by modifying it. Take a copy here. + # (Also typechecks *info* to some extent). + info = dict(info) + + # Syntax check mode string. + bitdepth = None + try: + mode = mode.split(";") + if len(mode) not in (1, 2): + raise Error() + if mode[0] not in ("L", "LA", "RGB", "RGBA"): + raise Error() + if len(mode) == 2: + try: + bitdepth = int(mode[1]) + except: + raise Error() + except Error: + raise Error("mode string should be 'RGB' or 'L;16' or similar.") + mode = mode[0] + + # Get bitdepth from *mode* if possible. + if bitdepth: + if info.get("bitdepth") and bitdepth != info["bitdepth"]: + raise Error( + "mode bitdepth (%d) should match info bitdepth (%d)." + % (bitdepth, info["bitdepth"]) + ) + info["bitdepth"] = bitdepth + + # Fill in and/or check entries in *info*. + # Dimensions. + if "size" in info: + # Check width, height, size all match where used. + for dimension, axis in [("width", 0), ("height", 1)]: + if dimension in info: + if info[dimension] != info["size"][axis]: + raise Error( + f"info[{dimension!r}] should match info['size'][{axis!r}]." + ) + info["width"], info["height"] = info["size"] + if "height" not in info: + try: + l = len(a) + except: + raise Error("len(a) does not work, supply info['height'] instead.") + info["height"] = l + # Colour format. + if "greyscale" in info: + if bool(info["greyscale"]) != ("L" in mode): + raise Error("info['greyscale'] should match mode.") + info["greyscale"] = "L" in mode + if "alpha" in info: + if bool(info["alpha"]) != ("A" in mode): + raise Error("info['alpha'] should match mode.") + info["alpha"] = "A" in mode + + planes = len(mode) + if "planes" in info: + if info["planes"] != planes: + raise Error("info['planes'] should match mode.") + + # In order to work out whether we the array is 2D or 3D we need its + # first row, which requires that we take a copy of its iterator. + # We may also need the first row to derive width and bitdepth. + a, t = itertools.tee(a) + row = next(t) + del t + try: + row[0][0] + threed = True + testelement = row[0] + except: + threed = False + testelement = row + if "width" not in info: + if threed: + width = len(row) + else: + width = len(row) // planes + info["width"] = width + + # Not implemented yet + assert not threed + + if "bitdepth" not in info: + try: + dtype = testelement.dtype + # goto the "else:" clause. Sorry. + except: + try: + # Try a Python array.array. + bitdepth = 8 * testelement.itemsize + except: + # We can't determine it from the array element's + # datatype, use a default of 8. + bitdepth = 8 + else: + # If we got here without exception, we now assume that + # the array is a numpy array. + if dtype.kind == "b": + bitdepth = 1 + else: + bitdepth = 8 * dtype.itemsize + info["bitdepth"] = bitdepth + + for thing in "width height bitdepth greyscale alpha".split(): + assert thing in info + return Image(a, info) + + +# So that refugee's from PIL feel more at home. Not documented. +fromarray = from_array + + +class Image: + """A PNG image. + You can create an :class:`Image` object from an array of pixels by calling + :meth:`png.from_array`. It can be saved to disk with the + :meth:`save` method.""" + + def __init__(self, rows, info): + """ + .. note :: + + The constructor is not public. Please do not call it. + """ + + self.rows = rows + self.info = info + + def save(self, file): + """Save the image to *file*. If *file* looks like an open file + descriptor then it is used, otherwise it is treated as a + filename and a fresh file is opened. + + In general, you can only call this method once; after it has + been called the first time and the PNG image has been saved, the + source data will have been streamed, and cannot be streamed + again. + """ + + w = Writer(**self.info) + + try: + file.write + + def close(): + pass + + except: + file = open(file, "wb") + + def close(): + file.close() + + try: + w.write(file, self.rows) + finally: + close() + + +class _readable: + """ + A simple file-like interface for strings and arrays. + """ + + def __init__(self, buf): + self.buf = buf + self.offset = 0 + + def read(self, n): + r = self.buf[self.offset : self.offset + n] + if isarray(r): + r = tostring(r) + self.offset += n + return r + + +class Reader: + """ + PNG decoder in pure Python. + """ + + def __init__(self, _guess=None, **kw): + """ + Create a PNG decoder object. + + The constructor expects exactly one keyword argument. If you + supply a positional argument instead, it will guess the input + type. You can choose among the following keyword arguments: + + filename + Name of input file (a PNG file). + file + A file-like object (object with a read() method). + bytes + ``array`` or ``string`` with PNG data. + + """ + if (_guess is not None and len(kw) != 0) or (_guess is None and len(kw) != 1): + raise TypeError("Reader() takes exactly 1 argument") + + # Will be the first 8 bytes, later on. See validate_signature. + self.signature = None + self.transparent = None + # A pair of (len,type) if a chunk has been read but its data and + # checksum have not (in other words the file position is just + # past the 4 bytes that specify the chunk type). See preamble + # method for how this is used. + self.atchunk = None + + if _guess is not None: + if isarray(_guess): + kw["bytes"] = _guess + elif isinstance(_guess, str): + kw["filename"] = _guess + elif isinstance(_guess, io.IOBase): + kw["file"] = _guess + + if "filename" in kw: + self.file = open(kw["filename"], "rb") + elif "file" in kw: + self.file = kw["file"] + elif "bytes" in kw: + self.file = _readable(kw["bytes"]) + else: + raise TypeError("expecting filename, file or bytes array") + + def chunk(self, seek=None): + """ + Read the next PNG chunk from the input file; returns a + (*type*,*data*) tuple. *type* is the chunk's type as a string + (all PNG chunk types are 4 characters long). *data* is the + chunk's data content, as a string. + + If the optional `seek` argument is + specified then it will keep reading chunks until it either runs + out of file or finds the type specified by the argument. Note + that in general the order of chunks in PNGs is unspecified, so + using `seek` can cause you to miss chunks. + """ + + self.validate_signature() + + while True: + # http://www.w3.org/TR/PNG/#5Chunk-layout + if not self.atchunk: + self.atchunk = self.chunklentype() + length, type = self.atchunk + self.atchunk = None + data = self.file.read(length) + if len(data) != length: + raise ChunkError( + "Chunk %s too short for required %i octets." % (type, length) + ) + checksum = self.file.read(4) + if len(checksum) != 4: + raise ValueError("Chunk %s too short for checksum.", checksum) + if seek and type != seek: + continue + verify = zlib.crc32(strtobytes(type)) + verify = zlib.crc32(data, verify) + # Whether the output from zlib.crc32 is signed or not varies + # according to hideous implementation details, see + # http://bugs.python.org/issue1202 . + # We coerce it to be positive here (in a way which works on + # Python 2.3 and older). + verify &= 2**32 - 1 + verify = struct.pack("!I", verify) + if checksum != verify: + # print(repr(checksum)) + (a,) = struct.unpack("!I", checksum) + (b,) = struct.unpack("!I", verify) + raise ChunkError( + f"Checksum error in {type} chunk: 0x{a:08X} != 0x{b:08X}." + ) + return type, data + + def chunks(self): + """Return an iterator that will yield each chunk as a + (*chunktype*, *content*) pair. + """ + + while True: + t, v = self.chunk() + yield t, v + if t == "IEND": + break + + def undo_filter(self, filter_type, scanline, previous): + """Undo the filter for a scanline. `scanline` is a sequence of + bytes that does not include the initial filter type byte. + `previous` is decoded previous scanline (for straightlaced + images this is the previous pixel row, but for interlaced + images, it is the previous scanline in the reduced image, which + in general is not the previous pixel row in the final image). + When there is no previous scanline (the first row of a + straightlaced image, or the first row in one of the passes in an + interlaced image), then this argument should be ``None``. + + The scanline will have the effects of filtering removed, and the + result will be returned as a fresh sequence of bytes. + """ + + # :todo: Would it be better to update scanline in place? + + # Create the result byte array. It seems that the best way to + # create the array to be the right size is to copy from an + # existing sequence. *sigh* + # If we fill the result with scanline, then this allows a + # micro-optimisation in the "null" and "sub" cases. + result = array("B", scanline) + + if filter_type == 0: + # And here, we _rely_ on filling the result with scanline, + # above. + return result + + if filter_type not in (1, 2, 3, 4): + raise FormatError( + "Invalid PNG Filter Type." + " See http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters ." + ) + + # Filter unit. The stride from one pixel to the corresponding + # byte from the previous previous. Normally this is the pixel + # size in bytes, but when this is smaller than 1, the previous + # byte is used instead. + fu = max(1, self.psize) + + # For the first line of a pass, synthesize a dummy previous + # line. An alternative approach would be to observe that on the + # first line 'up' is the same as 'null', 'paeth' is the same + # as 'sub', with only 'average' requiring any special case. + if not previous: + previous = array("B", [0] * len(scanline)) + + def sub(): + """Undo sub filter.""" + + ai = 0 + # Loops starts at index fu. Observe that the initial part + # of the result is already filled in correctly with + # scanline. + for i in range(fu, len(result)): + x = scanline[i] + a = result[ai] + result[i] = (x + a) & 0xFF + ai += 1 + + def up(): + """Undo up filter.""" + for i in range(len(result)): # pylint: disable=consider-using-enumerate + x = scanline[i] + b = previous[i] + result[i] = (x + b) & 0xFF + + def average(): + """Undo average filter.""" + + ai = -fu + for i in range(len(result)): # pylint: disable=consider-using-enumerate + x = scanline[i] + if ai < 0: + a = 0 + else: + a = result[ai] + b = previous[i] + result[i] = (x + ((a + b) >> 1)) & 0xFF + ai += 1 + + def paeth(): + """Undo Paeth filter.""" + + # Also used for ci. + ai = -fu + for i in range(len(result)): # pylint: disable=consider-using-enumerate + x = scanline[i] + if ai < 0: + a = c = 0 + else: + a = result[ai] + c = previous[ai] + b = previous[i] + p = a + b - c + pa = abs(p - a) + pb = abs(p - b) + pc = abs(p - c) + if pa <= pb and pa <= pc: + pr = a + elif pb <= pc: + pr = b + else: + pr = c + result[i] = (x + pr) & 0xFF + ai += 1 + + # Call appropriate filter algorithm. Note that 0 has already + # been dealt with. + (None, sub, up, average, paeth)[filter_type]() + return result + + def deinterlace(self, raw): + """ + Read raw pixel data, undo filters, deinterlace, and flatten. + Return in flat row flat pixel format. + """ + + # print("Reading interlaced, w=%s, r=%s, planes=%s, bpp=%s" + # % (self.width, self.height, self.planes, self.bps, file=sys.stderr)) + # Values per row (of the target image) + vpr = self.width * self.planes + + # Make a result array, and make it big enough. Interleaving + # writes to the output array randomly (well, not quite), so the + # entire output array must be in memory. + fmt = "BH"[self.bitdepth > 8] + a = array(fmt, [0] * vpr * self.height) + source_offset = 0 + + for xstart, ystart, xstep, ystep in _adam7: + # print("Adam7: start=%s,%s step=%s,%s" % ( + # xstart, ystart, xstep, ystep, file=sys.stderr)) + if xstart >= self.width: + continue + # The previous (reconstructed) scanline. None at the + # beginning of a pass to indicate that there is no previous + # line. + recon = None + # Pixels per row (reduced pass image) + ppr = int(math.ceil((self.width - xstart) / float(xstep))) + # Row size in bytes for this pass. + row_size = int(math.ceil(self.psize * ppr)) + for y in range(ystart, self.height, ystep): + filter_type = raw[source_offset] + source_offset += 1 + scanline = raw[source_offset : source_offset + row_size] + source_offset += row_size + recon = self.undo_filter(filter_type, scanline, recon) + # Convert so that there is one element per pixel value + flat = self.serialtoflat(recon, ppr) + if xstep == 1: + assert xstart == 0 + offset = y * vpr + a[offset : offset + vpr] = flat + else: + offset = y * vpr + xstart * self.planes + end_offset = (y + 1) * vpr + skip = self.planes * xstep + for i in range(self.planes): + a[offset + i : end_offset : skip] = flat[i :: self.planes] + return a + + def iterboxed(self, rows): + """Iterator that yields each scanline in boxed row flat pixel + format. `rows` should be an iterator that yields the bytes of + each row in turn. + """ + + def asvalues(raw): + """Convert a row of raw bytes into a flat row. Result may + or may not share with argument""" + + if self.bitdepth == 8: + return raw + if self.bitdepth == 16: + raw = tostring(raw) + return array("H", struct.unpack("!%dH" % (len(raw) // 2), raw)) + assert self.bitdepth < 8 + width = self.width + # Samples per byte + spb = 8 // self.bitdepth + out = array("B") + mask = 2**self.bitdepth - 1 + shifts = map(self.bitdepth.__mul__, reversed(range(spb))) + for o in raw: + out.extend(map(lambda i: mask & (o >> i), shifts)) + return out[:width] + + return map(asvalues, rows) + + def serialtoflat(self, bytes, width=None): + """Convert serial format (byte stream) pixel data to flat row + flat pixel. + """ + + if self.bitdepth == 8: + return bytes + if self.bitdepth == 16: + bytes = tostring(bytes) + return array("H", struct.unpack("!%dH" % (len(bytes) // 2), bytes)) + assert self.bitdepth < 8 + if width is None: + width = self.width + # Samples per byte + spb = 8 // self.bitdepth + out = array("B") + mask = 2**self.bitdepth - 1 + shifts = map(self.bitdepth.__mul__, reversed(range(spb))) + l = width + for o in bytes: + out.extend([(mask & (o >> s)) for s in shifts][:l]) + l -= spb + if l <= 0: + l = width + return out + + def iterstraight(self, raw): + """Iterator that undoes the effect of filtering, and yields each + row in serialised format (as a sequence of bytes). Assumes input + is straightlaced. `raw` should be an iterable that yields the + raw bytes in chunks of arbitrary size.""" + + # length of row, in bytes + rb = self.row_bytes + a = array("B") + # The previous (reconstructed) scanline. None indicates first + # line of image. + recon = None + for some in raw: + a.extend(some) + while len(a) >= rb + 1: + filter_type = a[0] + scanline = a[1 : rb + 1] + del a[: rb + 1] + recon = self.undo_filter(filter_type, scanline, recon) + yield recon + if len(a) != 0: + # :file:format We get here with a file format error: when the + # available bytes (after decompressing) do not pack into exact + # rows. + raise FormatError("Wrong size for decompressed IDAT chunk.") + assert len(a) == 0 + + def validate_signature(self): + """If signature (header) has not been read then read and + validate it; otherwise do nothing. + """ + + if self.signature: + return + self.signature = self.file.read(8) + if self.signature != _signature: + raise FormatError("PNG file has invalid signature.") + + def preamble(self): + """ + Extract the image metadata by reading the initial part of the PNG + file up to the start of the ``IDAT`` chunk. All the chunks that + precede the ``IDAT`` chunk are read and either processed for + metadata or discarded. + """ + + self.validate_signature() + + while True: + if not self.atchunk: + self.atchunk = self.chunklentype() + if self.atchunk is None: + raise FormatError("This PNG file has no IDAT chunks.") + if self.atchunk[1] == "IDAT": + return + self.process_chunk() + + def chunklentype(self): + """Reads just enough of the input to determine the next + chunk's length and type, returned as a (*length*, *type*) pair + where *type* is a string. If there are no more chunks, ``None`` + is returned. + """ + + x = self.file.read(8) + if not x: + return None + if len(x) != 8: + raise FormatError("End of file whilst reading chunk length and type.") + length, type = struct.unpack("!I4s", x) + type = bytestostr(type) + if length > 2**31 - 1: + raise FormatError("Chunk %s is too large: %d." % (type, length)) + return length, type + + def process_chunk(self): + """Process the next chunk and its data. This only processes the + following chunk types, all others are ignored: ``IHDR``, + ``PLTE``, ``bKGD``, ``tRNS``, ``gAMA``, ``sBIT``. + """ + + type, data = self.chunk() + if type == "IHDR": + # http://www.w3.org/TR/PNG/#11IHDR + if len(data) != 13: + raise FormatError("IHDR chunk has incorrect length.") + ( + self.width, + self.height, + self.bitdepth, + self.color_type, + self.compression, + self.filter, + self.interlace, + ) = struct.unpack("!2I5B", data) + + # Check that the header specifies only valid combinations. + if self.bitdepth not in (1, 2, 4, 8, 16): + raise Error("invalid bit depth %d" % self.bitdepth) + if self.color_type not in (0, 2, 3, 4, 6): + raise Error("invalid colour type %d" % self.color_type) + # Check indexed (palettized) images have 8 or fewer bits + # per pixel; check only indexed or greyscale images have + # fewer than 8 bits per pixel. + if (self.color_type & 1 and self.bitdepth > 8) or ( + self.bitdepth < 8 and self.color_type not in (0, 3) + ): + raise FormatError( + "Illegal combination of bit depth (%d)" + " and colour type (%d)." + " See http://www.w3.org/TR/2003/REC-PNG-20031110/#table111 ." + % (self.bitdepth, self.color_type) + ) + if self.compression != 0: + raise Error("unknown compression method %d" % self.compression) + if self.filter != 0: + raise FormatError( + "Unknown filter method %d," + " see http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters ." + % self.filter + ) + if self.interlace not in (0, 1): + raise FormatError( + "Unknown interlace method %d," + " see http://www.w3.org/TR/2003/REC-PNG-20031110/#8InterlaceMethods ." + % self.interlace + ) + + # Derived values + # http://www.w3.org/TR/PNG/#6Colour-values + colormap = bool(self.color_type & 1) + greyscale = not (self.color_type & 2) + alpha = bool(self.color_type & 4) + color_planes = (3, 1)[greyscale or colormap] + planes = color_planes + alpha + + self.colormap = colormap + self.greyscale = greyscale + self.alpha = alpha + self.color_planes = color_planes + self.planes = planes + self.psize = float(self.bitdepth) / float(8) * planes + if int(self.psize) == self.psize: + self.psize = int(self.psize) + self.row_bytes = int(math.ceil(self.width * self.psize)) + # Stores PLTE chunk if present, and is used to check + # chunk ordering constraints. + self.plte = None + # Stores tRNS chunk if present, and is used to check chunk + # ordering constraints. + self.trns = None + # Stores sbit chunk if present. + self.sbit = None + elif type == "PLTE": + # http://www.w3.org/TR/PNG/#11PLTE + if self.plte: + warnings.warn("Multiple PLTE chunks present.") + self.plte = data + if len(data) % 3 != 0: + raise FormatError("PLTE chunk's length should be a multiple of 3.") + if len(data) > (2**self.bitdepth) * 3: + raise FormatError("PLTE chunk is too long.") + if len(data) == 0: + raise FormatError("Empty PLTE is not allowed.") + elif type == "bKGD": + try: + if self.colormap: + if not self.plte: + warnings.warn("PLTE chunk is required before bKGD chunk.") + self.background = struct.unpack("B", data) + else: + self.background = struct.unpack("!%dH" % self.color_planes, data) + except struct.error: + raise FormatError("bKGD chunk has incorrect length.") + elif type == "tRNS": + # http://www.w3.org/TR/PNG/#11tRNS + self.trns = data + if self.colormap: + if not self.plte: + warnings.warn("PLTE chunk is required before tRNS chunk.") + else: + if len(data) > len(self.plte) / 3: + # Was warning, but promoted to Error as it + # would otherwise cause pain later on. + raise FormatError("tRNS chunk is too long.") + else: + if self.alpha: + raise FormatError( + "tRNS chunk is not valid with colour type %d." % self.color_type + ) + try: + self.transparent = struct.unpack("!%dH" % self.color_planes, data) + except struct.error: + raise FormatError("tRNS chunk has incorrect length.") + elif type == "gAMA": + try: + self.gamma = struct.unpack("!L", data)[0] / 100000.0 + except struct.error: + raise FormatError("gAMA chunk has incorrect length.") + elif type == "sBIT": + self.sbit = data + if ( + self.colormap + and len(data) != 3 + or not self.colormap + and len(data) != self.planes + ): + raise FormatError("sBIT chunk has incorrect length.") + + def read(self): + """ + Read the PNG file and decode it. Returns (`width`, `height`, + `pixels`, `metadata`). + + May use excessive memory. + + `pixels` are returned in boxed row flat pixel format. + """ + + def iteridat(): + """Iterator that yields all the ``IDAT`` chunks as strings.""" + while True: + try: + type, data = self.chunk() + except ValueError as e: + raise ChunkError(e.args[0]) + if type == "IEND": + # http://www.w3.org/TR/PNG/#11IEND + break + if type != "IDAT": + continue + # type == 'IDAT' + # http://www.w3.org/TR/PNG/#11IDAT + if self.colormap and not self.plte: + warnings.warn("PLTE chunk is required before IDAT chunk") + yield data + + def iterdecomp(idat): + """Iterator that yields decompressed strings. `idat` should + be an iterator that yields the ``IDAT`` chunk data. + """ + + # Currently, with no max_length parameter to decompress, this + # routine will do one yield per IDAT chunk. So not very + # incremental. + d = zlib.decompressobj() + # Each IDAT chunk is passed to the decompressor, then any + # remaining state is decompressed out. + for data in idat: + # :todo: add a max_length argument here to limit output + # size. + yield array("B", d.decompress(data)) + yield array("B", d.flush()) + + self.preamble() + raw = iterdecomp(iteridat()) + + if self.interlace: + raw = array("B", itertools.chain(*raw)) + arraycode = "BH"[self.bitdepth > 8] + # Like :meth:`group` but producing an array.array object for + # each row. + pixels = map( + lambda *row: array(arraycode, row), + *[iter(self.deinterlace(raw))] * self.width * self.planes, + ) + else: + pixels = self.iterboxed(self.iterstraight(raw)) + meta = dict() + for attr in "greyscale alpha planes bitdepth interlace".split(): + meta[attr] = getattr(self, attr) + meta["size"] = (self.width, self.height) + for attr in "gamma transparent background".split(): + a = getattr(self, attr, None) + if a is not None: + meta[attr] = a + return self.width, self.height, pixels, meta + + def read_flat(self): + """ + Read a PNG file and decode it into flat row flat pixel format. + Returns (*width*, *height*, *pixels*, *metadata*). + + May use excessive memory. + + `pixels` are returned in flat row flat pixel format. + + See also the :meth:`read` method which returns pixels in the + more stream-friendly boxed row flat pixel format. + """ + + x, y, pixel, meta = self.read() + arraycode = "BH"[meta["bitdepth"] > 8] + pixel = array(arraycode, itertools.chain(*pixel)) + return x, y, pixel, meta + + def palette(self, alpha="natural"): + """Returns a palette that is a sequence of 3-tuples or 4-tuples, + synthesizing it from the ``PLTE`` and ``tRNS`` chunks. These + chunks should have already been processed (for example, by + calling the :meth:`preamble` method). All the tuples are the + same size: 3-tuples if there is no ``tRNS`` chunk, 4-tuples when + there is a ``tRNS`` chunk. Assumes that the image is colour type + 3 and therefore a ``PLTE`` chunk is required. + + If the `alpha` argument is ``'force'`` then an alpha channel is + always added, forcing the result to be a sequence of 4-tuples. + """ + + if not self.plte: + raise FormatError("Required PLTE chunk is missing in colour type 3 image.") + plte = group(array("B", self.plte), 3) + if self.trns or alpha == "force": + trns = array("B", self.trns or "") + trns.extend([255] * (len(plte) - len(trns))) + plte = map(operator.add, plte, group(trns, 1)) + return plte + + def asDirect(self): + """Returns the image data as a direct representation of an + ``x * y * planes`` array. This method is intended to remove the + need for callers to deal with palettes and transparency + themselves. Images with a palette (colour type 3) + are converted to RGB or RGBA; images with transparency (a + ``tRNS`` chunk) are converted to LA or RGBA as appropriate. + When returned in this format the pixel values represent the + colour value directly without needing to refer to palettes or + transparency information. + + Like the :meth:`read` method this method returns a 4-tuple: + + (*width*, *height*, *pixels*, *meta*) + + This method normally returns pixel values with the bit depth + they have in the source image, but when the source PNG has an + ``sBIT`` chunk it is inspected and can reduce the bit depth of + the result pixels; pixel values will be reduced according to + the bit depth specified in the ``sBIT`` chunk (PNG nerds should + note a single result bit depth is used for all channels; the + maximum of the ones specified in the ``sBIT`` chunk. An RGB565 + image will be rescaled to 6-bit RGB666). + + The *meta* dictionary that is returned reflects the `direct` + format and not the original source image. For example, an RGB + source image with a ``tRNS`` chunk to represent a transparent + colour, will have ``planes=3`` and ``alpha=False`` for the + source image, but the *meta* dictionary returned by this method + will have ``planes=4`` and ``alpha=True`` because an alpha + channel is synthesized and added. + + *pixels* is the pixel data in boxed row flat pixel format (just + like the :meth:`read` method). + + All the other aspects of the image data are not changed. + """ + + self.preamble() + + # Simple case, no conversion necessary. + if not self.colormap and not self.trns and not self.sbit: + return self.read() + + x, y, pixels, meta = self.read() + + if self.colormap: + meta["colormap"] = False + meta["alpha"] = bool(self.trns) + meta["bitdepth"] = 8 + meta["planes"] = 3 + bool(self.trns) + plte = list(self.palette()) + + def iterpal(pixels): + for row in pixels: + row = map(plte.__getitem__, row) + yield array("B", itertools.chain(*row)) + + pixels = iterpal(pixels) + elif self.trns: + # It would be nice if there was some reasonable way of doing + # this without generating a whole load of intermediate tuples. + # But tuples does seem like the easiest way, with no other way + # clearly much simpler or much faster. (Actually, the L to LA + # conversion could perhaps go faster (all those 1-tuples!), but + # I still wonder whether the code proliferation is worth it) + it = self.transparent + maxval = 2 ** meta["bitdepth"] - 1 + planes = meta["planes"] + meta["alpha"] = True + meta["planes"] += 1 + typecode = "BH"[meta["bitdepth"] > 8] + + def itertrns(pixels): + for row in pixels: + # For each row we group it into pixels, then form a + # characterisation vector that says whether each pixel + # is opaque or not. Then we convert True/False to + # 0/maxval (by multiplication), and add it as the extra + # channel. + row = group(row, planes) + opa = map(it.__ne__, row) + opa = map(maxval.__mul__, opa) + opa = zip(opa) # convert to 1-tuples + yield array(typecode, itertools.chain(*map(operator.add, row, opa))) + + pixels = itertrns(pixels) + targetbitdepth = None + if self.sbit: + sbit = struct.unpack(f"{len(self.sbit)}B", self.sbit) + targetbitdepth = max(sbit) + if targetbitdepth > meta["bitdepth"]: + raise Error("sBIT chunk %r exceeds bitdepth %d" % (sbit, self.bitdepth)) + if min(sbit) <= 0: + raise Error(f"sBIT chunk {sbit!r} has a 0-entry") + if targetbitdepth == meta["bitdepth"]: + targetbitdepth = None + if targetbitdepth: + shift = meta["bitdepth"] - targetbitdepth + meta["bitdepth"] = targetbitdepth + + def itershift(pixels): + for row in pixels: + yield map(shift.__rrshift__, row) + + pixels = itershift(pixels) + return x, y, pixels, meta + + def asFloat(self, maxval=1.0): + """Return image pixels as per :meth:`asDirect` method, but scale + all pixel values to be floating point values between 0.0 and + *maxval*. + """ + + x, y, pixels, info = self.asDirect() + sourcemaxval = 2 ** info["bitdepth"] - 1 + del info["bitdepth"] + info["maxval"] = float(maxval) + factor = float(maxval) / float(sourcemaxval) + + def iterfloat(): + for row in pixels: + yield map(factor.__mul__, row) + + return x, y, iterfloat(), info + + def _as_rescale(self, get, targetbitdepth): + """Helper used by :meth:`asRGB8` and :meth:`asRGBA8`.""" + + width, height, pixels, meta = get() + maxval = 2 ** meta["bitdepth"] - 1 + targetmaxval = 2**targetbitdepth - 1 + factor = float(targetmaxval) / float(maxval) + meta["bitdepth"] = targetbitdepth + + def iterscale(): + for row in pixels: + yield map(lambda x: int(round(x * factor)), row) + + return width, height, iterscale(), meta + + def asRGB8(self): + """Return the image data as an RGB pixels with 8-bits per + sample. This is like the :meth:`asRGB` method except that + this method additionally rescales the values so that they + are all between 0 and 255 (8-bit). In the case where the + source image has a bit depth < 8 the transformation preserves + all the information; where the source image has bit depth + > 8, then rescaling to 8-bit values loses precision. No + dithering is performed. Like :meth:`asRGB`, an alpha channel + in the source image will raise an exception. + + This function returns a 4-tuple: + (*width*, *height*, *pixels*, *metadata*). + *width*, *height*, *metadata* are as per the :meth:`read` method. + + *pixels* is the pixel data in boxed row flat pixel format. + """ + + return self._as_rescale(self.asRGB, 8) + + def asRGBA8(self): + """Return the image data as RGBA pixels with 8-bits per + sample. This method is similar to :meth:`asRGB8` and + :meth:`asRGBA`: The result pixels have an alpha channel, *and* + values are rescaled to the range 0 to 255. The alpha channel is + synthesized if necessary (with a small speed penalty). + """ + + return self._as_rescale(self.asRGBA, 8) + + def asRGB(self): + """Return image as RGB pixels. RGB colour images are passed + through unchanged; greyscales are expanded into RGB + triplets (there is a small speed overhead for doing this). + + An alpha channel in the source image will raise an + exception. + + The return values are as for the :meth:`read` method + except that the *metadata* reflect the returned pixels, not the + source image. In particular, for this method + ``metadata['greyscale']`` will be ``False``. + """ + + width, height, pixels, meta = self.asDirect() + if meta["alpha"]: + raise Error("will not convert image with alpha channel to RGB") + if not meta["greyscale"]: + return width, height, pixels, meta + meta["greyscale"] = False + typecode = "BH"[meta["bitdepth"] > 8] + + def iterrgb(): + for row in pixels: + a = array(typecode, [0]) * 3 * width + for i in range(3): + a[i::3] = row + yield a + + return width, height, iterrgb(), meta + + def asRGBA(self): + """Return image as RGBA pixels. Greyscales are expanded into + RGB triplets; an alpha channel is synthesized if necessary. + The return values are as for the :meth:`read` method + except that the *metadata* reflect the returned pixels, not the + source image. In particular, for this method + ``metadata['greyscale']`` will be ``False``, and + ``metadata['alpha']`` will be ``True``. + """ + + width, height, pixels, meta = self.asDirect() + if meta["alpha"] and not meta["greyscale"]: + return width, height, pixels, meta + typecode = "BH"[meta["bitdepth"] > 8] + maxval = 2 ** meta["bitdepth"] - 1 + + def newarray(): + return array(typecode, [0]) * 4 * width + + if meta["alpha"] and meta["greyscale"]: + # LA to RGBA + def convert(): + for row in pixels: + # Create a fresh target row, then copy L channel + # into first three target channels, and A channel + # into fourth channel. + a = newarray() + for i in range(3): + a[i::4] = row[0::2] + a[3::4] = row[1::2] + yield a + + elif meta["greyscale"]: + # L to RGBA + def convert(): + for row in pixels: + a = newarray() + for i in range(3): + a[i::4] = row + a[3::4] = array(typecode, [maxval]) * width + yield a + + else: + assert not meta["alpha"] and not meta["greyscale"] + + # RGB to RGBA + def convert(): + for row in pixels: + a = newarray() + for i in range(3): + a[i::4] = row[i::3] + a[3::4] = array(typecode, [maxval]) * width + yield a + + meta["alpha"] = True + meta["greyscale"] = False + return width, height, convert(), meta + + +# === Internal Test Support === + +# This section comprises the tests that are internally validated (as +# opposed to tests which produce output files that are externally +# validated). Primarily they are unittests. + +# Note that it is difficult to internally validate the results of +# writing a PNG file. The only thing we can do is read it back in +# again, which merely checks consistency, not that the PNG file we +# produce is valid. + +# Run the tests from the command line: +# python -c 'import png;png.test()' + +# (For an in-memory binary file IO object) We use BytesIO where +# available, otherwise we use StringIO, but name it BytesIO. +try: + from io import BytesIO +except: + from StringIO import StringIO as BytesIO +import tempfile +import unittest + + +def test(): + unittest.main(__name__) + + +def topngbytes(name, rows, x, y, **k): + """Convenience function for creating a PNG file "in memory" as a + string. Creates a :class:`Writer` instance using the keyword arguments, + then passes `rows` to its :meth:`Writer.write` method. The resulting + PNG file is returned as a string. `name` is used to identify the file for + debugging. + """ + + import os + + print(name) + f = BytesIO() + w = Writer(x, y, **k) + w.write(f, rows) + if os.environ.get("PYPNG_TEST_TMP"): + w = open(name, "wb") + w.write(f.getvalue()) + w.close() + return f.getvalue() + + +def testWithIO(inp, out, f): + """Calls the function `f` with ``sys.stdin`` changed to `inp` + and ``sys.stdout`` changed to `out`. They are restored when `f` + returns. This function returns whatever `f` returns. + """ + + import os + + try: + oldin, sys.stdin = sys.stdin, inp + oldout, sys.stdout = sys.stdout, out + x = f() + finally: + sys.stdin = oldin + sys.stdout = oldout + if os.environ.get("PYPNG_TEST_TMP") and hasattr(out, "getvalue"): + name = mycallersname() + if name: + w = open(name + ".png", "wb") + w.write(out.getvalue()) + w.close() + return x + + +def mycallersname(): + """Returns the name of the caller of the caller of this function + (hence the name of the caller of the function in which + "mycallersname()" textually appears). Returns None if this cannot + be determined.""" + + # http://docs.python.org/library/inspect.html#the-interpreter-stack + import inspect + + frame = inspect.currentframe() + if not frame: + return None + frame_, filename_, lineno_, funname, linelist_, listi_ = inspect.getouterframes( + frame + )[2] + return funname + + +def seqtobytes(s): + """Convert a sequence of integers to a *bytes* instance. Good for + plastering over Python 2 / Python 3 cracks. + """ + + return strtobytes("".join(chr(x) for x in s)) + + +class Test(unittest.TestCase): + # This member is used by the superclass. If we don't define a new + # class here then when we use self.assertRaises() and the PyPNG code + # raises an assertion then we get no proper traceback. I can't work + # out why, but defining a new class here means we get a proper + # traceback. + class failureException(Exception): + pass + + def helperLN(self, n): + mask = (1 << n) - 1 + # Use small chunk_limit so that multiple chunk writing is + # tested. Making it a test for Issue 20. + w = Writer(15, 17, greyscale=True, bitdepth=n, chunk_limit=99) + f = BytesIO() + w.write_array(f, array("B", map(mask.__and__, range(1, 256)))) + r = Reader(bytes=f.getvalue()) + x, y, pixels, meta = r.read() + self.assertEqual(x, 15) + self.assertEqual(y, 17) + self.assertEqual( + list(itertools.chain(*pixels)), map(mask.__and__, range(1, 256)) + ) + + def testL8(self): + return self.helperLN(8) + + def testL4(self): + return self.helperLN(4) + + def testL2(self): + "Also tests asRGB8." + w = Writer(1, 4, greyscale=True, bitdepth=2) + f = BytesIO() + w.write_array(f, array("B", range(4))) + r = Reader(bytes=f.getvalue()) + x, y, pixels, meta = r.asRGB8() + self.assertEqual(x, 1) + self.assertEqual(y, 4) + for i, row in enumerate(pixels): + self.assertEqual(len(row), 3) + self.assertEqual(list(row), [0x55 * i] * 3) + + def testP2(self): + "2-bit palette." + a = (255, 255, 255) + b = (200, 120, 120) + c = (50, 99, 50) + w = Writer(1, 4, bitdepth=2, palette=[a, b, c]) + f = BytesIO() + w.write_array(f, array("B", (0, 1, 1, 2))) + r = Reader(bytes=f.getvalue()) + x, y, pixels, meta = r.asRGB8() + self.assertEqual(x, 1) + self.assertEqual(y, 4) + self.assertEqual(list(pixels), map(list, [a, b, b, c])) + + def testPtrns(self): + "Test colour type 3 and tRNS chunk (and 4-bit palette)." + a = (50, 99, 50, 50) + b = (200, 120, 120, 80) + c = (255, 255, 255) + d = (200, 120, 120) + e = (50, 99, 50) + w = Writer(3, 3, bitdepth=4, palette=[a, b, c, d, e]) + f = BytesIO() + w.write_array(f, array("B", (4, 3, 2, 3, 2, 0, 2, 0, 1))) + r = Reader(bytes=f.getvalue()) + x, y, pixels, meta = r.asRGBA8() + self.assertEqual(x, 3) + self.assertEqual(y, 3) + c = c + (255,) + d = d + (255,) + e = e + (255,) + boxed = [(e, d, c), (d, c, a), (c, a, b)] + flat = map(lambda row: itertools.chain(*row), boxed) + self.assertEqual(map(list, pixels), map(list, flat)) + + def testRGBtoRGBA(self): + "asRGBA8() on colour type 2 source." "" + # Test for Issue 26 + r = Reader(bytes=_pngsuite["basn2c08"]) + x, y, pixels, meta = r.asRGBA8() + # Test the pixels at row 9 columns 0 and 1. + row9 = list(pixels)[9] + self.assertEqual(row9[0:8], [0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xDE, 0xFF, 0xFF]) + + def testLtoRGBA(self): + "asRGBA() on grey source." "" + # Test for Issue 60 + r = Reader(bytes=_pngsuite["basi0g08"]) + x, y, pixels, meta = r.asRGBA() + row9 = list(list(pixels)[9]) + self.assertEqual(row9[0:8], [222, 222, 222, 255, 221, 221, 221, 255]) + + def testCtrns(self): + "Test colour type 2 and tRNS chunk." + # Test for Issue 25 + r = Reader(bytes=_pngsuite["tbrn2c08"]) + x, y, pixels, meta = r.asRGBA8() + # I just happen to know that the first pixel is transparent. + # In particular it should be #7f7f7f00 + row0 = list(pixels)[0] + self.assertEqual(tuple(row0[0:4]), (0x7F, 0x7F, 0x7F, 0x00)) + + def testAdam7read(self): + """Adam7 interlace reading. + Specifically, test that for images in the PngSuite that + have both an interlaced and straightlaced pair that both + images from the pair produce the same array of pixels.""" + for candidate in _pngsuite: + if not candidate.startswith("basn"): + continue + candi = candidate.replace("n", "i") + if candi not in _pngsuite: + continue + print(f"adam7 read {candidate}") + straight = Reader(bytes=_pngsuite[candidate]) + adam7 = Reader(bytes=_pngsuite[candi]) + # Just compare the pixels. Ignore x,y (because they're + # likely to be correct?); metadata is ignored because the + # "interlace" member differs. Lame. + straight = straight.read()[2] + adam7 = adam7.read()[2] + self.assertEqual(map(list, straight), map(list, adam7)) + + def testAdam7write(self): + """Adam7 interlace writing. + For each test image in the PngSuite, write an interlaced + and a straightlaced version. Decode both, and compare results. + """ + # Not such a great test, because the only way we can check what + # we have written is to read it back again. + + for name, bytes in _pngsuite.items(): + # Only certain colour types supported for this test. + if name[3:5] not in ["n0", "n2", "n4", "n6"]: + continue + it = Reader(bytes=bytes) + x, y, pixels, meta = it.read() + pngi = topngbytes( + f"adam7wn{name}.png", + pixels, + x=x, + y=y, + bitdepth=it.bitdepth, + greyscale=it.greyscale, + alpha=it.alpha, + transparent=it.transparent, + interlace=False, + ) + x, y, ps, meta = Reader(bytes=pngi).read() + it = Reader(bytes=bytes) + x, y, pixels, meta = it.read() + pngs = topngbytes( + f"adam7wi{name}.png", + pixels, + x=x, + y=y, + bitdepth=it.bitdepth, + greyscale=it.greyscale, + alpha=it.alpha, + transparent=it.transparent, + interlace=True, + ) + x, y, pi, meta = Reader(bytes=pngs).read() + self.assertEqual(map(list, ps), map(list, pi)) + + def testPGMin(self): + """Test that the command line tool can read PGM files.""" + + def do(): + return _main(["testPGMin"]) + + s = BytesIO() + s.write(strtobytes("P5 2 2 3\n")) + s.write(strtobytes("\x00\x01\x02\x03")) + s.flush() + s.seek(0) + o = BytesIO() + testWithIO(s, o, do) + r = Reader(bytes=o.getvalue()) + x, y, pixels, meta = r.read() + self.assertTrue(r.greyscale) + self.assertEqual(r.bitdepth, 2) + + def testPAMin(self): + """Test that the command line tool can read PAM file.""" + + def do(): + return _main(["testPAMin"]) + + s = BytesIO() + s.write( + strtobytes( + "P7\nWIDTH 3\nHEIGHT 1\nDEPTH 4\nMAXVAL 255\n" + "TUPLTYPE RGB_ALPHA\nENDHDR\n" + ) + ) + # The pixels in flat row flat pixel format + flat = [255, 0, 0, 255, 0, 255, 0, 120, 0, 0, 255, 30] + asbytes = seqtobytes(flat) + s.write(asbytes) + s.flush() + s.seek(0) + o = BytesIO() + testWithIO(s, o, do) + r = Reader(bytes=o.getvalue()) + x, y, pixels, meta = r.read() + self.assertTrue(r.alpha) + self.assertTrue(not r.greyscale) + self.assertEqual(list(itertools.chain(*pixels)), flat) + + def testLA4(self): + """Create an LA image with bitdepth 4.""" + bytes = topngbytes( + "la4.png", [[5, 12]], 1, 1, greyscale=True, alpha=True, bitdepth=4 + ) + sbit = Reader(bytes=bytes).chunk("sBIT")[1] + self.assertEqual(sbit, strtobytes("\x04\x04")) + + def testPNMsbit(self): + """Test that PNM files can generates sBIT chunk.""" + + def do(): + return _main(["testPNMsbit"]) + + s = BytesIO() + s.write(strtobytes("P6 8 1 1\n")) + for pixel in range(8): + s.write(struct.pack(" 255: + a = array("H") + else: + a = array("B") + fw = float(width) + fh = float(height) + pfun = test_patterns[pattern] + for y in range(height): + fy = float(y) / fh + for x in range(width): + a.append(int(round(pfun(float(x) / fw, fy) * maxval))) + return a + + def test_rgba(size=256, bitdepth=8, red="GTB", green="GLR", blue="RTL", alpha=None): + """ + Create a test image. Each channel is generated from the + specified pattern; any channel apart from red can be set to + None, which will cause it not to be in the image. It + is possible to create all PNG channel types (L, RGB, LA, RGBA), + as well as non PNG channel types (RGA, and so on). + """ + + i = test_pattern(size, size, bitdepth, red) + psize = 1 + for channel in (green, blue, alpha): + if channel: + c = test_pattern(size, size, bitdepth, channel) + i = interleave_planes(i, c, psize, 1) + psize += 1 + return i + + def pngsuite_image(name): + """ + Create a test image by reading an internal copy of the files + from the PngSuite. Returned in flat row flat pixel format. + """ + + if name not in _pngsuite: + raise NotImplementedError( + f"cannot find PngSuite file {name} (use -L for a list)" + ) + r = Reader(bytes=_pngsuite[name]) + w, h, pixels, meta = r.asDirect() + assert w == h + # LAn for n < 8 is a special case for which we need to rescale + # the data. + if meta["greyscale"] and meta["alpha"] and meta["bitdepth"] < 8: + factor = 255 // (2 ** meta["bitdepth"] - 1) + + def rescale(data): + for row in data: + yield map(factor.__mul__, row) + + pixels = rescale(pixels) + meta["bitdepth"] = 8 + arraycode = "BH"[meta["bitdepth"] > 8] + return w, array(arraycode, itertools.chain(*pixels)), meta + + # The body of test_suite() + size = 256 + if options.test_size: + size = options.test_size + options.bitdepth = options.test_depth + options.greyscale = bool(options.test_black) + + kwargs = {} + if options.test_red: + kwargs["red"] = options.test_red + if options.test_green: + kwargs["green"] = options.test_green + if options.test_blue: + kwargs["blue"] = options.test_blue + if options.test_alpha: + kwargs["alpha"] = options.test_alpha + if options.greyscale: + if options.test_red or options.test_green or options.test_blue: + raise ValueError( + "cannot specify colours (R, G, B) when greyscale image (black channel, K) is specified" + ) + kwargs["red"] = options.test_black + kwargs["green"] = None + kwargs["blue"] = None + options.alpha = bool(options.test_alpha) + if not args: + pixels = test_rgba(size, options.bitdepth, **kwargs) + else: + size, pixels, meta = pngsuite_image(args[0]) + for k in ["bitdepth", "alpha", "greyscale"]: + setattr(options, k, meta[k]) + + writer = Writer( + size, + size, + bitdepth=options.bitdepth, + transparent=options.transparent, + background=options.background, + gamma=options.gamma, + greyscale=options.greyscale, + alpha=options.alpha, + compression=options.compression, + interlace=options.interlace, + ) + writer.write_array(sys.stdout, pixels) + + +def read_pam_header(infile): + """ + Read (the rest of a) PAM header. `infile` should be positioned + immediately after the initial 'P7' line (at the beginning of the + second line). Returns are as for `read_pnm_header`. + """ + + # Unlike PBM, PGM, and PPM, we can read the header a line at a time. + header = dict() + while True: + l = infile.readline().strip() + if l == strtobytes("ENDHDR"): + break + if not l: + raise EOFError("PAM ended prematurely") + if l[0] == strtobytes("#"): + continue + l = l.split(None, 1) + if l[0] not in header: + header[l[0]] = l[1] + else: + header[l[0]] += strtobytes(" ") + l[1] + + required = ["WIDTH", "HEIGHT", "DEPTH", "MAXVAL"] + required = [strtobytes(x) for x in required] + WIDTH, HEIGHT, DEPTH, MAXVAL = required + present = [x for x in required if x in header] + if len(present) != len(required): + raise Error("PAM file must specify WIDTH, HEIGHT, DEPTH, and MAXVAL") + width = int(header[WIDTH]) + height = int(header[HEIGHT]) + depth = int(header[DEPTH]) + maxval = int(header[MAXVAL]) + if width <= 0 or height <= 0 or depth <= 0 or maxval <= 0: + raise Error("WIDTH, HEIGHT, DEPTH, MAXVAL must all be positive integers") + return "P7", width, height, depth, maxval + + +def read_pnm_header(infile, supported=("P5", "P6")): + """ + Read a PNM header, returning (format,width,height,depth,maxval). + `width` and `height` are in pixels. `depth` is the number of + channels in the image; for PBM and PGM it is synthesized as 1, for + PPM as 3; for PAM images it is read from the header. `maxval` is + synthesized (as 1) for PBM images. + """ + + # Generally, see http://netpbm.sourceforge.net/doc/ppm.html + # and http://netpbm.sourceforge.net/doc/pam.html + + supported = [strtobytes(x) for x in supported] + + # Technically 'P7' must be followed by a newline, so by using + # rstrip() we are being liberal in what we accept. I think this + # is acceptable. + type = infile.read(3).rstrip() + if type not in supported: + raise NotImplementedError(f"file format {type} not supported") + if type == strtobytes("P7"): + # PAM header parsing is completely different. + return read_pam_header(infile) + # Expected number of tokens in header (3 for P4, 4 for P6) + expected = 4 + pbm = ("P1", "P4") + if type in pbm: + expected = 3 + header = [type] + + # We have to read the rest of the header byte by byte because the + # final whitespace character (immediately following the MAXVAL in + # the case of P6) may not be a newline. Of course all PNM files in + # the wild use a newline at this point, so it's tempting to use + # readline; but it would be wrong. + def getc(): + c = infile.read(1) + if not c: + raise Error("premature EOF reading PNM header") + return c + + c = getc() + while True: + # Skip whitespace that precedes a token. + while c.isspace(): + c = getc() + # Skip comments. + while c == "#": + while c not in "\n\r": + c = getc() + if not c.isdigit(): + raise Error(f"unexpected character {c} found in header") + # According to the specification it is legal to have comments + # that appear in the middle of a token. + # This is bonkers; I've never seen it; and it's a bit awkward to + # code good lexers in Python (no goto). So we break on such + # cases. + token = strtobytes("") + while c.isdigit(): + token += c + c = getc() + # Slight hack. All "tokens" are decimal integers, so convert + # them here. + header.append(int(token)) + if len(header) == expected: + break + # Skip comments (again) + while c == "#": + while c not in "\n\r": + c = getc() + if not c.isspace(): + raise Error(f"expected header to end with whitespace, not {c}") + + if type in pbm: + # synthesize a MAXVAL + header.append(1) + depth = (1, 3)[type == strtobytes("P6")] + return header[0], header[1], header[2], depth, header[3] + + +def write_pnm(file, width, height, pixels, meta): + """Write a Netpbm PNM/PAM file.""" + + bitdepth = meta["bitdepth"] + maxval = 2**bitdepth - 1 + # Rudely, the number of image planes can be used to determine + # whether we are L (PGM), LA (PAM), RGB (PPM), or RGBA (PAM). + planes = meta["planes"] + # Can be an assert as long as we assume that pixels and meta came + # from a PNG file. + assert planes in (1, 2, 3, 4) + if planes in (1, 3): + if 1 == planes: + # PGM + # Could generate PBM if maxval is 1, but we don't (for one + # thing, we'd have to convert the data, not just blat it + # out). + fmt = "P5" + else: + # PPM + fmt = "P6" + file.write("%s %d %d %d\n" % (fmt, width, height, maxval)) + if planes in (2, 4): + # PAM + # See http://netpbm.sourceforge.net/doc/pam.html + if 2 == planes: + tupltype = "GRAYSCALE_ALPHA" + else: + tupltype = "RGB_ALPHA" + file.write( + "P7\nWIDTH %d\nHEIGHT %d\nDEPTH %d\nMAXVAL %d\n" + "TUPLTYPE %s\nENDHDR\n" % (width, height, planes, maxval, tupltype) + ) + # Values per row + vpr = planes * width + # struct format + fmt = ">%d" % vpr + if maxval > 0xFF: + fmt = fmt + "H" + else: + fmt = fmt + "B" + for row in pixels: + file.write(struct.pack(fmt, *row)) + file.flush() + + +def color_triple(color): + """ + Convert a command line colour value to a RGB triple of integers. + FIXME: Somewhere we need support for greyscale backgrounds etc. + """ + if color.startswith("#") and len(color) == 4: + return (int(color[1], 16), int(color[2], 16), int(color[3], 16)) + if color.startswith("#") and len(color) == 7: + return (int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16)) + elif color.startswith("#") and len(color) == 13: + return (int(color[1:5], 16), int(color[5:9], 16), int(color[9:13], 16)) + + +def _main(argv): + """ + Run the PNG encoder with options from the command line. + """ + + # Parse command line arguments + from optparse import OptionParser + import re + + version = "%prog " + re.sub(r"( ?\$|URL: |Rev:)", "", __version__) + parser = OptionParser(version=version) + parser.set_usage("%prog [options] [imagefile]") + parser.add_option( + "-r", + "--read-png", + default=False, + action="store_true", + help="Read PNG, write PNM", + ) + parser.add_option( + "-i", + "--interlace", + default=False, + action="store_true", + help="create an interlaced PNG file (Adam7)", + ) + parser.add_option( + "-t", + "--transparent", + action="store", + type="string", + metavar="color", + help="mark the specified colour (#RRGGBB) as transparent", + ) + parser.add_option( + "-b", + "--background", + action="store", + type="string", + metavar="color", + help="save the specified background colour", + ) + parser.add_option( + "-a", + "--alpha", + action="store", + type="string", + metavar="pgmfile", + help="alpha channel transparency (RGBA)", + ) + parser.add_option( + "-g", + "--gamma", + action="store", + type="float", + metavar="value", + help="save the specified gamma value", + ) + parser.add_option( + "-c", + "--compression", + action="store", + type="int", + metavar="level", + help="zlib compression level (0-9)", + ) + parser.add_option( + "-T", + "--test", + default=False, + action="store_true", + help="create a test image (a named PngSuite image if an argument is supplied)", + ) + parser.add_option( + "-L", + "--list", + default=False, + action="store_true", + help="print list of named test images", + ) + parser.add_option( + "-R", + "--test-red", + action="store", + type="string", + metavar="pattern", + help="test pattern for the red image layer", + ) + parser.add_option( + "-G", + "--test-green", + action="store", + type="string", + metavar="pattern", + help="test pattern for the green image layer", + ) + parser.add_option( + "-B", + "--test-blue", + action="store", + type="string", + metavar="pattern", + help="test pattern for the blue image layer", + ) + parser.add_option( + "-A", + "--test-alpha", + action="store", + type="string", + metavar="pattern", + help="test pattern for the alpha image layer", + ) + parser.add_option( + "-K", + "--test-black", + action="store", + type="string", + metavar="pattern", + help="test pattern for greyscale image", + ) + parser.add_option( + "-d", + "--test-depth", + default=8, + action="store", + type="int", + metavar="NBITS", + help="create test PNGs that are NBITS bits per channel", + ) + parser.add_option( + "-S", + "--test-size", + action="store", + type="int", + metavar="size", + help="width and height of the test image", + ) + (options, args) = parser.parse_args(args=argv[1:]) + + # Convert options + if options.transparent is not None: + options.transparent = color_triple(options.transparent) + if options.background is not None: + options.background = color_triple(options.background) + + if options.list: + names = list(_pngsuite) + names.sort() + for name in names: + print(name) + return + + # Run regression tests + if options.test: + return test_suite(options, args) + + # Prepare input and output files + if len(args) == 0: + infilename = "-" + infile = sys.stdin + elif len(args) == 1: + infilename = args[0] + infile = open(infilename, "rb") + else: + parser.error("more than one input file") + outfile = sys.stdout + + if options.read_png: + # Encode PNG to PPM + png = Reader(file=infile) + width, height, pixels, meta = png.asDirect() + write_pnm(outfile, width, height, pixels, meta) + else: + # Encode PNM to PNG + format, width, height, depth, maxval = read_pnm_header( + infile, ("P5", "P6", "P7") + ) + # When it comes to the variety of input formats, we do something + # rather rude. Observe that L, LA, RGB, RGBA are the 4 colour + # types supported by PNG and that they correspond to 1, 2, 3, 4 + # channels respectively. So we use the number of channels in + # the source image to determine which one we have. We do not + # care about TUPLTYPE. + greyscale = depth <= 2 + pamalpha = depth in (2, 4) + supported = map(lambda x: 2**x - 1, range(1, 17)) + try: + mi = supported.index(maxval) + except ValueError: + raise NotImplementedError( + f"your maxval ({maxval}) not in supported list {str(supported)}" + ) + bitdepth = mi + 1 + writer = Writer( + width, + height, + greyscale=greyscale, + bitdepth=bitdepth, + interlace=options.interlace, + transparent=options.transparent, + background=options.background, + alpha=bool(pamalpha or options.alpha), + gamma=options.gamma, + compression=options.compression, + ) + if options.alpha: + pgmfile = open(options.alpha, "rb") + format, awidth, aheight, adepth, amaxval = read_pnm_header(pgmfile, "P5") + if amaxval != "255": + raise NotImplementedError( + f"maxval {amaxval} not supported for alpha channel" + ) + if (awidth, aheight) != (width, height): + raise ValueError( + "alpha channel image size mismatch" + " (%s has %sx%s but %s has %sx%s)" + % (infilename, width, height, options.alpha, awidth, aheight) + ) + writer.convert_ppm_and_pgm(infile, pgmfile, outfile) + else: + writer.convert_pnm(infile, outfile) + + +if __name__ == "__main__": + try: + _main(sys.argv) + except Error as e: + sys.stderr.write(f"{e}\n") diff --git a/.venv/Lib/site-packages/pygame/tests/test_utils/run_tests.py b/.venv/Lib/site-packages/pygame/tests/test_utils/run_tests.py new file mode 100644 index 00000000..60968d53 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/test_utils/run_tests.py @@ -0,0 +1,349 @@ +import sys + +if __name__ == "__main__": + raise RuntimeError("This module is for import only") +test_pkg_name = ".".join(__name__.split(".")[0:-2]) +is_pygame_pkg = test_pkg_name == "pygame.tests" +test_runner_mod = test_pkg_name + ".test_utils.test_runner" + +if is_pygame_pkg: + from pygame.tests.test_utils import import_submodule + from pygame.tests.test_utils.test_runner import ( + prepare_test_env, + run_test, + combine_results, + get_test_results, + TEST_RESULTS_START, + ) +else: + from test.test_utils import import_submodule + from test.test_utils.test_runner import ( + prepare_test_env, + run_test, + combine_results, + get_test_results, + TEST_RESULTS_START, + ) +import pygame +import pygame.threads + +import os +import re +import shutil +import tempfile +import time +import random +from pprint import pformat + +was_run = False + + +def run(*args, **kwds): + """Run the Pygame unit test suite and return (total tests run, fails dict) + + Positional arguments (optional): + The names of tests to include. If omitted then all tests are run. Test + names need not include the trailing '_test'. + + Keyword arguments: + incomplete - fail incomplete tests (default False) + usesubprocess - run all test suites in the current process + (default False, use separate subprocesses) + dump - dump failures/errors as dict ready to eval (default False) + file - if provided, the name of a file into which to dump failures/errors + timings - if provided, the number of times to run each individual test to + get an average run time (default is run each test once) + exclude - A list of TAG names to exclude from the run. The items may be + comma or space separated. + show_output - show silenced stderr/stdout on errors (default False) + all - dump all results, not just errors (default False) + randomize - randomize order of tests (default False) + seed - if provided, a seed randomizer integer + multi_thread - if provided, the number of THREADS in which to run + subprocessed tests + time_out - if subprocess is True then the time limit in seconds before + killing a test (default 30) + fake - if provided, the name of the fake tests package in the + run_tests__tests subpackage to run instead of the normal + Pygame tests + python - the path to a python executable to run subprocessed tests + (default sys.executable) + interactive - allow tests tagged 'interactive'. + + Return value: + A tuple of total number of tests run, dictionary of error information. The + dictionary is empty if no errors were recorded. + + By default individual test modules are run in separate subprocesses. This + recreates normal Pygame usage where pygame.init() and pygame.quit() are + called only once per program execution, and avoids unfortunate + interactions between test modules. Also, a time limit is placed on test + execution, so frozen tests are killed when there time allotment expired. + Use the single process option if threading is not working properly or if + tests are taking too long. It is not guaranteed that all tests will pass + in single process mode. + + Tests are run in a randomized order if the randomize argument is True or a + seed argument is provided. If no seed integer is provided then the system + time is used. + + Individual test modules may have a corresponding *_tags.py module, + defining a __tags__ attribute, a list of tag strings used to selectively + omit modules from a run. By default only the 'interactive', 'ignore', and + 'subprocess_ignore' tags are ignored. 'interactive' is for modules that + take user input, like cdrom_test.py. 'ignore' and 'subprocess_ignore' for + for disabling modules for foreground and subprocess modes respectively. + These are for disabling tests on optional modules or for experimental + modules with known problems. These modules can be run from the console as + a Python program. + + This function can only be called once per Python session. It is not + reentrant. + + """ + + global was_run + + if was_run: + raise RuntimeError("run() was already called this session") + was_run = True + + options = kwds.copy() + option_usesubprocess = options.get("usesubprocess", False) + option_dump = options.pop("dump", False) + option_file = options.pop("file", None) + option_randomize = options.get("randomize", False) + option_seed = options.get("seed", None) + option_multi_thread = options.pop("multi_thread", 1) + option_time_out = options.pop("time_out", 120) + option_fake = options.pop("fake", None) + option_python = options.pop("python", sys.executable) + option_exclude = options.pop("exclude", ()) + option_interactive = options.pop("interactive", False) + + if not option_interactive and "interactive" not in option_exclude: + option_exclude += ("interactive",) + if option_usesubprocess and "subprocess_ignore" not in option_exclude: + option_exclude += ("subprocess_ignore",) + elif "ignore" not in option_exclude: + option_exclude += ("ignore",) + + option_exclude += ("python3_ignore",) + option_exclude += ("SDL2_ignore",) + + main_dir, test_subdir, fake_test_subdir = prepare_test_env() + + ########################################################################### + # Compile a list of test modules. If fake, then compile list of fake + # xxxx_test.py from run_tests__tests + + TEST_MODULE_RE = re.compile(r"^(.+_test)\.py$") + + test_mods_pkg_name = test_pkg_name + + working_dir_temp = tempfile.mkdtemp() + + if option_fake is not None: + test_mods_pkg_name = ".".join( + [test_mods_pkg_name, "run_tests__tests", option_fake] + ) + test_subdir = os.path.join(fake_test_subdir, option_fake) + working_dir = test_subdir + else: + working_dir = working_dir_temp + + # Added in because some machines will need os.environ else there will be + # false failures in subprocess mode. Same issue as python2.6. Needs some + # env vars. + + test_env = os.environ + + fmt1 = "%s.%%s" % test_mods_pkg_name + fmt2 = "%s.%%s_test" % test_mods_pkg_name + if args: + test_modules = [m.endswith("_test") and (fmt1 % m) or (fmt2 % m) for m in args] + else: + test_modules = [] + for f in sorted(os.listdir(test_subdir)): + for match in TEST_MODULE_RE.findall(f): + test_modules.append(fmt1 % (match,)) + + ########################################################################### + # Remove modules to be excluded. + + tmp = test_modules + test_modules = [] + for name in tmp: + tag_module_name = f"{name[0:-5]}_tags" + try: + tag_module = import_submodule(tag_module_name) + except ImportError: + test_modules.append(name) + else: + try: + tags = tag_module.__tags__ + except AttributeError: + print(f"{tag_module_name} has no tags: ignoring") + test_modules.append(name) + else: + for tag in tags: + if tag in option_exclude: + print(f"skipping {name} (tag '{tag}')") + break + else: + test_modules.append(name) + del tmp, tag_module_name, name + + ########################################################################### + # Meta results + + results = {} + meta_results = {"__meta__": {}} + meta = meta_results["__meta__"] + + ########################################################################### + # Randomization + + if option_randomize or option_seed is not None: + if option_seed is None: + option_seed = time.time() + meta["random_seed"] = option_seed + print(f"\nRANDOM SEED USED: {option_seed}\n") + random.seed(option_seed) + random.shuffle(test_modules) + + ########################################################################### + # Single process mode + + if not option_usesubprocess: + options["exclude"] = option_exclude + t = time.time() + for module in test_modules: + results.update(run_test(module, **options)) + t = time.time() - t + + ########################################################################### + # Subprocess mode + # + + else: + if is_pygame_pkg: + from pygame.tests.test_utils.async_sub import proc_in_time_or_kill + else: + from test.test_utils.async_sub import proc_in_time_or_kill + + pass_on_args = ["--exclude", ",".join(option_exclude)] + for field in ["randomize", "incomplete", "unbuffered", "verbosity"]: + if kwds.get(field, False): + pass_on_args.append("--" + field) + + def sub_test(module): + print(f"loading {module}") + + cmd = [option_python, "-m", test_runner_mod, module] + pass_on_args + + return ( + module, + (cmd, test_env, working_dir), + proc_in_time_or_kill( + cmd, option_time_out, env=test_env, wd=working_dir + ), + ) + + if option_multi_thread > 1: + + def tmap(f, args): + return pygame.threads.tmap( + f, args, stop_on_error=False, num_workers=option_multi_thread + ) + + else: + tmap = map + + t = time.time() + + for module, cmd, (return_code, raw_return) in tmap(sub_test, test_modules): + test_file = f"{os.path.join(test_subdir, module)}.py" + cmd, test_env, working_dir = cmd + + test_results = get_test_results(raw_return) + if test_results: + results.update(test_results) + else: + results[module] = {} + + results[module].update( + dict( + return_code=return_code, + raw_return=raw_return, + cmd=cmd, + test_file=test_file, + test_env=test_env, + working_dir=working_dir, + module=module, + ) + ) + + t = time.time() - t + + ########################################################################### + # Output Results + # + + untrusty_total, combined = combine_results(results, t) + total, n_errors, n_failures = count_results(results) + + meta["total_tests"] = total + meta["combined"] = combined + meta["total_errors"] = n_errors + meta["total_failures"] = n_failures + results.update(meta_results) + + if not option_usesubprocess and total != untrusty_total: + raise AssertionError( + "Something went wrong in the Test Machinery:\n" + "total: %d != untrusty_total: %d" % (total, untrusty_total) + ) + + if not option_dump: + print(combined) + else: + print(TEST_RESULTS_START) + print(pformat(results)) + + if option_file is not None: + results_file = open(option_file, "w") + try: + results_file.write(pformat(results)) + finally: + results_file.close() + + shutil.rmtree(working_dir_temp) + + return total, n_errors + n_failures + + +def count_results(results): + total = errors = failures = 0 + for result in results.values(): + if result.get("return_code", 0): + total += 1 + errors += 1 + else: + total += result["num_tests"] + errors += result["num_errors"] + failures += result["num_failures"] + + return total, errors, failures + + +def run_and_exit(*args, **kwargs): + """Run the tests, and if there are failures, exit with a return code of 1. + + This is needed for various buildbots to recognise that the tests have + failed. + """ + total, fails = run(*args, **kwargs) + if fails: + sys.exit(1) + sys.exit(0) diff --git a/.venv/Lib/site-packages/pygame/tests/test_utils/test_machinery.py b/.venv/Lib/site-packages/pygame/tests/test_utils/test_machinery.py new file mode 100644 index 00000000..0531cc2f --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/test_utils/test_machinery.py @@ -0,0 +1,89 @@ +import inspect +import random +import re +import unittest + +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + +from . import import_submodule + + +class PygameTestLoader(unittest.TestLoader): + def __init__( + self, randomize_tests=False, include_incomplete=False, exclude=("interactive",) + ): + super().__init__() + self.randomize_tests = randomize_tests + + if exclude is None: + self.exclude = set() + else: + self.exclude = set(exclude) + + if include_incomplete: + self.testMethodPrefix = ("test", "todo_") + + def getTestCaseNames(self, testCaseClass): + res = [] + for name in super().getTestCaseNames(testCaseClass): + tags = get_tags(testCaseClass, getattr(testCaseClass, name)) + if self.exclude.isdisjoint(tags): + res.append(name) + + if self.randomize_tests: + random.shuffle(res) + + return res + + +# Exclude by tags: + +TAGS_RE = re.compile(r"\|[tT]ags:(-?[ a-zA-Z,0-9_\n]+)\|", re.M) + + +class TestTags: + def __init__(self): + self.memoized = {} + self.parent_modules = {} + + def get_parent_module(self, class_): + if class_ not in self.parent_modules: + self.parent_modules[class_] = import_submodule(class_.__module__) + return self.parent_modules[class_] + + def __call__(self, parent_class, meth): + key = (parent_class, meth.__name__) + if key not in self.memoized: + parent_module = self.get_parent_module(parent_class) + + module_tags = getattr(parent_module, "__tags__", []) + class_tags = getattr(parent_class, "__tags__", []) + + tags = TAGS_RE.search(inspect.getdoc(meth) or "") + if tags: + test_tags = [t.strip() for t in tags.group(1).split(",")] + else: + test_tags = [] + + combined = set() + for tags in (module_tags, class_tags, test_tags): + if not tags: + continue + + add = {t for t in tags if not t.startswith("-")} + remove = {t[1:] for t in tags if t not in add} + + if add: + combined.update(add) + if remove: + combined.difference_update(remove) + + self.memoized[key] = combined + + return self.memoized[key] + + +get_tags = TestTags() diff --git a/.venv/Lib/site-packages/pygame/tests/test_utils/test_runner.py b/.venv/Lib/site-packages/pygame/tests/test_utils/test_runner.py new file mode 100644 index 00000000..a19d7b00 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/test_utils/test_runner.py @@ -0,0 +1,324 @@ +import sys +import os + +if __name__ == "__main__": + pkg_dir = os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + parent_dir, pkg_name = os.path.split(pkg_dir) + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" + if not is_pygame_pkg: + sys.path.insert(0, parent_dir) +else: + is_pygame_pkg = __name__.startswith("pygame.tests.") + +import io +import optparse +import re +import unittest +from pprint import pformat + +from .test_machinery import PygameTestLoader + + +def prepare_test_env(): + test_subdir = os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + main_dir = os.path.split(test_subdir)[0] + sys.path.insert(0, test_subdir) + fake_test_subdir = os.path.join(test_subdir, "run_tests__tests") + return main_dir, test_subdir, fake_test_subdir + + +main_dir, test_subdir, fake_test_subdir = prepare_test_env() + +################################################################################ +# Set the command line options +# +# options are shared with run_tests.py so make sure not to conflict +# in time more will be added here + +TAG_PAT = r"-?[a-zA-Z0-9_]+" +TAG_RE = re.compile(TAG_PAT) +EXCLUDE_RE = re.compile(rf"({TAG_PAT},?\s*)+$") + + +def exclude_callback(option, opt, value, parser): + if EXCLUDE_RE.match(value) is None: + raise optparse.OptionValueError(f"{opt} argument has invalid value") + parser.values.exclude = TAG_RE.findall(value) + + +opt_parser = optparse.OptionParser() + +opt_parser.add_option( + "-i", "--incomplete", action="store_true", help="fail incomplete tests" +) + +opt_parser.add_option( + "-s", + "--usesubprocess", + action="store_true", + help="run everything in a single process " " (default: use no subprocesses)", +) + +opt_parser.add_option( + "-e", + "--exclude", + action="callback", + type="string", + help="exclude tests containing any of TAGS", + callback=exclude_callback, +) + +opt_parser.add_option( + "-u", + "--unbuffered", + action="store_true", + help="Show stdout/stderr as tests run, rather than storing it and showing on failures", +) + +opt_parser.add_option( + "-v", + "--verbose", + dest="verbosity", + action="store_const", + const=2, + help="Verbose output", +) +opt_parser.add_option( + "-q", + "--quiet", + dest="verbosity", + action="store_const", + const=0, + help="Quiet output", +) + +opt_parser.add_option( + "-r", "--randomize", action="store_true", help="randomize order of tests" +) + +################################################################################ +# If an xxxx_test.py takes longer than TIME_OUT seconds it will be killed +# This is only the default, can be over-ridden on command line + +TIME_OUT = 30 + +# DEFAULTS + +################################################################################ +# Human readable output +# + +COMPLETE_FAILURE_TEMPLATE = """ +====================================================================== +ERROR: all_tests_for (%(module)s.AllTestCases) +---------------------------------------------------------------------- +Traceback (most recent call last): + File "test/%(module)s.py", line 1, in all_tests_for +subprocess completely failed with return code of %(return_code)s +cmd: %(cmd)s +test_env: %(test_env)s +working_dir: %(working_dir)s +return (first 10 and last 10 lines): +%(raw_return)s + +""" # Leave that last empty line else build page regex won't match +# Text also needs to be vertically compressed + + +RAN_TESTS_DIV = (70 * "-") + "\nRan" + +DOTS = re.compile("^([FE.sux]*)$", re.MULTILINE) + + +def extract_tracebacks(output): + """from test runner output return the tracebacks.""" + verbose_mode = " ..." in output + + if verbose_mode: + if "ERROR" in output or "FAILURE" in output: + return "\n\n==".join(output.split("\n\n==")[1:]) + else: + dots = DOTS.search(output).group(1) + if "E" in dots or "F" in dots: + return output[len(dots) + 1 :].split(RAN_TESTS_DIV)[0] + return "" + + +def output_into_dots(output): + """convert the test runner output into dots.""" + # verbose_mode = ") ..." in output + verbose_mode = " ..." in output + + if verbose_mode: + # a map from the verbose output to the dots output. + reasons = { + "... ERROR": "E", + "... unexpected success": "u", + "... skipped": "s", + "... expected failure": "x", + "... ok": ".", + "... FAIL": "F", + } + results = output.split("\n\n==")[0] + lines = [l for l in results.split("\n") if l and "..." in l] + dotlist = [] + for l in lines: + found = False + for reason in reasons: + if reason in l: + dotlist.append(reasons[reason]) + found = True + break + if not found: + raise ValueError(f"Not sure what this is. Add to reasons. :{l}") + + return "".join(dotlist) + dots = DOTS.search(output).group(1) + return dots + + +def combine_results(all_results, t): + """ + + Return pieced together results in a form fit for human consumption. Don't + rely on results if piecing together subprocessed results (single process + mode is fine). Was originally meant for that purpose but was found to be + unreliable. See the dump option for reliable results. + + """ + + all_dots = "" + failures = [] + + for module, results in sorted(all_results.items()): + output, return_code, raw_return = map( + results.get, ("output", "return_code", "raw_return") + ) + + if not output or (return_code and RAN_TESTS_DIV not in output): + # would this effect the original dict? TODO + output_lines = raw_return.splitlines() + if len(output_lines) > 20: + results["raw_return"] = "\n".join( + output_lines[:10] + ["..."] + output_lines[-10:] + ) + failures.append(COMPLETE_FAILURE_TEMPLATE % results) + all_dots += "E" + continue + + dots = output_into_dots(output) + all_dots += dots + tracebacks = extract_tracebacks(output) + if tracebacks: + failures.append(tracebacks) + + total_fails, total_errors = map(all_dots.count, "FE") + total_tests = len(all_dots) + + combined = [all_dots] + if failures: + combined += ["".join(failures).lstrip("\n")[:-1]] + combined += [f"{RAN_TESTS_DIV} {total_tests} tests in {t:.3f}s\n"] + + if failures: + infos = ([f"failures={total_fails}"] if total_fails else []) + ( + [f"errors={total_errors}"] if total_errors else [] + ) + combined += [f"FAILED ({', '.join(infos)})\n"] + else: + combined += ["OK\n"] + + return total_tests, "\n".join(combined) + + +################################################################################ + +TEST_RESULTS_START = "<--!! TEST RESULTS START HERE !!-->" +TEST_RESULTS_END = "<--!! TEST RESULTS END HERE !!-->" +_test_re_str = f"{TEST_RESULTS_START}\n(.*){TEST_RESULTS_END}" +TEST_RESULTS_RE = re.compile(_test_re_str, re.DOTALL | re.M) + + +def get_test_results(raw_return): + test_results = TEST_RESULTS_RE.search(raw_return) + if test_results: + try: + return eval(test_results.group(1)) + except: + print(f"BUGGY TEST RESULTS EVAL:\n {test_results.group(1)}") + raise + + +################################################################################ + + +def run_test( + module, + incomplete=False, + usesubprocess=True, + randomize=False, + exclude=("interactive",), + buffer=True, + unbuffered=None, + verbosity=1, +): + """Run a unit test module""" + suite = unittest.TestSuite() + + if verbosity is None: + verbosity = 1 + + if verbosity: + print(f"loading {module}") + + loader = PygameTestLoader( + randomize_tests=randomize, include_incomplete=incomplete, exclude=exclude + ) + suite.addTest(loader.loadTestsFromName(module)) + + output = io.StringIO() + runner = unittest.TextTestRunner(stream=output, buffer=buffer, verbosity=verbosity) + results = runner.run(suite) + + if verbosity == 2: + output.seek(0) + print(output.read()) + output.seek(0) + + results = { + module: { + "output": output.getvalue(), + "num_tests": results.testsRun, + "num_errors": len(results.errors), + "num_failures": len(results.failures), + } + } + + if usesubprocess: + print(TEST_RESULTS_START) + print(pformat(results)) + print(TEST_RESULTS_END) + else: + return results + + +################################################################################ + +if __name__ == "__main__": + options, args = opt_parser.parse_args() + if not args: + if is_pygame_pkg: + run_from = "pygame.tests.go" + else: + run_from = os.path.join(main_dir, "run_tests.py") + sys.exit(f"No test module provided; consider using {run_from} instead") + run_test( + args[0], + incomplete=options.incomplete, + usesubprocess=options.usesubprocess, + randomize=options.randomize, + exclude=options.exclude, + buffer=(not options.unbuffered), + ) + +################################################################################ diff --git a/.venv/Lib/site-packages/pygame/tests/threads_test.py b/.venv/Lib/site-packages/pygame/tests/threads_test.py new file mode 100644 index 00000000..07829841 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/threads_test.py @@ -0,0 +1,238 @@ +import unittest +from pygame.threads import FuncResult, tmap, WorkerQueue, Empty, STOP +from pygame import threads, Surface, transform + + +import time + + +class WorkerQueueTypeTest(unittest.TestCase): + def test_usage_with_different_functions(self): + def f(x): + return x + 1 + + def f2(x): + return x + 2 + + wq = WorkerQueue() + fr = FuncResult(f) + fr2 = FuncResult(f2) + wq.do(fr, 1) + wq.do(fr2, 1) + wq.wait() + wq.stop() + + self.assertEqual(fr.result, 2) + self.assertEqual(fr2.result, 3) + + def test_do(self): + """Tests function placement on queue and execution after blocking function completion.""" + # __doc__ (as of 2008-06-28) for pygame.threads.WorkerQueue.do: + + # puts a function on a queue for running _later_. + + # TODO: This tests needs refactoring to avoid sleep. + # sleep is slow and unreliable (especially on VMs). + + # def sleep_test(): + # time.sleep(0.5) + + # def calc_test(x): + # return x + 1 + + # worker_queue = WorkerQueue(num_workers=1) + # sleep_return = FuncResult(sleep_test) + # calc_return = FuncResult(calc_test) + # init_time = time.time() + # worker_queue.do(sleep_return) + # worker_queue.do(calc_return, 1) + # worker_queue.wait() + # worker_queue.stop() + # time_diff = time.time() - init_time + + # self.assertEqual(sleep_return.result, None) + # self.assertEqual(calc_return.result, 2) + # self.assertGreaterEqual(time_diff, 0.5) + + def test_stop(self): + """Ensure stop() stops the worker queue""" + wq = WorkerQueue() + + self.assertGreater(len(wq.pool), 0) + + for t in wq.pool: + self.assertTrue(t.is_alive()) + + for i in range(200): + wq.do(lambda x: x + 1, i) + + wq.stop() + + for t in wq.pool: + self.assertFalse(t.is_alive()) + + self.assertIs(wq.queue.get(), STOP) + + def test_threadloop(self): + # __doc__ (as of 2008-06-28) for pygame.threads.WorkerQueue.threadloop: + + # Loops until all of the tasks are finished. + + # Make a worker queue with only one thread + wq = WorkerQueue(1) + + # Ocuppy the one worker with the threadloop + # wq threads are just threadloop, so this makes an embedded threadloop + wq.do(wq.threadloop) + + # Make sure wq can still do work + # If wq can still do work, threadloop works + l = [] + wq.do(l.append, 1) + # Wait won't work because the primary thread is in an infinite loop + time.sleep(0.5) + self.assertEqual(l[0], 1) + + # Kill the embedded threadloop by sending stop onto the stack + # Threadloop puts STOP back onto the queue when it STOPs so this kills both loops + wq.stop() + + # Make sure wq has stopped + self.assertFalse(wq.pool[0].is_alive()) + + def test_wait(self): + # __doc__ (as of 2008-06-28) for pygame.threads.WorkerQueue.wait: + + # waits until all tasks are complete. + + wq = WorkerQueue() + + for i in range(2000): + wq.do(lambda x: x + 1, i) + wq.wait() + + self.assertRaises(Empty, wq.queue.get_nowait) + + wq.stop() + + +class ThreadsModuleTest(unittest.TestCase): + def test_benchmark_workers(self): + """Ensure benchmark_workers performance measure functions properly with both default and specified inputs""" + "tags:long_running" + + # __doc__ (as of 2008-06-28) for pygame.threads.benchmark_workers: + + # does a little test to see if workers are at all faster. + # Returns the number of workers which works best. + # Takes a little bit of time to run, so you should only really call + # it once. + # You can pass in benchmark data, and functions if you want. + # a_bench_func - f(data) + # the_data - data to work on. + optimal_workers = threads.benchmark_workers() + self.assertIsInstance(optimal_workers, int) + self.assertTrue(0 <= optimal_workers < 64) + + # Test passing benchmark data and function explicitly + def smooth_scale_bench(data): + transform.smoothscale(data, (128, 128)) + + surf_data = [Surface((x, x), 0, 32) for x in range(12, 64, 12)] + best_num_workers = threads.benchmark_workers(smooth_scale_bench, surf_data) + self.assertIsInstance(best_num_workers, int) + + def test_init(self): + """Ensure init() sets up the worker queue""" + threads.init(8) + + self.assertIsInstance(threads._wq, WorkerQueue) + + threads.quit() + + def test_quit(self): + """Ensure quit() cleans up the worker queue""" + threads.init(8) + threads.quit() + + self.assertIsNone(threads._wq) + + def test_tmap(self): + # __doc__ (as of 2008-06-28) for pygame.threads.tmap: + + # like map, but uses a thread pool to execute. + # num_workers - the number of worker threads that will be used. If pool + # is passed in, then the num_workers arg is ignored. + # worker_queue - you can optionally pass in an existing WorkerQueue. + # wait - True means that the results are returned when everything is finished. + # False means that we return the [worker_queue, results] right away instead. + # results, is returned as a list of FuncResult instances. + # stop_on_error - + + ## test that the outcomes of map and tmap are the same + func, data = lambda x: x + 1, range(100) + + tmapped = list(tmap(func, data)) + mapped = list(map(func, data)) + + self.assertEqual(tmapped, mapped) + + ## Test that setting tmap to not stop on errors produces the expected result + data2 = range(100) + always_excepts = lambda x: 1 / 0 + + tmapped2 = list(tmap(always_excepts, data2, stop_on_error=False)) + + # Use list comprehension to check all entries are None as all function + # calls made by tmap will have thrown an exception (ZeroDivisionError) + # Condense to single bool with `all`, which will return true if all + # entries are true + self.assertTrue(all([x is None for x in tmapped2])) + + def todo_test_tmap__None_func_and_multiple_sequences(self): + """Using a None as func and multiple sequences""" + self.fail() + + res = tmap(None, [1, 2, 3, 4]) + res2 = tmap(None, [1, 2, 3, 4], [22, 33, 44, 55]) + res3 = tmap(None, [1, 2, 3, 4], [22, 33, 44, 55, 66]) + res4 = tmap(None, [1, 2, 3, 4, 5], [22, 33, 44, 55]) + + self.assertEqual([1, 2, 3, 4], res) + self.assertEqual([(1, 22), (2, 33), (3, 44), (4, 55)], res2) + self.assertEqual([(1, 22), (2, 33), (3, 44), (4, 55), (None, 66)], res3) + self.assertEqual([(1, 22), (2, 33), (3, 44), (4, 55), (5, None)], res4) + + def test_tmap__wait(self): + r = range(1000) + wq, results = tmap(lambda x: x, r, num_workers=5, wait=False) + wq.wait() + r2 = map(lambda x: x.result, results) + self.assertEqual(list(r), list(r2)) + + def test_FuncResult(self): + """Ensure FuncResult sets its result and exception attributes""" + # Results are stored in result attribute + fr = FuncResult(lambda x: x + 1) + fr(2) + + self.assertEqual(fr.result, 3) + + # Exceptions are store in exception attribute + self.assertIsNone(fr.exception, "no exception should be raised") + + exception = ValueError("rast") + + def x(sdf): + raise exception + + fr = FuncResult(x) + fr(None) + + self.assertIs(fr.exception, exception) + + +################################################################################ + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/time_test.py b/.venv/Lib/site-packages/pygame/tests/time_test.py new file mode 100644 index 00000000..95e04f04 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/time_test.py @@ -0,0 +1,410 @@ +import os +import platform +import unittest +import pygame +import time + +Clock = pygame.time.Clock + + +class ClockTypeTest(unittest.TestCase): + __tags__ = ["timing"] + + def test_construction(self): + """Ensure a Clock object can be created""" + c = Clock() + + self.assertTrue(c, "Clock cannot be constructed") + + def test_get_fps(self): + """test_get_fps tests pygame.time.get_fps()""" + # Initialization check, first call should return 0 fps + c = Clock() + self.assertEqual(c.get_fps(), 0) + # Type check get_fps should return float + self.assertTrue(type(c.get_fps()) == float) + # Allowable margin of error in percentage + delta = 0.30 + # Test fps correctness for 100, 60 and 30 fps + self._fps_test(c, 100, delta) + self._fps_test(c, 60, delta) + self._fps_test(c, 30, delta) + + def _fps_test(self, clock, fps, delta): + """ticks fps times each second, hence get_fps() should return fps""" + delay_per_frame = 1.0 / fps + for f in range(fps): # For one second tick and sleep + clock.tick() + time.sleep(delay_per_frame) + # We should get around fps (+- fps*delta -- delta % of fps) + self.assertAlmostEqual(clock.get_fps(), fps, delta=fps * delta) + + def test_get_rawtime(self): + iterations = 10 + delay = 0.1 + delay_miliseconds = delay * (10**3) # actual time difference between ticks + framerate_limit = 5 + delta = 50 # allowable error in milliseconds + + # Testing Clock Initialization + c = Clock() + self.assertEqual(c.get_rawtime(), 0) + + # Testing Raw Time with Frame Delay + for f in range(iterations): + time.sleep(delay) + c.tick(framerate_limit) + c1 = c.get_rawtime() + self.assertAlmostEqual(delay_miliseconds, c1, delta=delta) + + # Testing get_rawtime() = get_time() + for f in range(iterations): + time.sleep(delay) + c.tick() + c1 = c.get_rawtime() + c2 = c.get_time() + self.assertAlmostEqual(c1, c2, delta=delta) + + @unittest.skipIf(platform.machine() == "s390x", "Fails on s390x") + @unittest.skipIf( + os.environ.get("CI", None), "CI can have variable time slices, slow." + ) + def test_get_time(self): + # Testing parameters + delay = 0.1 # seconds + delay_miliseconds = delay * (10**3) + iterations = 10 + delta = 50 # milliseconds + + # Testing Clock Initialization + c = Clock() + self.assertEqual(c.get_time(), 0) + + # Testing within delay parameter range + for i in range(iterations): + time.sleep(delay) + c.tick() + c1 = c.get_time() + self.assertAlmostEqual(delay_miliseconds, c1, delta=delta) + + # Comparing get_time() results with the 'time' module + for i in range(iterations): + t0 = time.time() + time.sleep(delay) + c.tick() + t1 = time.time() + c1 = c.get_time() # elapsed time in milliseconds + d0 = (t1 - t0) * ( + 10**3 + ) #'time' module elapsed time converted to milliseconds + self.assertAlmostEqual(d0, c1, delta=delta) + + @unittest.skipIf(platform.machine() == "s390x", "Fails on s390x") + @unittest.skipIf( + os.environ.get("CI", None), "CI can have variable time slices, slow." + ) + def test_tick(self): + """Tests time.Clock.tick()""" + """ + Loops with a set delay a few times then checks what tick reports to + verify its accuracy. Then calls tick with a desired frame-rate and + verifies it is not faster than the desired frame-rate nor is it taking + a dramatically long time to complete + """ + + # Adjust this value to increase the acceptable sleep jitter + epsilon = 5 # 1.5 + + # Adjust this value to increase the acceptable locked frame-rate jitter + epsilon2 = 0.3 + # adjust this value to increase the acceptable frame-rate margin + epsilon3 = 20 + testing_framerate = 60 + milliseconds = 5.0 + + collection = [] + c = Clock() + + # verify time.Clock.tick() will measure the time correctly + c.tick() + for i in range(100): + time.sleep(milliseconds / 1000) # convert to seconds + collection.append(c.tick()) + + # removes the first highest and lowest value + for outlier in [min(collection), max(collection)]: + if outlier != milliseconds: + collection.remove(outlier) + + average_time = float(sum(collection)) / len(collection) + + # assert the deviation from the intended frame-rate is within the + # acceptable amount (the delay is not taking a dramatically long time) + self.assertAlmostEqual(average_time, milliseconds, delta=epsilon) + + # verify tick will control the frame-rate + + c = Clock() + collection = [] + + start = time.time() + + for i in range(testing_framerate): + collection.append(c.tick(testing_framerate)) + + # remove the highest and lowest outliers + for outlier in [min(collection), max(collection)]: + if outlier != round(1000 / testing_framerate): + collection.remove(outlier) + + end = time.time() + + # Since calling tick with a desired fps will prevent the program from + # running at greater than the given fps, 100 iterations at 100 fps + # should last no less than 1 second + self.assertAlmostEqual(end - start, 1, delta=epsilon2) + + average_tick_time = float(sum(collection)) / len(collection) + self.assertAlmostEqual( + 1000 / average_tick_time, testing_framerate, delta=epsilon3 + ) + + def test_tick_busy_loop(self): + """Test tick_busy_loop""" + + c = Clock() + + # Test whether the return value of tick_busy_loop is equal to + # (FPS is accurate) or greater than (slower than the set FPS) + # with a small margin for error based on differences in how this + # test runs in practise - it either sometimes runs slightly fast + # or seems to based on a rounding error. + second_length = 1000 + shortfall_tolerance = 1 # (ms) The amount of time a tick is allowed to run short of, to account for underlying rounding errors + sample_fps = 40 + + self.assertGreaterEqual( + c.tick_busy_loop(sample_fps), + (second_length / sample_fps) - shortfall_tolerance, + ) + pygame.time.wait(10) # incur delay between ticks that's faster than sample_fps + self.assertGreaterEqual( + c.tick_busy_loop(sample_fps), + (second_length / sample_fps) - shortfall_tolerance, + ) + pygame.time.wait(200) # incur delay between ticks that's slower than sample_fps + self.assertGreaterEqual( + c.tick_busy_loop(sample_fps), + (second_length / sample_fps) - shortfall_tolerance, + ) + + high_fps = 500 + self.assertGreaterEqual( + c.tick_busy_loop(high_fps), (second_length / high_fps) - shortfall_tolerance + ) + + low_fps = 1 + self.assertGreaterEqual( + c.tick_busy_loop(low_fps), (second_length / low_fps) - shortfall_tolerance + ) + + low_non_factor_fps = 35 # 1000/35 makes 28.5714285714 + frame_length_without_decimal_places = int( + second_length / low_non_factor_fps + ) # Same result as math.floor + self.assertGreaterEqual( + c.tick_busy_loop(low_non_factor_fps), + frame_length_without_decimal_places - shortfall_tolerance, + ) + + high_non_factor_fps = 750 # 1000/750 makes 1.3333... + frame_length_without_decimal_places_2 = int( + second_length / high_non_factor_fps + ) # Same result as math.floor + self.assertGreaterEqual( + c.tick_busy_loop(high_non_factor_fps), + frame_length_without_decimal_places_2 - shortfall_tolerance, + ) + + zero_fps = 0 + self.assertEqual(c.tick_busy_loop(zero_fps), 0) + + # Check behaviour of unexpected values + + negative_fps = -1 + self.assertEqual(c.tick_busy_loop(negative_fps), 0) + + fractional_fps = 32.75 + frame_length_without_decimal_places_3 = int(second_length / fractional_fps) + self.assertGreaterEqual( + c.tick_busy_loop(fractional_fps), + frame_length_without_decimal_places_3 - shortfall_tolerance, + ) + + bool_fps = True + self.assertGreaterEqual( + c.tick_busy_loop(bool_fps), (second_length / bool_fps) - shortfall_tolerance + ) + + +class TimeModuleTest(unittest.TestCase): + __tags__ = ["timing"] + + @unittest.skipIf(platform.machine() == "s390x", "Fails on s390x") + @unittest.skipIf( + os.environ.get("CI", None), "CI can have variable time slices, slow." + ) + def test_delay(self): + """Tests time.delay() function.""" + millis = 50 # millisecond to wait on each iteration + iterations = 20 # number of iterations + delta = 150 # Represents acceptable margin of error for wait in ms + # Call checking function + self._wait_delay_check(pygame.time.delay, millis, iterations, delta) + # After timing behaviour, check argument type exceptions + self._type_error_checks(pygame.time.delay) + + def test_get_ticks(self): + """Tests time.get_ticks()""" + """ + Iterates and delays for arbitrary amount of time for each iteration, + check get_ticks to equal correct gap time + """ + iterations = 20 + millis = 50 + delta = 15 # Acceptable margin of error in ms + # Assert return type to be int + self.assertTrue(type(pygame.time.get_ticks()) == int) + for i in range(iterations): + curr_ticks = pygame.time.get_ticks() # Save current tick count + curr_time = time.time() # Save current time + pygame.time.delay(millis) # Delay for millis + # Time and Ticks difference from start of the iteration + time_diff = round((time.time() - curr_time) * 1000) + ticks_diff = pygame.time.get_ticks() - curr_ticks + # Assert almost equality of the ticking time and time difference + self.assertAlmostEqual(ticks_diff, time_diff, delta=delta) + + @unittest.skipIf(platform.machine() == "s390x", "Fails on s390x") + @unittest.skipIf( + os.environ.get("CI", None), "CI can have variable time slices, slow." + ) + def test_set_timer(self): + """Tests time.set_timer()""" + """ + Tests if a timer will post the correct amount of eventid events in + the specified delay. Test is posting event objects work. + Also tests if setting milliseconds to 0 stops the timer and if + the once argument and repeat arguments work. + """ + pygame.init() + TIMER_EVENT_TYPE = pygame.event.custom_type() + timer_event = pygame.event.Event(TIMER_EVENT_TYPE) + delta = 50 + timer_delay = 100 + test_number = 8 # Number of events to read for the test + events = 0 # Events read + + pygame.event.clear() + pygame.time.set_timer(TIMER_EVENT_TYPE, timer_delay) + + # Test that 'test_number' events are posted in the right amount of time + t1 = pygame.time.get_ticks() + max_test_time = t1 + timer_delay * test_number + delta + while events < test_number: + for event in pygame.event.get(): + if event == timer_event: + events += 1 + + # The test takes too much time + if pygame.time.get_ticks() > max_test_time: + break + + pygame.time.set_timer(TIMER_EVENT_TYPE, 0) + t2 = pygame.time.get_ticks() + # Is the number ef events and the timing right? + self.assertEqual(events, test_number) + self.assertAlmostEqual(timer_delay * test_number, t2 - t1, delta=delta) + + # Test that the timer stopped when set with 0ms delay. + pygame.time.delay(200) + self.assertNotIn(timer_event, pygame.event.get()) + + # Test that the old timer for an event is deleted when a new timer is set + pygame.time.set_timer(TIMER_EVENT_TYPE, timer_delay) + pygame.time.delay(int(timer_delay * 3.5)) + self.assertEqual(pygame.event.get().count(timer_event), 3) + pygame.time.set_timer(TIMER_EVENT_TYPE, timer_delay * 10) # long wait time + pygame.time.delay(timer_delay * 5) + self.assertNotIn(timer_event, pygame.event.get()) + pygame.time.set_timer(TIMER_EVENT_TYPE, timer_delay * 3) + pygame.time.delay(timer_delay * 7) + self.assertEqual(pygame.event.get().count(timer_event), 2) + pygame.time.set_timer(TIMER_EVENT_TYPE, timer_delay) + pygame.time.delay(int(timer_delay * 5.5)) + self.assertEqual(pygame.event.get().count(timer_event), 5) + + # Test that the loops=True works + pygame.time.set_timer(TIMER_EVENT_TYPE, 10, True) + pygame.time.delay(40) + self.assertEqual(pygame.event.get().count(timer_event), 1) + + # Test a variety of event objects, test loops argument + events_to_test = [ + pygame.event.Event(TIMER_EVENT_TYPE), + pygame.event.Event( + TIMER_EVENT_TYPE, foo="9gwz5", baz=12, lol=[124, (34, "")] + ), + pygame.event.Event(pygame.KEYDOWN, key=pygame.K_a, unicode="a"), + ] + repeat = 3 + millis = 50 + for e in events_to_test: + pygame.time.set_timer(e, millis, loops=repeat) + pygame.time.delay(2 * millis * repeat) + self.assertEqual(pygame.event.get().count(e), repeat) + pygame.quit() + + def test_wait(self): + """Tests time.wait() function.""" + millis = 100 # millisecond to wait on each iteration + iterations = 10 # number of iterations + delta = 50 # Represents acceptable margin of error for wait in ms + # Call checking function + self._wait_delay_check(pygame.time.wait, millis, iterations, delta) + # After timing behaviour, check argument type exceptions + self._type_error_checks(pygame.time.wait) + + def _wait_delay_check(self, func_to_check, millis, iterations, delta): + """ " + call func_to_check(millis) "iterations" times and check each time if + function "waited" for given millisecond (+- delta). At the end, take + average time for each call (whole_duration/iterations), which should + be equal to millis (+- delta - acceptable margin of error). + *Created to avoid code duplication during delay and wait tests + """ + # take starting time for duration calculation + start_time = time.time() + for i in range(iterations): + wait_time = func_to_check(millis) + # Check equality of wait_time and millis with margin of error delta + self.assertAlmostEqual(wait_time, millis, delta=delta) + stop_time = time.time() + # Cycle duration in millisecond + duration = round((stop_time - start_time) * 1000) + # Duration/Iterations should be (almost) equal to predefined millis + self.assertAlmostEqual(duration / iterations, millis, delta=delta) + + def _type_error_checks(self, func_to_check): + """Checks 3 TypeError (float, tuple, string) for the func_to_check""" + """Intended for time.delay and time.wait functions""" + # Those methods throw no exceptions on negative integers + self.assertRaises(TypeError, func_to_check, 0.1) # check float + self.assertRaises(TypeError, pygame.time.delay, (0, 1)) # check tuple + self.assertRaises(TypeError, pygame.time.delay, "10") # check string + + +############################################################################### + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/touch_test.py b/.venv/Lib/site-packages/pygame/tests/touch_test.py new file mode 100644 index 00000000..259a2c70 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/touch_test.py @@ -0,0 +1,97 @@ +import unittest +import os +import pygame +from pygame._sdl2 import touch +from pygame.tests.test_utils import question + + +has_touchdevice = touch.get_num_devices() > 0 + + +class TouchTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + pygame.display.init() + + @classmethod + def tearDownClass(cls): + pygame.display.quit() + + def test_num_devices(self): + touch.get_num_devices() + + @unittest.skipIf(not has_touchdevice, "no touch devices found") + def test_get_device(self): + touch.get_device(0) + + def test_get_device__invalid(self): + self.assertRaises(pygame.error, touch.get_device, -1234) + self.assertRaises(TypeError, touch.get_device, "test") + + @unittest.skipIf(not has_touchdevice, "no touch devices found") + def test_num_fingers(self): + touch.get_num_fingers(touch.get_device(0)) + + def test_num_fingers__invalid(self): + self.assertRaises(TypeError, touch.get_num_fingers, "test") + self.assertRaises(pygame.error, touch.get_num_fingers, -1234) + + +class TouchInteractiveTest(unittest.TestCase): + __tags__ = ["interactive"] + + @unittest.skipIf(not has_touchdevice, "no touch devices found") + def test_get_finger(self): + """ask for touch input and check the dict""" + + pygame.display.init() + pygame.font.init() + + os.environ["SDL_VIDEO_WINDOW_POS"] = "50,50" + screen = pygame.display.set_mode((800, 600)) + screen.fill((255, 255, 255)) + + font = pygame.font.Font(None, 32) + instructions_str_1 = "Please place some fingers on your touch device" + instructions_str_2 = ( + "Close the window when finished, " "and answer the question" + ) + inst_1_render = font.render(instructions_str_1, True, pygame.Color("#000000")) + inst_2_render = font.render(instructions_str_2, True, pygame.Color("#000000")) + + running = True + while running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + + finger_data_renders = [] + num_devices = pygame._sdl2.touch.get_num_devices() + if num_devices > 0: + first_device = pygame._sdl2.touch.get_device(0) + num_fingers = pygame._sdl2.touch.get_num_fingers(first_device) + if num_fingers > 0: + for finger_index in range(0, num_fingers): + data = pygame._sdl2.touch.get_finger(first_device, finger_index) + render = font.render( + f"finger - {data}", True, pygame.Color("#000000") + ) + + finger_data_renders.append(render) + + screen.fill((255, 255, 255)) + screen.blit(inst_1_render, (5, 5)) + screen.blit(inst_2_render, (5, 40)) + for index, finger in enumerate(finger_data_renders): + screen.blit(finger, (5, 80 + (index * 40))) + + pygame.display.update() + + response = question("Does the finger data seem correct?") + self.assertTrue(response) + + pygame.display.quit() + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/transform_test.py b/.venv/Lib/site-packages/pygame/tests/transform_test.py new file mode 100644 index 00000000..b7c64e91 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/transform_test.py @@ -0,0 +1,1420 @@ +import unittest +import os +import platform + +from pygame.tests import test_utils +from pygame.tests.test_utils import example_path + +import pygame +import pygame.transform +from pygame.locals import * + + +def show_image(s, images=[]): + # pygame.display.init() + size = s.get_rect()[2:] + screen = pygame.display.set_mode(size) + screen.blit(s, (0, 0)) + pygame.display.flip() + pygame.event.pump() + going = True + idx = 0 + while going: + events = pygame.event.get() + for e in events: + if e.type == QUIT: + going = False + if e.type == KEYDOWN: + if e.key in [K_s, K_a]: + if e.key == K_s: + idx += 1 + if e.key == K_a: + idx -= 1 + s = images[idx] + screen.blit(s, (0, 0)) + pygame.display.flip() + pygame.event.pump() + elif e.key in [K_ESCAPE]: + going = False + pygame.display.quit() + pygame.display.init() + + +def threshold( + return_surf, + surf, + color, + threshold=(0, 0, 0), + diff_color=(0, 0, 0), + change_return=True, +): + """given the color it makes return_surf only have areas with the given colour.""" + + width, height = surf.get_width(), surf.get_height() + + if change_return: + return_surf.fill(diff_color) + + try: + r, g, b = color + except ValueError: + r, g, b, a = color + + try: + tr, tg, tb = color + except ValueError: + tr, tg, tb, ta = color + + similar = 0 + for y in range(height): + for x in range(width): + c1 = surf.get_at((x, y)) + + if (abs(c1[0] - r) < tr) & (abs(c1[1] - g) < tg) & (abs(c1[2] - b) < tb): + # this pixel is within the threshold. + if change_return: + return_surf.set_at((x, y), c1) + similar += 1 + # else: + # print(c1, c2) + + return similar + + +class TransformModuleTest(unittest.TestCase): + def test_scale__alpha(self): + """see if set_alpha information is kept.""" + + s = pygame.Surface((32, 32)) + s.set_alpha(55) + self.assertEqual(s.get_alpha(), 55) + + s = pygame.Surface((32, 32)) + s.set_alpha(55) + s2 = pygame.transform.scale(s, (64, 64)) + s3 = s.copy() + self.assertEqual(s.get_alpha(), s3.get_alpha()) + self.assertEqual(s.get_alpha(), s2.get_alpha()) + + def test_scale__destination(self): + """see if the destination surface can be passed in to use.""" + + s = pygame.Surface((32, 32)) + s2 = pygame.transform.scale(s, (64, 64)) + s3 = s2.copy() + + # Also validate keyword arguments + s3 = pygame.transform.scale(surface=s, size=(64, 64), dest_surface=s3) + pygame.transform.scale(s, (64, 64), s2) + + # the wrong size surface is past in. Should raise an error. + self.assertRaises(ValueError, pygame.transform.scale, s, (33, 64), s3) + + s = pygame.Surface((32, 32)) + s2 = pygame.transform.smoothscale(s, (64, 64)) + s3 = s2.copy() + + # Also validate keyword arguments + s3 = pygame.transform.smoothscale(surface=s, size=(64, 64), dest_surface=s3) + + # the wrong size surface is past in. Should raise an error. + self.assertRaises(ValueError, pygame.transform.smoothscale, s, (33, 64), s3) + + def test_scale__vector2(self): + s = pygame.Surface((32, 32)) + s2 = pygame.transform.scale(s, pygame.Vector2(64, 64)) + s3 = pygame.transform.smoothscale(s, pygame.Vector2(64, 64)) + + self.assertEqual((64, 64), s2.get_size()) + self.assertEqual((64, 64), s3.get_size()) + + def test_scale__zero_surface_transform(self): + tmp_surface = pygame.transform.scale(pygame.Surface((128, 128)), (0, 0)) + self.assertEqual(tmp_surface.get_size(), (0, 0)) + tmp_surface = pygame.transform.scale(tmp_surface, (128, 128)) + self.assertEqual(tmp_surface.get_size(), (128, 128)) + + def test_scale_by(self): + s = pygame.Surface((32, 32)) + + s2 = pygame.transform.scale_by(s, 2) + self.assertEqual((64, 64), s2.get_size()) + + s2 = pygame.transform.scale_by(s, factor=(2.0, 1.5)) + self.assertEqual((64, 48), s2.get_size()) + + dest = pygame.Surface((64, 48)) + pygame.transform.scale_by(s, (2.0, 1.5), dest_surface=dest) + + def test_smoothscale_by(self): + s = pygame.Surface((32, 32)) + + s2 = pygame.transform.smoothscale_by(s, 2) + self.assertEqual((64, 64), s2.get_size()) + + s2 = pygame.transform.smoothscale_by(s, factor=(2.0, 1.5)) + self.assertEqual((64, 48), s2.get_size()) + + dest = pygame.Surface((64, 48)) + pygame.transform.smoothscale_by(s, (2.0, 1.5), dest_surface=dest) + + def test_grayscale(self): + s = pygame.Surface((32, 32)) + s.fill((255, 0, 0)) + + s2 = pygame.transform.grayscale(s) + self.assertEqual(pygame.transform.average_color(s2)[0], 76) + self.assertEqual(pygame.transform.average_color(s2)[1], 76) + self.assertEqual(pygame.transform.average_color(s2)[2], 76) + + dest = pygame.Surface((32, 32), depth=32) + pygame.transform.grayscale(s, dest) + self.assertEqual(pygame.transform.average_color(dest)[0], 76) + self.assertEqual(pygame.transform.average_color(dest)[1], 76) + self.assertEqual(pygame.transform.average_color(dest)[2], 76) + + dest = pygame.Surface((32, 32), depth=32) + s.fill((34, 12, 65)) + pygame.transform.grayscale(s, dest) + self.assertEqual(pygame.transform.average_color(dest)[0], 24) + self.assertEqual(pygame.transform.average_color(dest)[1], 24) + self.assertEqual(pygame.transform.average_color(dest)[2], 24) + + dest = pygame.Surface((32, 32), depth=32) + s.fill((123, 123, 123)) + pygame.transform.grayscale(s, dest) + self.assertIn(pygame.transform.average_color(dest)[0], [123, 122]) + self.assertIn(pygame.transform.average_color(dest)[1], [123, 122]) + self.assertIn(pygame.transform.average_color(dest)[2], [123, 122]) + + s = pygame.Surface((32, 32), depth=24) + s.fill((255, 0, 0)) + dest = pygame.Surface((32, 32), depth=24) + pygame.transform.grayscale(s, dest) + self.assertEqual(pygame.transform.average_color(dest)[0], 76) + self.assertEqual(pygame.transform.average_color(dest)[1], 76) + self.assertEqual(pygame.transform.average_color(dest)[2], 76) + + s = pygame.Surface((32, 32), depth=16) + s.fill((255, 0, 0)) + dest = pygame.Surface((32, 32), depth=16) + pygame.transform.grayscale(s, dest) + self.assertEqual(pygame.transform.average_color(dest)[0], 72) + self.assertEqual(pygame.transform.average_color(dest)[1], 76) + self.assertEqual(pygame.transform.average_color(dest)[2], 72) + + def test_threshold__honors_third_surface(self): + # __doc__ for threshold as of Tue 07/15/2008 + + # pygame.transform.threshold(DestSurface, Surface, color, threshold = + # (0,0,0,0), diff_color = (0,0,0,0), change_return = True, Surface = + # None): return num_threshold_pixels + + # When given the optional third + # surface, it would use the colors in that rather than the "color" + # specified in the function to check against. + + # New in pygame 1.8 + + ################################################################ + # Sizes + (w, h) = size = (32, 32) + + # the original_color is within the threshold of the threshold_color + threshold = (20, 20, 20, 20) + + original_color = (25, 25, 25, 25) + threshold_color = (10, 10, 10, 10) + + # Surfaces + original_surface = pygame.Surface(size, pygame.SRCALPHA, 32) + dest_surface = pygame.Surface(size, pygame.SRCALPHA, 32) + + # Third surface is used in lieu of 3rd position arg color + third_surface = pygame.Surface(size, pygame.SRCALPHA, 32) + + # Color filling + original_surface.fill(original_color) + third_surface.fill(threshold_color) + + ################################################################ + # All pixels for color should be within threshold + # + pixels_within_threshold = pygame.transform.threshold( + dest_surface=None, + surface=original_surface, + search_color=threshold_color, + threshold=threshold, + set_color=None, + set_behavior=0, + ) + + self.assertEqual(w * h, pixels_within_threshold) + + ################################################################ + # This should respect third_surface colors in place of 3rd arg + # color Should be the same as: surface.fill(threshold_color) + # all within threshold + + pixels_within_threshold = pygame.transform.threshold( + dest_surface=None, + surface=original_surface, + search_color=None, + threshold=threshold, + set_color=None, + set_behavior=0, + search_surf=third_surface, + ) + self.assertEqual(w * h, pixels_within_threshold) + + def test_threshold_dest_surf_not_change(self): + """the pixels within the threshold. + + All pixels not within threshold are changed to set_color. + So there should be none changed in this test. + """ + (w, h) = size = (32, 32) + threshold = (20, 20, 20, 20) + original_color = (25, 25, 25, 25) + original_dest_color = (65, 65, 65, 55) + threshold_color = (10, 10, 10, 10) + set_color = (255, 10, 10, 10) + + surf = pygame.Surface(size, pygame.SRCALPHA, 32) + dest_surf = pygame.Surface(size, pygame.SRCALPHA, 32) + search_surf = pygame.Surface(size, pygame.SRCALPHA, 32) + + surf.fill(original_color) + search_surf.fill(threshold_color) + dest_surf.fill(original_dest_color) + + # set_behavior=1, set dest_surface from set_color. + # all within threshold of third_surface, so no color is set. + + THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR = 1 + pixels_within_threshold = pygame.transform.threshold( + dest_surface=dest_surf, + surface=surf, + search_color=None, + threshold=threshold, + set_color=set_color, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR, + search_surf=search_surf, + ) + + # # Return, of pixels within threshold is correct + self.assertEqual(w * h, pixels_within_threshold) + + # # Size of dest surface is correct + dest_rect = dest_surf.get_rect() + dest_size = dest_rect.size + self.assertEqual(size, dest_size) + + # The color is not the change_color specified for every pixel As all + # pixels are within threshold + + for pt in test_utils.rect_area_pts(dest_rect): + self.assertNotEqual(dest_surf.get_at(pt), set_color) + self.assertEqual(dest_surf.get_at(pt), original_dest_color) + + def test_threshold_dest_surf_all_changed(self): + """Lowering the threshold, expecting changed surface""" + + (w, h) = size = (32, 32) + threshold = (20, 20, 20, 20) + original_color = (25, 25, 25, 25) + original_dest_color = (65, 65, 65, 55) + threshold_color = (10, 10, 10, 10) + set_color = (255, 10, 10, 10) + + surf = pygame.Surface(size, pygame.SRCALPHA, 32) + dest_surf = pygame.Surface(size, pygame.SRCALPHA, 32) + search_surf = pygame.Surface(size, pygame.SRCALPHA, 32) + + surf.fill(original_color) + search_surf.fill(threshold_color) + dest_surf.fill(original_dest_color) + + THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR = 1 + pixels_within_threshold = pygame.transform.threshold( + dest_surf, + surf, + search_color=None, + set_color=set_color, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR, + search_surf=search_surf, + ) + + self.assertEqual(0, pixels_within_threshold) + + dest_rect = dest_surf.get_rect() + dest_size = dest_rect.size + self.assertEqual(size, dest_size) + + # The color is the set_color specified for every pixel As all + # pixels are not within threshold + for pt in test_utils.rect_area_pts(dest_rect): + self.assertEqual(dest_surf.get_at(pt), set_color) + + def test_threshold_count(self): + """counts the colors, and not changes them.""" + surf_size = (32, 32) + surf = pygame.Surface(surf_size, pygame.SRCALPHA, 32) + search_surf = pygame.Surface(surf_size, pygame.SRCALPHA, 32) + search_color = (55, 55, 55, 255) + original_color = (10, 10, 10, 255) + + surf.fill(original_color) + # set 2 pixels to the color we are searching for. + surf.set_at((0, 0), search_color) + surf.set_at((12, 5), search_color) + + # There is no destination surface, but we ask to change it. + # This should be an error. + self.assertRaises( + TypeError, pygame.transform.threshold, None, surf, search_color + ) + # from pygame.transform import THRESHOLD_BEHAVIOR_COUNT + THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF = 2 + self.assertRaises( + TypeError, + pygame.transform.threshold, + None, + surf, + search_color, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, + ) + + THRESHOLD_BEHAVIOR_COUNT = 0 + num_threshold_pixels = pygame.transform.threshold( + dest_surface=None, + surface=surf, + search_color=search_color, + set_behavior=THRESHOLD_BEHAVIOR_COUNT, + ) + self.assertEqual(num_threshold_pixels, 2) + + def test_threshold_search_surf(self): + surf_size = (32, 32) + surf = pygame.Surface(surf_size, pygame.SRCALPHA, 32) + search_surf = pygame.Surface(surf_size, pygame.SRCALPHA, 32) + dest_surf = pygame.Surface(surf_size, pygame.SRCALPHA, 32) + + original_color = (10, 10, 10, 255) + search_color = (55, 55, 55, 255) + + surf.fill(original_color) + dest_surf.fill(original_color) + # set 2 pixels to the color we are searching for. + surf.set_at((0, 0), search_color) + surf.set_at((12, 5), search_color) + + search_surf.fill(search_color) + + # We look in the other surface for matching colors. + # Change it in dest_surf + THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF = 2 + + # TypeError: if search_surf is used, search_color should be None + self.assertRaises( + TypeError, + pygame.transform.threshold, + dest_surf, + surf, + search_color, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, + search_surf=search_surf, + ) + + # surf, dest_surf, and search_surf should all be the same size. + # Check surface sizes are the same size. + different_sized_surf = pygame.Surface((22, 33), pygame.SRCALPHA, 32) + self.assertRaises( + TypeError, + pygame.transform.threshold, + different_sized_surf, + surf, + search_color=None, + set_color=None, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, + search_surf=search_surf, + ) + + self.assertRaises( + TypeError, + pygame.transform.threshold, + dest_surf, + surf, + search_color=None, + set_color=None, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, + search_surf=different_sized_surf, + ) + + # We look to see if colors in search_surf are in surf. + num_threshold_pixels = pygame.transform.threshold( + dest_surface=dest_surf, + surface=surf, + search_color=None, + set_color=None, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, + search_surf=search_surf, + ) + + num_pixels_within = 2 + self.assertEqual(num_threshold_pixels, num_pixels_within) + + dest_surf.fill(original_color) + num_threshold_pixels = pygame.transform.threshold( + dest_surf, + surf, + search_color=None, + set_color=None, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, + search_surf=search_surf, + inverse_set=True, + ) + + self.assertEqual(num_threshold_pixels, 2) + + def test_threshold_inverse_set(self): + """changes the pixels within the threshold, and not outside.""" + surf_size = (32, 32) + _dest_surf = pygame.Surface(surf_size, pygame.SRCALPHA, 32) + _surf = pygame.Surface(surf_size, pygame.SRCALPHA, 32) + + dest_surf = _dest_surf # surface we are changing. + surf = _surf # surface we are looking at + search_color = (55, 55, 55, 255) # color we are searching for. + threshold = (0, 0, 0, 0) # within this distance from search_color. + set_color = (245, 245, 245, 255) # color we set. + inverse_set = 1 # pixels within threshold are changed to 'set_color' + + original_color = (10, 10, 10, 255) + surf.fill(original_color) + # set 2 pixels to the color we are searching for. + surf.set_at((0, 0), search_color) + surf.set_at((12, 5), search_color) + + dest_surf.fill(original_color) + # set 2 pixels to the color we are searching for. + dest_surf.set_at((0, 0), search_color) + dest_surf.set_at((12, 5), search_color) + + THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR = 1 + num_threshold_pixels = pygame.transform.threshold( + dest_surf, + surf, + search_color=search_color, + threshold=threshold, + set_color=set_color, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR, + inverse_set=1, + ) + + self.assertEqual(num_threshold_pixels, 2) + # only two pixels changed to diff_color. + self.assertEqual(dest_surf.get_at((0, 0)), set_color) + self.assertEqual(dest_surf.get_at((12, 5)), set_color) + + # other pixels should be the same as they were before. + # We just check one other pixel, not all of them. + self.assertEqual(dest_surf.get_at((2, 2)), original_color) + + # XXX + def test_threshold_non_src_alpha(self): + result = pygame.Surface((10, 10)) + s1 = pygame.Surface((10, 10)) + s2 = pygame.Surface((10, 10)) + s3 = pygame.Surface((10, 10)) + s4 = pygame.Surface((10, 10)) + + x = s1.fill((0, 0, 0)) + s1.set_at((0, 0), (32, 20, 0)) + + x = s2.fill((0, 20, 0)) + x = s3.fill((0, 0, 0)) + x = s4.fill((0, 0, 0)) + s2.set_at((0, 0), (33, 21, 0)) + s2.set_at((3, 0), (63, 61, 0)) + s3.set_at((0, 0), (112, 31, 0)) + s4.set_at((0, 0), (11, 31, 0)) + s4.set_at((1, 1), (12, 31, 0)) + + self.assertEqual(s1.get_at((0, 0)), (32, 20, 0, 255)) + self.assertEqual(s2.get_at((0, 0)), (33, 21, 0, 255)) + self.assertEqual((0, 0), (s1.get_flags(), s2.get_flags())) + + similar_color = (255, 255, 255, 255) + diff_color = (222, 0, 0, 255) + threshold_color = (20, 20, 20, 255) + + THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR = 1 + num_threshold_pixels = pygame.transform.threshold( + dest_surface=result, + surface=s1, + search_color=similar_color, + threshold=threshold_color, + set_color=diff_color, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR, + ) + self.assertEqual(num_threshold_pixels, 0) + + num_threshold_pixels = pygame.transform.threshold( + dest_surface=result, + surface=s1, + search_color=(40, 40, 0), + threshold=threshold_color, + set_color=diff_color, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR, + ) + self.assertEqual(num_threshold_pixels, 1) + + self.assertEqual(result.get_at((0, 0)), diff_color) + + def test_threshold__uneven_colors(self): + (w, h) = size = (16, 16) + + original_surface = pygame.Surface(size, pygame.SRCALPHA, 32) + dest_surface = pygame.Surface(size, pygame.SRCALPHA, 32) + + original_surface.fill(0) + + threshold_color_template = [5, 5, 5, 5] + threshold_template = [6, 6, 6, 6] + + ################################################################ + + for pos in range(len("rgb")): + threshold_color = threshold_color_template[:] + threshold = threshold_template[:] + + threshold_color[pos] = 45 + threshold[pos] = 50 + + pixels_within_threshold = pygame.transform.threshold( + None, + original_surface, + threshold_color, + threshold, + set_color=None, + set_behavior=0, + ) + + self.assertEqual(w * h, pixels_within_threshold) + + ################################################################ + + def test_threshold_set_behavior2(self): + """raises an error when set_behavior=2 and set_color is not None.""" + from pygame.transform import threshold + + s1 = pygame.Surface((32, 32), SRCALPHA, 32) + s2 = pygame.Surface((32, 32), SRCALPHA, 32) + THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF = 2 + self.assertRaises( + TypeError, + threshold, + dest_surface=s2, + surface=s1, + search_color=(30, 30, 30), + threshold=(11, 11, 11), + set_color=(255, 0, 0), + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, + ) + + def test_threshold_set_behavior0(self): + """raises an error when set_behavior=1 + and set_color is not None, + and dest_surf is not None. + """ + from pygame.transform import threshold + + s1 = pygame.Surface((32, 32), SRCALPHA, 32) + s2 = pygame.Surface((32, 32), SRCALPHA, 32) + THRESHOLD_BEHAVIOR_COUNT = 0 + + self.assertRaises( + TypeError, + threshold, + dest_surface=None, + surface=s2, + search_color=(30, 30, 30), + threshold=(11, 11, 11), + set_color=(0, 0, 0), + set_behavior=THRESHOLD_BEHAVIOR_COUNT, + ) + + self.assertRaises( + TypeError, + threshold, + dest_surface=s1, + surface=s2, + search_color=(30, 30, 30), + threshold=(11, 11, 11), + set_color=None, + set_behavior=THRESHOLD_BEHAVIOR_COUNT, + ) + + threshold( + dest_surface=None, + surface=s2, + search_color=(30, 30, 30), + threshold=(11, 11, 11), + set_color=None, + set_behavior=THRESHOLD_BEHAVIOR_COUNT, + ) + + def test_threshold_from_surface(self): + """Set similar pixels in 'dest_surf' to color in the 'surf'.""" + from pygame.transform import threshold + + surf = pygame.Surface((32, 32), SRCALPHA, 32) + dest_surf = pygame.Surface((32, 32), SRCALPHA, 32) + surf_color = (40, 40, 40, 255) + dest_color = (255, 255, 255) + surf.fill(surf_color) + dest_surf.fill(dest_color) + THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF = 2 + + num_threshold_pixels = threshold( + dest_surface=dest_surf, + surface=surf, + search_color=(30, 30, 30), + threshold=(11, 11, 11), + set_color=None, + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, + inverse_set=1, + ) + + self.assertEqual( + num_threshold_pixels, dest_surf.get_height() * dest_surf.get_width() + ) + self.assertEqual(dest_surf.get_at((0, 0)), surf_color) + + def test_threshold__surface(self): + """ """ + from pygame.transform import threshold + + s1 = pygame.Surface((32, 32), SRCALPHA, 32) + s2 = pygame.Surface((32, 32), SRCALPHA, 32) + s3 = pygame.Surface((1, 1), SRCALPHA, 32) + THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF = 2 + + # # only one pixel should not be changed. + # s1.fill((40,40,40)) + # s2.fill((255,255,255)) + # s1.set_at( (0,0), (170, 170, 170) ) + # # set the similar pixels in destination surface to the color + # # in the first surface. + # num_threshold_pixels = threshold( + # dest_surface=s2, + # surface=s1, + # search_color=(30,30,30), + # threshold=(11,11,11), + # set_color=None, + # set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF) + + # #num_threshold_pixels = threshold(s2, s1, (30,30,30)) + # self.assertEqual(num_threshold_pixels, (s1.get_height() * s1.get_width()) -1) + # self.assertEqual(s2.get_at((0,0)), (0,0,0, 255)) + # self.assertEqual(s2.get_at((0,1)), (40, 40, 40, 255)) + # self.assertEqual(s2.get_at((17,1)), (40, 40, 40, 255)) + + # # abs(40 - 255) < 100 + # #(abs(c1[0] - r) < tr) + + # s1.fill((160,160,160)) + # s2.fill((255,255,255)) + # num_threshold_pixels = threshold(s2, s1, (255,255,255), (100,100,100), (0,0,0), True) + + # self.assertEqual(num_threshold_pixels, (s1.get_height() * s1.get_width())) + + # only one pixel should not be changed. + s1.fill((40, 40, 40)) + s1.set_at((0, 0), (170, 170, 170)) + THRESHOLD_BEHAVIOR_COUNT = 0 + + num_threshold_pixels = threshold( + dest_surface=None, + surface=s1, + search_color=(30, 30, 30), + threshold=(11, 11, 11), + set_color=None, + set_behavior=THRESHOLD_BEHAVIOR_COUNT, + ) + + # num_threshold_pixels = threshold(s2, s1, (30,30,30)) + self.assertEqual(num_threshold_pixels, (s1.get_height() * s1.get_width()) - 1) + + # test end markers. 0, and 255 + + # the pixels are different by 1. + s1.fill((254, 254, 254)) + s2.fill((255, 255, 255)) + s3.fill((255, 255, 255)) + s1.set_at((0, 0), (170, 170, 170)) + num_threshold_pixels = threshold( + None, s1, (254, 254, 254), (1, 1, 1), None, THRESHOLD_BEHAVIOR_COUNT + ) + self.assertEqual(num_threshold_pixels, (s1.get_height() * s1.get_width()) - 1) + + # compare the two surfaces. Should be all but one matching. + num_threshold_pixels = threshold( + None, s1, None, (1, 1, 1), None, THRESHOLD_BEHAVIOR_COUNT, s2 + ) + self.assertEqual(num_threshold_pixels, (s1.get_height() * s1.get_width()) - 1) + + # within (0,0,0) threshold? Should match no pixels. + num_threshold_pixels = threshold( + None, s1, (253, 253, 253), (0, 0, 0), None, THRESHOLD_BEHAVIOR_COUNT + ) + self.assertEqual(num_threshold_pixels, 0) + + # other surface within (0,0,0) threshold? Should match no pixels. + num_threshold_pixels = threshold( + None, s1, None, (0, 0, 0), None, THRESHOLD_BEHAVIOR_COUNT, s2 + ) + self.assertEqual(num_threshold_pixels, 0) + + def test_threshold__subclassed_surface(self): + """Ensure threshold accepts subclassed surfaces.""" + expected_size = (13, 11) + expected_flags = 0 + expected_depth = 32 + expected_color = (90, 80, 70, 255) + expected_count = 0 + surface = test_utils.SurfaceSubclass( + expected_size, expected_flags, expected_depth + ) + dest_surface = test_utils.SurfaceSubclass( + expected_size, expected_flags, expected_depth + ) + search_surface = test_utils.SurfaceSubclass( + expected_size, expected_flags, expected_depth + ) + surface.fill((10, 10, 10)) + dest_surface.fill((255, 255, 255)) + search_surface.fill((20, 20, 20)) + + count = pygame.transform.threshold( + dest_surface=dest_surface, + surface=surface, + threshold=(1, 1, 1), + set_color=expected_color, + search_color=None, + search_surf=search_surface, + ) + + self.assertIsInstance(dest_surface, pygame.Surface) + self.assertIsInstance(dest_surface, test_utils.SurfaceSubclass) + self.assertEqual(count, expected_count) + self.assertEqual(dest_surface.get_at((0, 0)), expected_color) + self.assertEqual(dest_surface.get_bitsize(), expected_depth) + self.assertEqual(dest_surface.get_size(), expected_size) + self.assertEqual(dest_surface.get_flags(), expected_flags) + + def test_laplacian(self): + """ """ + + SIZE = 32 + s1 = pygame.Surface((SIZE, SIZE)) + s2 = pygame.Surface((SIZE, SIZE)) + s1.fill((10, 10, 70)) + pygame.draw.line(s1, (255, 0, 0), (3, 10), (20, 20)) + + # a line at the last row of the image. + pygame.draw.line(s1, (255, 0, 0), (0, 31), (31, 31)) + + pygame.transform.laplacian(s1, s2) + + # show_image(s1) + # show_image(s2) + + self.assertEqual(s2.get_at((0, 0)), (0, 0, 0, 255)) + self.assertEqual(s2.get_at((3, 10)), (255, 0, 0, 255)) + self.assertEqual(s2.get_at((0, 31)), (255, 0, 0, 255)) + self.assertEqual(s2.get_at((31, 31)), (255, 0, 0, 255)) + + # here we create the return surface. + s2 = pygame.transform.laplacian(s1) + + self.assertEqual(s2.get_at((0, 0)), (0, 0, 0, 255)) + self.assertEqual(s2.get_at((3, 10)), (255, 0, 0, 255)) + self.assertEqual(s2.get_at((0, 31)), (255, 0, 0, 255)) + self.assertEqual(s2.get_at((31, 31)), (255, 0, 0, 255)) + + def test_laplacian__24_big_endian(self): + """ """ + pygame.display.init() + try: + surf_1 = pygame.image.load( + example_path(os.path.join("data", "laplacian.png")) + ) + SIZE = 32 + surf_2 = pygame.Surface((SIZE, SIZE), 0, 24) + # s1.fill((10, 10, 70)) + # pygame.draw.line(s1, (255, 0, 0), (3, 10), (20, 20)) + + # a line at the last row of the image. + # pygame.draw.line(s1, (255, 0, 0), (0, 31), (31, 31)) + + # Also validate keyword arguments + pygame.transform.laplacian(surface=surf_1, dest_surface=surf_2) + + # show_image(s1) + # show_image(s2) + + self.assertEqual(surf_2.get_at((0, 0)), (0, 0, 0, 255)) + self.assertEqual(surf_2.get_at((3, 10)), (255, 0, 0, 255)) + self.assertEqual(surf_2.get_at((0, 31)), (255, 0, 0, 255)) + self.assertEqual(surf_2.get_at((31, 31)), (255, 0, 0, 255)) + + # here we create the return surface. + surf_2 = pygame.transform.laplacian(surf_1) + + self.assertEqual(surf_2.get_at((0, 0)), (0, 0, 0, 255)) + self.assertEqual(surf_2.get_at((3, 10)), (255, 0, 0, 255)) + self.assertEqual(surf_2.get_at((0, 31)), (255, 0, 0, 255)) + self.assertEqual(surf_2.get_at((31, 31)), (255, 0, 0, 255)) + finally: + pygame.display.quit() + + def test_average_surfaces(self): + """ """ + + SIZE = 32 + s1 = pygame.Surface((SIZE, SIZE)) + s2 = pygame.Surface((SIZE, SIZE)) + s3 = pygame.Surface((SIZE, SIZE)) + s1.fill((10, 10, 70)) + s2.fill((10, 20, 70)) + s3.fill((10, 130, 10)) + + surfaces = [s1, s2, s3] + surfaces = [s1, s2] + sr = pygame.transform.average_surfaces(surfaces) + + self.assertEqual(sr.get_at((0, 0)), (10, 15, 70, 255)) + + self.assertRaises(TypeError, pygame.transform.average_surfaces, 1) + self.assertRaises(TypeError, pygame.transform.average_surfaces, []) + + self.assertRaises(TypeError, pygame.transform.average_surfaces, [1]) + self.assertRaises(TypeError, pygame.transform.average_surfaces, [s1, 1]) + self.assertRaises(TypeError, pygame.transform.average_surfaces, [1, s1]) + self.assertRaises(TypeError, pygame.transform.average_surfaces, [s1, s2, 1]) + + self.assertRaises( + TypeError, pygame.transform.average_surfaces, (s for s in [s1, s2, s3]) + ) + + def test_average_surfaces__24(self): + SIZE = 32 + depth = 24 + s1 = pygame.Surface((SIZE, SIZE), 0, depth) + s2 = pygame.Surface((SIZE, SIZE), 0, depth) + s3 = pygame.Surface((SIZE, SIZE), 0, depth) + s1.fill((10, 10, 70, 255)) + s2.fill((10, 20, 70, 255)) + s3.fill((10, 130, 10, 255)) + + surfaces = [s1, s2, s3] + sr = pygame.transform.average_surfaces(surfaces) + self.assertEqual(sr.get_masks(), s1.get_masks()) + self.assertEqual(sr.get_flags(), s1.get_flags()) + self.assertEqual(sr.get_losses(), s1.get_losses()) + + if 0: + print(sr, s1) + print(sr.get_masks(), s1.get_masks()) + print(sr.get_flags(), s1.get_flags()) + print(sr.get_losses(), s1.get_losses()) + print(sr.get_shifts(), s1.get_shifts()) + + self.assertEqual(sr.get_at((0, 0)), (10, 53, 50, 255)) + + def test_average_surfaces__24_big_endian(self): + pygame.display.init() + try: + surf_1 = pygame.image.load(example_path(os.path.join("data", "BGR.png"))) + + surf_2 = surf_1.copy() + + surfaces = [surf_1, surf_2] + self.assertEqual(surf_1.get_at((0, 0)), (255, 0, 0, 255)) + self.assertEqual(surf_2.get_at((0, 0)), (255, 0, 0, 255)) + + surf_av = pygame.transform.average_surfaces(surfaces) + self.assertEqual(surf_av.get_masks(), surf_1.get_masks()) + self.assertEqual(surf_av.get_flags(), surf_1.get_flags()) + self.assertEqual(surf_av.get_losses(), surf_1.get_losses()) + + self.assertEqual(surf_av.get_at((0, 0)), (255, 0, 0, 255)) + finally: + pygame.display.quit() + + def test_average_surfaces__subclassed_surfaces(self): + """Ensure average_surfaces accepts subclassed surfaces.""" + expected_size = (23, 17) + expected_flags = 0 + expected_depth = 32 + expected_color = (50, 50, 50, 255) + surfaces = [] + + for color in ((40, 60, 40), (60, 40, 60)): + s = test_utils.SurfaceSubclass( + expected_size, expected_flags, expected_depth + ) + s.fill(color) + surfaces.append(s) + + surface = pygame.transform.average_surfaces(surfaces) + + self.assertIsInstance(surface, pygame.Surface) + self.assertNotIsInstance(surface, test_utils.SurfaceSubclass) + self.assertEqual(surface.get_at((0, 0)), expected_color) + self.assertEqual(surface.get_bitsize(), expected_depth) + self.assertEqual(surface.get_size(), expected_size) + self.assertEqual(surface.get_flags(), expected_flags) + + def test_average_surfaces__subclassed_destination_surface(self): + """Ensure average_surfaces accepts a destination subclassed surface.""" + expected_size = (13, 27) + expected_flags = 0 + expected_depth = 32 + expected_color = (15, 15, 15, 255) + surfaces = [] + + for color in ((10, 10, 20), (20, 20, 10), (30, 30, 30)): + s = test_utils.SurfaceSubclass( + expected_size, expected_flags, expected_depth + ) + s.fill(color) + surfaces.append(s) + expected_dest_surface = surfaces.pop() + + # Also validate keyword arguments + dest_surface = pygame.transform.average_surfaces( + surfaces=surfaces, dest_surface=expected_dest_surface + ) + + self.assertIsInstance(dest_surface, pygame.Surface) + self.assertIsInstance(dest_surface, test_utils.SurfaceSubclass) + self.assertIs(dest_surface, expected_dest_surface) + self.assertEqual(dest_surface.get_at((0, 0)), expected_color) + self.assertEqual(dest_surface.get_bitsize(), expected_depth) + self.assertEqual(dest_surface.get_size(), expected_size) + self.assertEqual(dest_surface.get_flags(), expected_flags) + + def test_average_color(self): + """ """ + for i in (24, 32): + with self.subTest(f"Testing {i}-bit surface"): + s = pygame.Surface((32, 32), 0, i) + s.fill((0, 100, 200)) + s.fill((10, 50, 100), (0, 0, 16, 32)) + + self.assertEqual(pygame.transform.average_color(s), (5, 75, 150, 0)) + + # Also validate keyword arguments + avg_color = pygame.transform.average_color( + surface=s, rect=(16, 0, 16, 32) + ) + self.assertEqual(avg_color, (0, 100, 200, 0)) + + def test_average_color_considering_alpha_all_pixels_opaque(self): + """ """ + s = pygame.Surface((32, 32), pygame.SRCALPHA, 32) + s.fill((0, 100, 200, 255)) + s.fill((10, 50, 100, 255), (0, 0, 16, 32)) + + self.assertEqual( + pygame.transform.average_color(s, consider_alpha=True), (5, 75, 150, 255) + ) + + # Also validate keyword arguments + avg_color = pygame.transform.average_color( + surface=s, rect=(16, 0, 16, 32), consider_alpha=True + ) + self.assertEqual(avg_color, (0, 100, 200, 255)) + + def test_average_color_considering_alpha(self): + """ """ + s = pygame.Surface((32, 32), pygame.SRCALPHA, 32) + s.fill((0, 100, 200, 255)) + s.fill((10, 50, 100, 128), (0, 0, 16, 32)) + + # formula for this example of half filled square + # n = number of pixels, e.g. 32 * 32 + # rgb = (n/2 * ( a_left * rgb_left) + n/2 (a_right * rgb_right) ) / (n/2 * a_left + n/2 * a_right) + # a = (n/2 * a_left + n/2 * a_right) / n + self.assertEqual( + pygame.transform.average_color(s, consider_alpha=True), (3, 83, 166, 191) + ) + + # Also validate keyword arguments + avg_color = pygame.transform.average_color( + surface=s, rect=(0, 0, 16, 32), consider_alpha=True + ) + self.assertEqual(avg_color, (10, 50, 100, 128)) + + def test_rotate(self): + # setting colors and canvas + blue = (0, 0, 255, 255) + red = (255, 0, 0, 255) + black = (0, 0, 0) + canvas = pygame.Surface((3, 3)) + rotation = 0 + + canvas.set_at((2, 0), blue) + canvas.set_at((0, 2), red) + + self.assertEqual(canvas.get_at((0, 0)), black) + self.assertEqual(canvas.get_at((2, 0)), blue) + self.assertEqual(canvas.get_at((0, 2)), red) + + for i in range(0, 4): + if i % 2 == 0: + self.assertEqual(canvas.get_at((0, 0)), black) + elif i == 1: + self.assertEqual(canvas.get_at((0, 0)), blue) + elif i == 3: + self.assertEqual(canvas.get_at((0, 0)), red) + + rotation += 90 + # Also validate keyword arguments + canvas = pygame.transform.rotate(surface=canvas, angle=90) + + self.assertEqual(canvas.get_at((0, 0)), black) + + def test_rotate_of_0_sized_surface(self): + # This function just tests possible Segmentation Fault + canvas1 = pygame.Surface((0, 1)) + canvas2 = pygame.Surface((1, 0)) + pygame.transform.rotate(canvas1, 42) + pygame.transform.rotate(canvas2, 42) + + def test_rotate__lossless_at_90_degrees(self): + w, h = 32, 32 + s = pygame.Surface((w, h), pygame.SRCALPHA) + + gradient = list(test_utils.gradient(w, h)) + + for pt, color in gradient: + s.set_at(pt, color) + + for rotation in (90, -90): + s = pygame.transform.rotate(s, rotation) + + for pt, color in gradient: + self.assertTrue(s.get_at(pt) == color) + + def test_scale2x(self): + # __doc__ (as of 2008-06-25) for pygame.transform.scale2x: + + # pygame.transform.scale2x(Surface, DestSurface = None): Surface + # specialized image doubler + + w, h = 32, 32 + s = pygame.Surface((w, h), pygame.SRCALPHA, 32) + + # s.set_at((0,0), (20, 20, 20, 255)) + + s1 = pygame.transform.scale2x(s) + # Also validate keyword arguments + s2 = pygame.transform.scale2x(surface=s) + self.assertEqual(s1.get_rect().size, (64, 64)) + self.assertEqual(s2.get_rect().size, (64, 64)) + + def test_scale2xraw(self): + w, h = 32, 32 + s = pygame.Surface((w, h), pygame.SRCALPHA, 32) + s.fill((0, 0, 0)) + pygame.draw.circle(s, (255, 0, 0), (w // 2, h // 2), (w // 3)) + + s2 = pygame.transform.scale(s, (w * 2, h * 2)) + s2_2 = pygame.transform.scale(s2, (w * 4, h * 4)) + s4 = pygame.transform.scale(s, (w * 4, h * 4)) + + self.assertEqual(s2_2.get_rect().size, (128, 128)) + + for pt in test_utils.rect_area_pts(s2_2.get_rect()): + self.assertEqual(s2_2.get_at(pt), s4.get_at(pt)) + + def test_get_smoothscale_backend(self): + filter_type = pygame.transform.get_smoothscale_backend() + self.assertTrue(filter_type in ["GENERIC", "MMX", "SSE"]) + # It would be nice to test if a non-generic type corresponds to an x86 + # processor. But there is no simple test for this. platform.machine() + # returns process version specific information, like 'i686'. + + def test_set_smoothscale_backend(self): + # All machines should allow 'GENERIC'. + original_type = pygame.transform.get_smoothscale_backend() + pygame.transform.set_smoothscale_backend("GENERIC") + filter_type = pygame.transform.get_smoothscale_backend() + self.assertEqual(filter_type, "GENERIC") + # All machines should allow returning to original value. + # Also check that keyword argument works. + pygame.transform.set_smoothscale_backend(backend=original_type) + + # Something invalid. + def change(): + pygame.transform.set_smoothscale_backend("mmx") + + self.assertRaises(ValueError, change) + + # Invalid argument keyword. + def change(): + pygame.transform.set_smoothscale_backend(t="GENERIC") + + self.assertRaises(TypeError, change) + + # Invalid argument type. + def change(): + pygame.transform.set_smoothscale_backend(1) + + self.assertRaises(TypeError, change) + # Unsupported type, if possible. + if original_type != "SSE": + + def change(): + pygame.transform.set_smoothscale_backend("SSE") + + self.assertRaises(ValueError, change) + # Should be back where we started. + filter_type = pygame.transform.get_smoothscale_backend() + self.assertEqual(filter_type, original_type) + + def test_chop(self): + original_surface = pygame.Surface((20, 20)) + pygame.draw.rect(original_surface, (255, 0, 0), (0, 0, 10, 10)) + pygame.draw.rect(original_surface, (0, 255, 0), (0, 10, 10, 10)) + pygame.draw.rect(original_surface, (0, 0, 255), (10, 0, 10, 10)) + pygame.draw.rect(original_surface, (255, 255, 0), (10, 10, 10, 10)) + # Test chopping the corner of image + rect = pygame.Rect(0, 0, 5, 15) + test_surface = pygame.transform.chop(original_surface, rect) + # Check the size of chopped image + self.assertEqual(test_surface.get_size(), (15, 5)) + # Check if the colors of the chopped image are correct + for x in range(15): + for y in range(5): + if x < 5: + self.assertEqual(test_surface.get_at((x, y)), (0, 255, 0)) + else: + self.assertEqual(test_surface.get_at((x, y)), (255, 255, 0)) + # Check if the original image stayed the same + self.assertEqual(original_surface.get_size(), (20, 20)) + for x in range(20): + for y in range(20): + if x < 10 and y < 10: + self.assertEqual(original_surface.get_at((x, y)), (255, 0, 0)) + if x < 10 < y: + self.assertEqual(original_surface.get_at((x, y)), (0, 255, 0)) + if x > 10 > y: + self.assertEqual(original_surface.get_at((x, y)), (0, 0, 255)) + if x > 10 and y > 10: + self.assertEqual(original_surface.get_at((x, y)), (255, 255, 0)) + # Test chopping the center of the surface: + rect = pygame.Rect(0, 0, 10, 10) + rect.center = original_surface.get_rect().center + # Also validate keyword arguments + test_surface = pygame.transform.chop(surface=original_surface, rect=rect) + self.assertEqual(test_surface.get_size(), (10, 10)) + for x in range(10): + for y in range(10): + if x < 5 and y < 5: + self.assertEqual(test_surface.get_at((x, y)), (255, 0, 0)) + if x < 5 < y: + self.assertEqual(test_surface.get_at((x, y)), (0, 255, 0)) + if x > 5 > y: + self.assertEqual(test_surface.get_at((x, y)), (0, 0, 255)) + if x > 5 and y > 5: + self.assertEqual(test_surface.get_at((x, y)), (255, 255, 0)) + # Test chopping with the empty rect + rect = pygame.Rect(10, 10, 0, 0) + test_surface = pygame.transform.chop(original_surface, rect) + self.assertEqual(test_surface.get_size(), (20, 20)) + # Test chopping the entire surface + rect = pygame.Rect(0, 0, 20, 20) + test_surface = pygame.transform.chop(original_surface, rect) + self.assertEqual(test_surface.get_size(), (0, 0)) + # Test chopping outside of surface + rect = pygame.Rect(5, 15, 20, 20) + test_surface = pygame.transform.chop(original_surface, rect) + self.assertEqual(test_surface.get_size(), (5, 15)) + rect = pygame.Rect(400, 400, 10, 10) + test_surface = pygame.transform.chop(original_surface, rect) + self.assertEqual(test_surface.get_size(), (20, 20)) + + def test_rotozoom(self): + # __doc__ (as of 2008-08-02) for pygame.transform.rotozoom: + + # pygame.transform.rotozoom(Surface, angle, scale): return Surface + # filtered scale and rotation + # + # This is a combined scale and rotation transform. The resulting + # Surface will be a filtered 32-bit Surface. The scale argument is a + # floating point value that will be multiplied by the current + # resolution. The angle argument is a floating point value that + # represents the counterclockwise degrees to rotate. A negative + # rotation angle will rotate clockwise. + + s = pygame.Surface((10, 0)) + pygame.transform.scale(s, (10, 2)) + s1 = pygame.transform.rotozoom(s, 30, 1) + # Also validate keyword arguments + s2 = pygame.transform.rotozoom(surface=s, angle=30, scale=1) + + self.assertEqual(s1.get_rect(), pygame.Rect(0, 0, 0, 0)) + self.assertEqual(s2.get_rect(), pygame.Rect(0, 0, 0, 0)) + + def test_smoothscale(self): + """Tests the stated boundaries, sizing, and color blending of smoothscale function""" + # __doc__ (as of 2008-08-02) for pygame.transform.smoothscale: + + # pygame.transform.smoothscale(Surface, (width, height), DestSurface = + # None): return Surface + # + # scale a surface to an arbitrary size smoothly + # + # Uses one of two different algorithms for scaling each dimension of + # the input surface as required. For shrinkage, the output pixels are + # area averages of the colors they cover. For expansion, a bilinear + # filter is used. For the amd64 and i686 architectures, optimized MMX + # routines are included and will run much faster than other machine + # types. The size is a 2 number sequence for (width, height). This + # function only works for 24-bit or 32-bit surfaces. An exception + # will be thrown if the input surface bit depth is less than 24. + # + # New in pygame 1.8 + + # check stated exceptions + def smoothscale_low_bpp(): + starting_surface = pygame.Surface((20, 20), depth=12) + smoothscaled_surface = pygame.transform.smoothscale( + starting_surface, (10, 10) + ) + + self.assertRaises(ValueError, smoothscale_low_bpp) + + def smoothscale_high_bpp(): + starting_surface = pygame.Surface((20, 20), depth=48) + smoothscaled_surface = pygame.transform.smoothscale( + starting_surface, (10, 10) + ) + + self.assertRaises(ValueError, smoothscale_high_bpp) + + def smoothscale_invalid_scale(): + starting_surface = pygame.Surface((20, 20), depth=32) + smoothscaled_surface = pygame.transform.smoothscale( + starting_surface, (-1, -1) + ) + + self.assertRaises(ValueError, smoothscale_invalid_scale) + + # Test Color Blending Scaling-Up + two_pixel_surface = pygame.Surface((2, 1), depth=32) + two_pixel_surface.fill(pygame.Color(0, 0, 0), pygame.Rect(0, 0, 1, 1)) + two_pixel_surface.fill(pygame.Color(255, 255, 255), pygame.Rect(1, 0, 1, 1)) + for k in [2**x for x in range(5, 8)]: # Enlarge to targets 32, 64...256 + bigger_surface = pygame.transform.smoothscale(two_pixel_surface, (k, 1)) + self.assertEqual( + bigger_surface.get_at((k // 2, 0)), pygame.Color(127, 127, 127) + ) + self.assertEqual(bigger_surface.get_size(), (k, 1)) + # Test Color Blending Scaling-Down + two_five_six_surf = pygame.Surface((256, 1), depth=32) + two_five_six_surf.fill(pygame.Color(0, 0, 0), pygame.Rect(0, 0, 128, 1)) + two_five_six_surf.fill(pygame.Color(255, 255, 255), pygame.Rect(128, 0, 128, 1)) + for k in range(3, 11, 2): # Shrink to targets 3, 5...11 pixels wide + smaller_surface = pygame.transform.smoothscale(two_five_six_surf, (k, 1)) + self.assertEqual( + smaller_surface.get_at(((k // 2), 0)), pygame.Color(127, 127, 127) + ) + self.assertEqual(smaller_surface.get_size(), (k, 1)) + + +class TransformDisplayModuleTest(unittest.TestCase): + def setUp(self): + pygame.display.init() + pygame.display.set_mode((320, 200)) + + def tearDown(self): + pygame.display.quit() + + def test_flip(self): + """honors the set_color key on the returned surface from flip.""" + image_loaded = pygame.image.load(example_path("data/chimp.png")) + + image = pygame.Surface(image_loaded.get_size(), 0, 32) + image.blit(image_loaded, (0, 0)) + + image_converted = image_loaded.convert() + + self.assertFalse(image.get_flags() & pygame.SRCALPHA) + self.assertFalse(image_converted.get_flags() & pygame.SRCALPHA) + + surf = pygame.Surface(image.get_size(), 0, 32) + surf2 = pygame.Surface(image.get_size(), 0, 32) + + surf.fill((255, 255, 255)) + surf2.fill((255, 255, 255)) + + colorkey = image.get_at((0, 0)) + image.set_colorkey(colorkey, RLEACCEL) + timage = pygame.transform.flip(image, 1, 0) + + colorkey = image_converted.get_at((0, 0)) + image_converted.set_colorkey(colorkey, RLEACCEL) + # Also validate keyword arguments + timage_converted = pygame.transform.flip( + surface=image_converted, flip_x=1, flip_y=0 + ) + + # blit the flipped surface, and non flipped surface. + surf.blit(timage, (0, 0)) + surf2.blit(image, (0, 0)) + + # the results should be the same. + self.assertEqual(surf.get_at((0, 0)), surf2.get_at((0, 0))) + self.assertEqual(surf2.get_at((0, 0)), (255, 255, 255, 255)) + + # now we test the convert() ed image also works. + surf.fill((255, 255, 255)) + surf2.fill((255, 255, 255)) + surf.blit(timage_converted, (0, 0)) + surf2.blit(image_converted, (0, 0)) + self.assertEqual(surf.get_at((0, 0)), surf2.get_at((0, 0))) + + def test_flip_alpha(self): + """returns a surface with the same properties as the input.""" + image_loaded = pygame.image.load(example_path("data/chimp.png")) + + image_alpha = pygame.Surface(image_loaded.get_size(), pygame.SRCALPHA, 32) + image_alpha.blit(image_loaded, (0, 0)) + + surf = pygame.Surface(image_loaded.get_size(), 0, 32) + surf2 = pygame.Surface(image_loaded.get_size(), 0, 32) + + colorkey = image_alpha.get_at((0, 0)) + image_alpha.set_colorkey(colorkey, RLEACCEL) + timage_alpha = pygame.transform.flip(image_alpha, 1, 0) + + self.assertTrue(image_alpha.get_flags() & pygame.SRCALPHA) + self.assertTrue(timage_alpha.get_flags() & pygame.SRCALPHA) + + # now we test the alpha image works. + surf.fill((255, 255, 255)) + surf2.fill((255, 255, 255)) + surf.blit(timage_alpha, (0, 0)) + surf2.blit(image_alpha, (0, 0)) + self.assertEqual(surf.get_at((0, 0)), surf2.get_at((0, 0))) + self.assertEqual(surf2.get_at((0, 0)), (255, 0, 0, 255)) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/version_test.py b/.venv/Lib/site-packages/pygame/tests/version_test.py new file mode 100644 index 00000000..ba0bb3d0 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/version_test.py @@ -0,0 +1,48 @@ +import os +import unittest + + +pg_header = os.path.join("src_c", "include", "_pygame.h") + + +class VersionTest(unittest.TestCase): + @unittest.skipIf( + not os.path.isfile(pg_header), "Skipping because we cannot find _pygame.h" + ) + def test_pg_version_consistency(self): + from pygame import version + + pgh_major = -1 + pgh_minor = -1 + pgh_patch = -1 + import re + + major_exp_search = re.compile(r"define\s+PG_MAJOR_VERSION\s+([0-9]+)").search + minor_exp_search = re.compile(r"define\s+PG_MINOR_VERSION\s+([0-9]+)").search + patch_exp_search = re.compile(r"define\s+PG_PATCH_VERSION\s+([0-9]+)").search + with open(pg_header) as f: + for line in f: + if pgh_major == -1: + m = major_exp_search(line) + if m: + pgh_major = int(m.group(1)) + if pgh_minor == -1: + m = minor_exp_search(line) + if m: + pgh_minor = int(m.group(1)) + if pgh_patch == -1: + m = patch_exp_search(line) + if m: + pgh_patch = int(m.group(1)) + self.assertEqual(pgh_major, version.vernum[0]) + self.assertEqual(pgh_minor, version.vernum[1]) + self.assertEqual(pgh_patch, version.vernum[2]) + + def test_sdl_version(self): + from pygame import version + + self.assertEqual(len(version.SDL), 3) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/tests/video_test.py b/.venv/Lib/site-packages/pygame/tests/video_test.py new file mode 100644 index 00000000..55ef4149 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/tests/video_test.py @@ -0,0 +1,26 @@ +import unittest +import sys +import pygame + +from pygame._sdl2 import video + + +class VideoModuleTest(unittest.TestCase): + default_caption = "pygame window" + + @unittest.skipIf( + not (sys.maxsize > 2**32), + "32 bit SDL 2.0.16 has an issue.", + ) + def test_renderer_set_viewport(self): + """works.""" + window = video.Window(title=self.default_caption, size=(800, 600)) + renderer = video.Renderer(window=window) + renderer.logical_size = (1920, 1080) + rect = pygame.Rect(0, 0, 1920, 1080) + renderer.set_viewport(rect) + self.assertEqual(renderer.get_viewport(), (0, 0, 1920, 1080)) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/Lib/site-packages/pygame/threads/__init__.py b/.venv/Lib/site-packages/pygame/threads/__init__.py new file mode 100644 index 00000000..6337349b --- /dev/null +++ b/.venv/Lib/site-packages/pygame/threads/__init__.py @@ -0,0 +1,271 @@ +""" +* Experimental * + +Like the map function, but can use a pool of threads. + +Really easy to use threads. eg. tmap(f, alist) + +If you know how to use the map function, you can use threads. +""" + +__author__ = "Rene Dudfield" +__version__ = "0.3.0" +__license__ = "Python license" + +from queue import Queue, Empty +import threading + + +Thread = threading.Thread + +STOP = object() +FINISH = object() + +# DONE_ONE = object() +# DONE_TWO = object() + +# a default worker queue. +_wq = None + +# if we are using threads or not. This is the number of workers. +_use_workers = 0 + +# Set this to the maximum for the amount of Cores/CPUs +# Note, that the tests early out. +# So it should only test the best number of workers +2 +MAX_WORKERS_TO_TEST = 64 + + +def init(number_of_workers=0): + """Does a little test to see if threading is worth it. + Sets up a global worker queue if it's worth it. + + Calling init() is not required, but is generally better to do. + """ + global _wq, _use_workers + + if number_of_workers: + _use_workers = number_of_workers + else: + _use_workers = benchmark_workers() + + # if it is best to use zero workers, then use that. + _wq = WorkerQueue(_use_workers) + + +def quit(): + """cleans up everything.""" + global _wq, _use_workers + _wq.stop() + _wq = None + _use_workers = False + + +def benchmark_workers(a_bench_func=None, the_data=None): + """does a little test to see if workers are at all faster. + Returns the number of workers which works best. + Takes a little bit of time to run, so you should only really call + it once. + You can pass in benchmark data, and functions if you want. + a_bench_func - f(data) + the_data - data to work on. + """ + # TODO: try and make this scale better with slower/faster cpus. + # first find some variables so that using 0 workers takes about 1.0 seconds. + # then go from there. + + # note, this will only work with pygame 1.8rc3+ + # replace the doit() and the_data with something that releases the GIL + + import pygame + import pygame.transform + import time + + if not a_bench_func: + + def doit(x): + return pygame.transform.scale(x, (544, 576)) + + else: + doit = a_bench_func + + if not the_data: + thedata = [pygame.Surface((155, 155), 0, 32) for x in range(10)] + else: + thedata = the_data + + best = time.time() + 100000000 + best_number = 0 + # last_best = -1 + + for num_workers in range(0, MAX_WORKERS_TO_TEST): + wq = WorkerQueue(num_workers) + t1 = time.time() + for _ in range(20): + print(f"active count:{threading.active_count()}") + tmap(doit, thedata, worker_queue=wq) + t2 = time.time() + + wq.stop() + + total_time = t2 - t1 + print(f"total time num_workers:{num_workers}: time:{total_time}:") + + if total_time < best: + # last_best = best_number + best_number = num_workers + best = total_time + + if num_workers - best_number > 1: + # We tried to add more, but it didn't like it. + # so we stop with testing at this number. + break + + return best_number + + +class WorkerQueue: + def __init__(self, num_workers=20): + self.queue = Queue() + self.pool = [] + self._setup_workers(num_workers) + + def _setup_workers(self, num_workers): + """Sets up the worker threads + NOTE: undefined behaviour if you call this again. + """ + self.pool = [] + + for _ in range(num_workers): + self.pool.append(Thread(target=self.threadloop)) + + for a_thread in self.pool: + a_thread.setDaemon(True) + a_thread.start() + + def do(self, f, *args, **kwArgs): + """puts a function on a queue for running later.""" + self.queue.put((f, args, kwArgs)) + + def stop(self): + """Stops the WorkerQueue, waits for all of the threads to finish up.""" + self.queue.put(STOP) + for thread in self.pool: + thread.join() + + def threadloop(self): # , finish=False): + """Loops until all of the tasks are finished.""" + while True: + args = self.queue.get() + if args is STOP: + self.queue.put(STOP) + self.queue.task_done() + break + try: + args[0](*args[1], **args[2]) + finally: + # clean up the queue, raise the exception. + self.queue.task_done() + # raise + + def wait(self): + """waits until all tasks are complete.""" + self.queue.join() + + +class FuncResult: + """Used for wrapping up a function call so that the results are stored + inside the instances result attribute. + """ + + def __init__(self, f, callback=None, errback=None): + """f - is the function we that we call + callback(result) - this is called when the function(f) returns + errback(exception) - this is called when the function(f) raises + an exception. + """ + self.f = f + self.exception = None + self.result = None + self.callback = callback + self.errback = errback + + def __call__(self, *args, **kwargs): + # we try to call the function here. If it fails we store the exception. + try: + self.result = self.f(*args, **kwargs) + if self.callback: + self.callback(self.result) + except Exception as e: + self.exception = e + if self.errback: + self.errback(self.exception) + + +def tmap(f, seq_args, num_workers=20, worker_queue=None, wait=True, stop_on_error=True): + """like map, but uses a thread pool to execute. + num_workers - the number of worker threads that will be used. If pool + is passed in, then the num_workers arg is ignored. + worker_queue - you can optionally pass in an existing WorkerQueue. + wait - True means that the results are returned when everything is finished. + False means that we return the [worker_queue, results] right away instead. + results, is returned as a list of FuncResult instances. + stop_on_error - + """ + + if worker_queue: + wq = worker_queue + else: + # see if we have a global queue to work with. + if _wq: + wq = _wq + else: + if num_workers == 0: + return map(f, seq_args) + + wq = WorkerQueue(num_workers) + + # we short cut it here if the number of workers is 0. + # normal map should be faster in this case. + if len(wq.pool) == 0: + return map(f, seq_args) + + # print("queue size:%s" % wq.queue.qsize()) + + # TODO: divide the data (seq_args) into even chunks and + # then pass each thread a map(f, equal_part(seq_args)) + # That way there should be less locking, and overhead. + + results = [] + for sa in seq_args: + results.append(FuncResult(f)) + wq.do(results[-1], sa) + + # wq.stop() + + if wait: + # print("wait") + wq.wait() + # print("after wait") + # print("queue size:%s" % wq.queue.qsize()) + if wq.queue.qsize(): + raise RuntimeError("buggy threadmap") + # if we created a worker queue, we need to stop it. + if not worker_queue and not _wq: + # print("stopping") + wq.stop() + if wq.queue.qsize(): + um = wq.queue.get() + if not um is STOP: + raise RuntimeError("buggy threadmap") + + # see if there were any errors. If so raise the first one. This matches map behaviour. + # TODO: the traceback doesn't show up nicely. + # NOTE: TODO: we might want to return the results anyway? This should be an option. + if stop_on_error: + error_ones = list(filter(lambda x: x.exception, results)) + if error_ones: + raise error_ones[0].exception + + return map(lambda x: x.result, results) + return [wq, results] diff --git a/.venv/Lib/site-packages/pygame/threads/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/pygame/threads/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 00000000..fa41333c Binary files /dev/null and b/.venv/Lib/site-packages/pygame/threads/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/pygame/time.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/time.cp311-win_amd64.pyd new file mode 100644 index 00000000..be739524 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/time.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/time.pyi b/.venv/Lib/site-packages/pygame/time.pyi new file mode 100644 index 00000000..6183def4 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/time.pyi @@ -0,0 +1,15 @@ +from typing import Union, final + +from pygame.event import Event + +def get_ticks() -> int: ... +def wait(milliseconds: int) -> int: ... +def delay(milliseconds: int) -> int: ... +def set_timer(event: Union[int, Event], millis: int, loops: int = 0) -> None: ... +@final +class Clock: + def tick(self, framerate: float = 0) -> int: ... + def tick_busy_loop(self, framerate: float = 0) -> int: ... + def get_time(self) -> int: ... + def get_rawtime(self) -> int: ... + def get_fps(self) -> float: ... diff --git a/.venv/Lib/site-packages/pygame/transform.cp311-win_amd64.pyd b/.venv/Lib/site-packages/pygame/transform.cp311-win_amd64.pyd new file mode 100644 index 00000000..2f9afd11 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/transform.cp311-win_amd64.pyd differ diff --git a/.venv/Lib/site-packages/pygame/transform.pyi b/.venv/Lib/site-packages/pygame/transform.pyi new file mode 100644 index 00000000..92fd10c1 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/transform.pyi @@ -0,0 +1,58 @@ +from typing import Literal, Optional, Sequence, Union + +from pygame.color import Color +from pygame.surface import Surface + +from ._common import ColorValue, Coordinate, RectValue + +def flip( + surface: Surface, + flip_x: bool | Literal[0] | Literal[1], + flip_y: bool | Literal[0] | Literal[1], +) -> Surface: ... +def scale( + surface: Surface, + size: Coordinate, + dest_surface: Optional[Surface] = None, +) -> Surface: ... +def scale_by( + surface: Surface, + factor: Union[float, Sequence[float]], + dest_surface: Optional[Surface] = None, +) -> Surface: ... +def rotate(surface: Surface, angle: float) -> Surface: ... +def rotozoom(surface: Surface, angle: float, scale: float) -> Surface: ... +def scale2x(surface: Surface, dest_surface: Optional[Surface] = None) -> Surface: ... +def grayscale(surface: Surface, dest_surface: Optional[Surface] = None) -> Surface: ... +def smoothscale( + surface: Surface, + size: Coordinate, + dest_surface: Optional[Surface] = None, +) -> Surface: ... +def smoothscale_by( + surface: Surface, + factor: Union[float, Sequence[float]], + dest_surface: Optional[Surface] = None, +) -> Surface: ... +def get_smoothscale_backend() -> str: ... +def set_smoothscale_backend(backend: str) -> None: ... +def chop(surface: Surface, rect: RectValue) -> Surface: ... +def laplacian(surface: Surface, dest_surface: Optional[Surface] = None) -> Surface: ... +def average_surfaces( + surfaces: Sequence[Surface], + dest_surface: Optional[Surface] = None, + palette_colors: Union[bool, int] = 1, +) -> Surface: ... +def average_color( + surface: Surface, rect: Optional[RectValue] = None, consider_alpha: bool = False +) -> Color: ... +def threshold( + dest_surface: Optional[Surface], + surface: Surface, + search_color: Optional[ColorValue], + threshold: ColorValue = (0, 0, 0, 0), + set_color: Optional[ColorValue] = (0, 0, 0, 0), + set_behavior: int = 1, + search_surf: Optional[Surface] = None, + inverse_set: bool = False, +) -> int: ... diff --git a/.venv/Lib/site-packages/pygame/version.py b/.venv/Lib/site-packages/pygame/version.py new file mode 100644 index 00000000..8233ad42 --- /dev/null +++ b/.venv/Lib/site-packages/pygame/version.py @@ -0,0 +1,72 @@ +## pygame - Python Game Library +## Copyright (C) 2000-2003 Pete Shinners +## +## This library is free software; you can redistribute it and/or +## modify it under the terms of the GNU Library General Public +## License as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## Library General Public License for more details. +## +## You should have received a copy of the GNU Library General Public +## License along with this library; if not, write to the Free +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## +## Pete Shinners +## pete@shinners.org + +"""Simply the current installed pygame version. The version information is +stored in the regular pygame module as 'pygame.ver'. Keeping the version +information also available in a separate module allows you to test the +pygame version without importing the main pygame module. + +The python version information should always compare greater than any previous +releases. (hmm, until we get to versions > 10) +""" +from pygame.base import get_sdl_version + +############### +# This file is generated with version.py.in +## + +class SoftwareVersion(tuple): + """ + A class for storing data about software versions. + """ + __slots__ = () + fields = "major", "minor", "patch" + + def __new__(cls, major, minor, patch): + return tuple.__new__(cls, (major, minor, patch)) + + def __repr__(self): + fields = (f"{fld}={val}" for fld, val in zip(self.fields, self)) + return f"{str(self.__class__.__name__)}({', '.join(fields)})" + + def __str__(self): + return f"{self.major}.{self.minor}.{self.patch}" + + major = property(lambda self: self[0]) + minor = property(lambda self: self[1]) + patch = property(lambda self: self[2]) + +class PygameVersion(SoftwareVersion): + """ + Pygame Version class. + """ + +class SDLVersion(SoftwareVersion): + """ + SDL Version class. + """ + +_sdl_tuple = get_sdl_version() +SDL = SDLVersion(_sdl_tuple[0], _sdl_tuple[1], _sdl_tuple[2]) +ver = "2.5.2" # pylint: disable=invalid-name +vernum = PygameVersion(2, 5, 2) +rev = "" # pylint: disable=invalid-name + +__all__ = ["SDL", "ver", "vernum", "rev"] diff --git a/.venv/Lib/site-packages/pygame/version.pyi b/.venv/Lib/site-packages/pygame/version.pyi new file mode 100644 index 00000000..69a3b6fe --- /dev/null +++ b/.venv/Lib/site-packages/pygame/version.pyi @@ -0,0 +1,23 @@ +from typing import Tuple + +from ._common import Literal + +class SoftwareVersion(Tuple[int, int, int]): + def __new__(cls, major: int, minor: int, patch: int) -> SoftwareVersion: ... + def __repr__(self) -> str: ... + def __str__(self) -> str: ... + @property + def major(self) -> int: ... + @property + def minor(self) -> int: ... + @property + def patch(self) -> int: ... + fields: Tuple[Literal["major"], Literal["minor"], Literal["patch"]] + +class PygameVersion(SoftwareVersion): ... +class SDLVersion(SoftwareVersion): ... + +SDL: SDLVersion +ver: str +vernum: PygameVersion +rev: str diff --git a/.venv/Lib/site-packages/pygame/zlib1.dll b/.venv/Lib/site-packages/pygame/zlib1.dll new file mode 100644 index 00000000..e7493de3 Binary files /dev/null and b/.venv/Lib/site-packages/pygame/zlib1.dll differ diff --git a/.venv/Lib/site-packages/sniffio/__pycache__/__init__.cpython-311.pyc b/.venv/Lib/site-packages/sniffio/__pycache__/__init__.cpython-311.pyc index 69652294..61711838 100644 Binary files a/.venv/Lib/site-packages/sniffio/__pycache__/__init__.cpython-311.pyc and b/.venv/Lib/site-packages/sniffio/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/sniffio/__pycache__/_impl.cpython-311.pyc b/.venv/Lib/site-packages/sniffio/__pycache__/_impl.cpython-311.pyc index 621e196d..afcb54b3 100644 Binary files a/.venv/Lib/site-packages/sniffio/__pycache__/_impl.cpython-311.pyc and b/.venv/Lib/site-packages/sniffio/__pycache__/_impl.cpython-311.pyc differ diff --git a/.venv/Lib/site-packages/sniffio/__pycache__/_version.cpython-311.pyc b/.venv/Lib/site-packages/sniffio/__pycache__/_version.cpython-311.pyc index 7513fafe..9ba42491 100644 Binary files a/.venv/Lib/site-packages/sniffio/__pycache__/_version.cpython-311.pyc and b/.venv/Lib/site-packages/sniffio/__pycache__/_version.cpython-311.pyc differ diff --git a/.venv/include/site/python3.11/pygame/_blit_info.h b/.venv/include/site/python3.11/pygame/_blit_info.h new file mode 100644 index 00000000..5320d0b1 --- /dev/null +++ b/.venv/include/site/python3.11/pygame/_blit_info.h @@ -0,0 +1,21 @@ +#define NO_PYGAME_C_API +#include "_surface.h" + +/* The structure passed to the low level blit functions */ +typedef struct { + int width; + int height; + Uint8 *s_pixels; + int s_pxskip; + int s_skip; + Uint8 *d_pixels; + int d_pxskip; + int d_skip; + SDL_PixelFormat *src; + SDL_PixelFormat *dst; + Uint8 src_blanket_alpha; + int src_has_colorkey; + Uint32 src_colorkey; + SDL_BlendMode src_blend; + SDL_BlendMode dst_blend; +} SDL_BlitInfo; diff --git a/.venv/include/site/python3.11/pygame/_camera.h b/.venv/include/site/python3.11/pygame/_camera.h new file mode 100644 index 00000000..075ef6fb --- /dev/null +++ b/.venv/include/site/python3.11/pygame/_camera.h @@ -0,0 +1,26 @@ +/* + pygame - Python Game Library + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef _CAMERA_H +#define _CAMERA_H + +#include "_pygame.h" +#include "camera.h" + +#endif diff --git a/.venv/include/site/python3.11/pygame/_pygame.h b/.venv/include/site/python3.11/pygame/_pygame.h new file mode 100644 index 00000000..e3521b33 --- /dev/null +++ b/.venv/include/site/python3.11/pygame/_pygame.h @@ -0,0 +1,374 @@ +/* + pygame - Python Game Library + Copyright (C) 2000-2001 Pete Shinners + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Pete Shinners + pete@shinners.org +*/ + +/* This will use PYGAMEAPI_EXTERN_SLOTS instead + * of PYGAMEAPI_DEFINE_SLOTS for base modules. + */ +#ifndef _PYGAME_INTERNAL_H +#define _PYGAME_INTERNAL_H + +#include "pgplatform.h" +/* + If PY_SSIZE_T_CLEAN is defined before including Python.h, length is a + Py_ssize_t rather than an int for all # variants of formats (s#, y#, etc.) +*/ +#define PY_SSIZE_T_CLEAN +#include + +/* Ensure PyPy-specific code is not in use when running on GraalPython (PR + * #2580) */ +#if defined(GRAALVM_PYTHON) && defined(PYPY_VERSION) +#undef PYPY_VERSION +#endif + +#include + +/* SDL 1.2 constants removed from SDL 2 */ +typedef enum { + SDL_HWSURFACE = 0, + SDL_RESIZABLE = SDL_WINDOW_RESIZABLE, + SDL_ASYNCBLIT = 0, + SDL_OPENGL = SDL_WINDOW_OPENGL, + SDL_OPENGLBLIT = 0, + SDL_ANYFORMAT = 0, + SDL_HWPALETTE = 0, + SDL_DOUBLEBUF = 0, + SDL_FULLSCREEN = SDL_WINDOW_FULLSCREEN, + SDL_HWACCEL = 0, + SDL_SRCCOLORKEY = 0, + SDL_RLEACCELOK = 0, + SDL_SRCALPHA = 0, + SDL_NOFRAME = SDL_WINDOW_BORDERLESS, + SDL_GL_SWAP_CONTROL = 0, + TIMER_RESOLUTION = 0 +} PygameVideoFlags; + +/* the wheel button constants were removed from SDL 2 */ +typedef enum { + PGM_BUTTON_LEFT = SDL_BUTTON_LEFT, + PGM_BUTTON_RIGHT = SDL_BUTTON_RIGHT, + PGM_BUTTON_MIDDLE = SDL_BUTTON_MIDDLE, + PGM_BUTTON_WHEELUP = 4, + PGM_BUTTON_WHEELDOWN = 5, + PGM_BUTTON_X1 = SDL_BUTTON_X1 + 2, + PGM_BUTTON_X2 = SDL_BUTTON_X2 + 2, + PGM_BUTTON_KEEP = 0x80 +} PygameMouseFlags; + +typedef enum { + /* Any SDL_* events here are for backward compatibility. */ + SDL_NOEVENT = 0, + + SDL_ACTIVEEVENT = SDL_USEREVENT, + SDL_VIDEORESIZE, + SDL_VIDEOEXPOSE, + + PGE_MIDIIN, + PGE_MIDIOUT, + PGE_KEYREPEAT, /* Special internal pygame event, for managing key-presses + */ + + /* DO NOT CHANGE THE ORDER OF EVENTS HERE */ + PGE_WINDOWSHOWN, + PGE_WINDOWHIDDEN, + PGE_WINDOWEXPOSED, + PGE_WINDOWMOVED, + PGE_WINDOWRESIZED, + PGE_WINDOWSIZECHANGED, + PGE_WINDOWMINIMIZED, + PGE_WINDOWMAXIMIZED, + PGE_WINDOWRESTORED, + PGE_WINDOWENTER, + PGE_WINDOWLEAVE, + PGE_WINDOWFOCUSGAINED, + PGE_WINDOWFOCUSLOST, + PGE_WINDOWCLOSE, + PGE_WINDOWTAKEFOCUS, + PGE_WINDOWHITTEST, + PGE_WINDOWICCPROFCHANGED, + PGE_WINDOWDISPLAYCHANGED, + + /* Here we define PGPOST_* events, events that act as a one-to-one + * proxy for SDL events (and some extra events too!), the proxy is used + * internally when pygame users use event.post() + * + * At a first glance, these may look redundant, but they are really + * important, especially with event blocking. If proxy events are + * not there, blocked events dont make it to our event filter, and + * that can break a lot of stuff. + * + * IMPORTANT NOTE: Do not post events directly with these proxy types, + * use the appropriate functions from event.c, that handle these proxy + * events for you. + * Proxy events are for internal use only */ + PGPOST_EVENTBEGIN, /* mark start of proxy-events */ + PGPOST_ACTIVEEVENT = PGPOST_EVENTBEGIN, + PGPOST_APP_TERMINATING, + PGPOST_APP_LOWMEMORY, + PGPOST_APP_WILLENTERBACKGROUND, + PGPOST_APP_DIDENTERBACKGROUND, + PGPOST_APP_WILLENTERFOREGROUND, + PGPOST_APP_DIDENTERFOREGROUND, + PGPOST_AUDIODEVICEADDED, + PGPOST_AUDIODEVICEREMOVED, + PGPOST_CLIPBOARDUPDATE, + PGPOST_CONTROLLERAXISMOTION, + PGPOST_CONTROLLERBUTTONDOWN, + PGPOST_CONTROLLERBUTTONUP, + PGPOST_CONTROLLERDEVICEADDED, + PGPOST_CONTROLLERDEVICEREMOVED, + PGPOST_CONTROLLERDEVICEREMAPPED, + PGPOST_CONTROLLERTOUCHPADDOWN, + PGPOST_CONTROLLERTOUCHPADMOTION, + PGPOST_CONTROLLERTOUCHPADUP, + PGPOST_CONTROLLERSENSORUPDATE, + PGPOST_DOLLARGESTURE, + PGPOST_DOLLARRECORD, + PGPOST_DROPFILE, + PGPOST_DROPTEXT, + PGPOST_DROPBEGIN, + PGPOST_DROPCOMPLETE, + PGPOST_FINGERMOTION, + PGPOST_FINGERDOWN, + PGPOST_FINGERUP, + PGPOST_KEYDOWN, + PGPOST_KEYMAPCHANGED, + PGPOST_KEYUP, + PGPOST_JOYAXISMOTION, + PGPOST_JOYBALLMOTION, + PGPOST_JOYHATMOTION, + PGPOST_JOYBUTTONDOWN, + PGPOST_JOYBUTTONUP, + PGPOST_JOYDEVICEADDED, + PGPOST_JOYDEVICEREMOVED, + PGPOST_LOCALECHANGED, + PGPOST_MIDIIN, + PGPOST_MIDIOUT, + PGPOST_MOUSEMOTION, + PGPOST_MOUSEBUTTONDOWN, + PGPOST_MOUSEBUTTONUP, + PGPOST_MOUSEWHEEL, + PGPOST_MULTIGESTURE, + PGPOST_NOEVENT, + PGPOST_QUIT, + PGPOST_RENDER_TARGETS_RESET, + PGPOST_RENDER_DEVICE_RESET, + PGPOST_SYSWMEVENT, + PGPOST_TEXTEDITING, + PGPOST_TEXTINPUT, + PGPOST_VIDEORESIZE, + PGPOST_VIDEOEXPOSE, + PGPOST_WINDOWSHOWN, + PGPOST_WINDOWHIDDEN, + PGPOST_WINDOWEXPOSED, + PGPOST_WINDOWMOVED, + PGPOST_WINDOWRESIZED, + PGPOST_WINDOWSIZECHANGED, + PGPOST_WINDOWMINIMIZED, + PGPOST_WINDOWMAXIMIZED, + PGPOST_WINDOWRESTORED, + PGPOST_WINDOWENTER, + PGPOST_WINDOWLEAVE, + PGPOST_WINDOWFOCUSGAINED, + PGPOST_WINDOWFOCUSLOST, + PGPOST_WINDOWCLOSE, + PGPOST_WINDOWTAKEFOCUS, + PGPOST_WINDOWHITTEST, + PGPOST_WINDOWICCPROFCHANGED, + PGPOST_WINDOWDISPLAYCHANGED, + + PGE_USEREVENT, /* this event must stay in this position only */ + + PG_NUMEVENTS = + SDL_LASTEVENT /* Not an event. Indicates end of user events. */ +} PygameEventCode; + +/* SDL1 ACTIVEEVENT state attribute can take the following values */ +/* These constant values are directly picked from SDL1 source */ +#define SDL_APPMOUSEFOCUS 0x01 +#define SDL_APPINPUTFOCUS 0x02 +#define SDL_APPACTIVE 0x04 + +/* Surface flags: based on SDL 1.2 flags */ +typedef enum { + PGS_SWSURFACE = 0x00000000, + PGS_HWSURFACE = 0x00000001, + PGS_ASYNCBLIT = 0x00000004, + + PGS_ANYFORMAT = 0x10000000, + PGS_HWPALETTE = 0x20000000, + PGS_DOUBLEBUF = 0x40000000, + PGS_FULLSCREEN = 0x80000000, + PGS_SCALED = 0x00000200, + + PGS_OPENGL = 0x00000002, + PGS_OPENGLBLIT = 0x0000000A, + PGS_RESIZABLE = 0x00000010, + PGS_NOFRAME = 0x00000020, + PGS_SHOWN = 0x00000040, /* Added from SDL 2 */ + PGS_HIDDEN = 0x00000080, /* Added from SDL 2 */ + + PGS_HWACCEL = 0x00000100, + PGS_SRCCOLORKEY = 0x00001000, + PGS_RLEACCELOK = 0x00002000, + PGS_RLEACCEL = 0x00004000, + PGS_SRCALPHA = 0x00010000, + PGS_PREALLOC = 0x01000000 +} PygameSurfaceFlags; + +// TODO Implement check below in a way that does not break CI +/* New buffer protocol (PEP 3118) implemented on all supported Py versions. +#if !defined(Py_TPFLAGS_HAVE_NEWBUFFER) +#error No support for PEP 3118/Py_TPFLAGS_HAVE_NEWBUFFER. Please use a +supported Python version. #endif */ + +#define RAISE(x, y) (PyErr_SetString((x), (y)), NULL) +#define DEL_ATTR_NOT_SUPPORTED_CHECK(name, value) \ + do { \ + if (!value) { \ + PyErr_Format(PyExc_AttributeError, "Cannot delete attribute %s", \ + name); \ + return -1; \ + } \ + } while (0) + +#define DEL_ATTR_NOT_SUPPORTED_CHECK_NO_NAME(value) \ + do { \ + if (!value) { \ + PyErr_SetString(PyExc_AttributeError, "Cannot delete attribute"); \ + return -1; \ + } \ + } while (0) + +/* + * Initialization checks + */ + +#define VIDEO_INIT_CHECK() \ + if (!SDL_WasInit(SDL_INIT_VIDEO)) \ + return RAISE(pgExc_SDLError, "video system not initialized") + +#define JOYSTICK_INIT_CHECK() \ + if (!SDL_WasInit(SDL_INIT_JOYSTICK)) \ + return RAISE(pgExc_SDLError, "joystick system not initialized") + +/* thread check */ +#ifdef WITH_THREAD +#define PG_CHECK_THREADS() (1) +#else /* ~WITH_THREAD */ +#define PG_CHECK_THREADS() \ + (RAISE(PyExc_NotImplementedError, "Python built without thread support")) +#endif /* ~WITH_THREAD */ + +#define PyType_Init(x) (((x).ob_type) = &PyType_Type) + +/* CPython 3.6 had initial and undocumented FASTCALL support, but we play it + * safe by not relying on implementation details */ +#if PY_VERSION_HEX < 0x03070000 + +/* Macro for naming a pygame fastcall wrapper function */ +#define PG_FASTCALL_NAME(func) _##func##_fastcall_wrap + +/* used to forward declare compat functions */ +#define PG_DECLARE_FASTCALL_FUNC(func, self_type) \ + static PyObject *PG_FASTCALL_NAME(func)(self_type * self, PyObject * args) + +/* Using this macro on a function defined with the FASTCALL calling convention + * adds a wrapper definition that uses regular python VARARGS convention. + * Since it is guaranteed that the 'args' object is a tuple, we can directly + * call PySequence_Fast_ITEMS and PyTuple_GET_SIZE on it (which are macros that + * assume the same, and don't do error checking) */ +#define PG_WRAP_FASTCALL_FUNC(func, self_type) \ + PG_DECLARE_FASTCALL_FUNC(func, self_type) \ + { \ + return func(self, (PyObject *const *)PySequence_Fast_ITEMS(args), \ + PyTuple_GET_SIZE(args)); \ + } + +#define PG_FASTCALL METH_VARARGS + +#else /* PY_VERSION_HEX >= 0x03070000 */ +/* compat macros are no-op on python versions that support fastcall */ +#define PG_FASTCALL_NAME(func) func +#define PG_DECLARE_FASTCALL_FUNC(func, self_type) +#define PG_WRAP_FASTCALL_FUNC(func, self_type) + +#define PG_FASTCALL METH_FASTCALL + +#endif /* PY_VERSION_HEX >= 0x03070000 */ + +/* + * event module internals + */ +struct pgEventObject { + PyObject_HEAD int type; + PyObject *dict; +}; + +/* + * surflock module internals + */ +typedef struct { + PyObject_HEAD PyObject *surface; + PyObject *lockobj; + PyObject *weakrefs; +} pgLifetimeLockObject; + +/* + * surface module internals + */ +struct pgSubSurface_Data { + PyObject *owner; + int pixeloffset; + int offsetx, offsety; +}; + +/* + * color module internals + */ +struct pgColorObject { + PyObject_HEAD Uint8 data[4]; + Uint8 len; +}; + +/* + * include public API + */ +#include "include/_pygame.h" + +/* Slot counts. + * Remember to keep these constants up to date. + */ + +#define PYGAMEAPI_RECT_NUMSLOTS 5 +#define PYGAMEAPI_JOYSTICK_NUMSLOTS 2 +#define PYGAMEAPI_DISPLAY_NUMSLOTS 2 +#define PYGAMEAPI_SURFACE_NUMSLOTS 4 +#define PYGAMEAPI_SURFLOCK_NUMSLOTS 8 +#define PYGAMEAPI_RWOBJECT_NUMSLOTS 6 +#define PYGAMEAPI_PIXELARRAY_NUMSLOTS 2 +#define PYGAMEAPI_COLOR_NUMSLOTS 5 +#define PYGAMEAPI_MATH_NUMSLOTS 2 +#define PYGAMEAPI_BASE_NUMSLOTS 27 +#define PYGAMEAPI_EVENT_NUMSLOTS 6 + +#endif /* _PYGAME_INTERNAL_H */ diff --git a/.venv/include/site/python3.11/pygame/_surface.h b/.venv/include/site/python3.11/pygame/_surface.h new file mode 100644 index 00000000..b2b4644a --- /dev/null +++ b/.venv/include/site/python3.11/pygame/_surface.h @@ -0,0 +1,30 @@ +/* + pygame - Python Game Library + Copyright (C) 2000-2001 Pete Shinners + Copyright (C) 2007 Marcus von Appen + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Pete Shinners + pete@shinners.org +*/ + +#ifndef _SURFACE_H +#define _SURFACE_H + +#include "_pygame.h" +#include "surface.h" + +#endif diff --git a/.venv/include/site/python3.11/pygame/camera.h b/.venv/include/site/python3.11/pygame/camera.h new file mode 100644 index 00000000..3079a9a3 --- /dev/null +++ b/.venv/include/site/python3.11/pygame/camera.h @@ -0,0 +1,252 @@ +#ifndef CAMERA_H +#define CAMERA_H +/* + pygame - Python Game Library + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "pygame.h" +#include "pgcompat.h" +#include "doc/camera_doc.h" + +#if defined(__unix__) +#include +#include +#include +#include +#include + +#include /* low-level i/o */ +#include +#include +#include +#include +#include +#include +#include + +/* on freebsd there is no asm/types */ +#ifdef linux +#include /* for videodev2.h */ +#endif + +#include +#endif + +#if defined(__WIN32__) +#define PYGAME_WINDOWS_CAMERA 1 + +#include +#include +#include +#include +#include +#include +#endif + +/* some constants used which are not defined on non-v4l machines. */ +#ifndef V4L2_PIX_FMT_RGB24 +#define V4L2_PIX_FMT_RGB24 'RGB3' +#endif +#ifndef V4L2_PIX_FMT_RGB444 +#define V4L2_PIX_FMT_RGB444 'R444' +#endif +#ifndef V4L2_PIX_FMT_YUYV +#define V4L2_PIX_FMT_YUYV 'YUYV' +#endif +#ifndef V4L2_PIX_FMT_XBGR32 +#define V4L2_PIX_FMT_XBGR32 'XR24' +#endif + +#define CLEAR(x) memset(&(x), 0, sizeof(x)) +#define SAT(c) \ + if (c & (~255)) { \ + if (c < 0) \ + c = 0; \ + else \ + c = 255; \ + } +#define SAT2(c) ((c) & (~255) ? ((c) < 0 ? 0 : 255) : (c)) +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 +#define RGB_OUT 1 +#define YUV_OUT 2 +#define HSV_OUT 4 +#define CAM_V4L \ + 1 /* deprecated. the incomplete support in pygame was removed */ +#define CAM_V4L2 2 + +struct buffer { + void *start; + size_t length; +}; + +#if defined(__unix__) +typedef struct pgCameraObject { + PyObject_HEAD char *device_name; + int camera_type; + unsigned long pixelformat; + unsigned int color_out; + struct buffer *buffers; + unsigned int n_buffers; + int width; + int height; + int size; + int hflip; + int vflip; + int brightness; + int fd; +} pgCameraObject; +#elif defined(PYGAME_WINDOWS_CAMERA) +typedef struct pgCameraObject { + PyObject_HEAD WCHAR *device_name; + IMFSourceReader *reader; + IMFTransform *transform; + IMFVideoProcessorControl *control; + IMFMediaBuffer *buf; + IMFMediaBuffer *raw_buf; + int buffer_ready; + short open; /* used to signal the update_function to exit */ + HANDLE t_handle; + HRESULT t_error; + int t_error_line; + int width; + int height; + int hflip; + int vflip; + int last_vflip; + int color_out; + unsigned long pixelformat; +} pgCameraObject; + +#else +/* generic definition. + */ + +typedef struct pgCameraObject { + PyObject_HEAD char *device_name; + int camera_type; + unsigned long pixelformat; + unsigned int color_out; + struct buffer *buffers; + unsigned int n_buffers; + int width; + int height; + int size; + int hflip; + int vflip; + int brightness; + int fd; +} pgCameraObject; +#endif + +/* internal functions for colorspace conversion */ +void +colorspace(SDL_Surface *src, SDL_Surface *dst, int cspace); +void +rgb24_to_rgb(const void *src, void *dst, int length, SDL_PixelFormat *format); +void +bgr32_to_rgb(const void *src, void *dst, int length, SDL_PixelFormat *format); +void +rgb444_to_rgb(const void *src, void *dst, int length, SDL_PixelFormat *format); +void +rgb_to_yuv(const void *src, void *dst, int length, unsigned long source, + SDL_PixelFormat *format); +void +rgb_to_hsv(const void *src, void *dst, int length, unsigned long source, + SDL_PixelFormat *format); +void +yuyv_to_rgb(const void *src, void *dst, int length, SDL_PixelFormat *format); +void +yuyv_to_yuv(const void *src, void *dst, int length, SDL_PixelFormat *format); +void +uyvy_to_rgb(const void *src, void *dst, int length, SDL_PixelFormat *format); +void +uyvy_to_yuv(const void *src, void *dst, int length, SDL_PixelFormat *format); +void +sbggr8_to_rgb(const void *src, void *dst, int width, int height, + SDL_PixelFormat *format); +void +yuv420_to_rgb(const void *src, void *dst, int width, int height, + SDL_PixelFormat *format); +void +yuv420_to_yuv(const void *src, void *dst, int width, int height, + SDL_PixelFormat *format); + +#if defined(__unix__) +/* internal functions specific to v4l2 */ +char ** +v4l2_list_cameras(int *num_devices); +int +v4l2_get_control(int fd, int id, int *value); +int +v4l2_set_control(int fd, int id, int value); +PyObject * +v4l2_read_raw(pgCameraObject *self); +int +v4l2_xioctl(int fd, int request, void *arg); +int +v4l2_process_image(pgCameraObject *self, const void *image, int buffer_size, + SDL_Surface *surf); +int +v4l2_query_buffer(pgCameraObject *self); +int +v4l2_read_frame(pgCameraObject *self, SDL_Surface *surf, int *errno_code); +int +v4l2_stop_capturing(pgCameraObject *self); +int +v4l2_start_capturing(pgCameraObject *self); +int +v4l2_uninit_device(pgCameraObject *self); +int +v4l2_init_mmap(pgCameraObject *self); +int +v4l2_init_device(pgCameraObject *self); +int +v4l2_close_device(pgCameraObject *self); +int +v4l2_open_device(pgCameraObject *self); + +#elif defined(PYGAME_WINDOWS_CAMERA) +/* internal functions specific to WINDOWS */ +WCHAR ** +windows_list_cameras(int *num_devices); +int +windows_init_device(pgCameraObject *self); +int +windows_open_device(pgCameraObject *self); +IMFActivate * +windows_device_from_name(WCHAR *device_name); +int +windows_close_device(pgCameraObject *self); +int +windows_read_frame(pgCameraObject *self, SDL_Surface *surf); +int +windows_frame_ready(pgCameraObject *self, int *result); +PyObject * +windows_read_raw(pgCameraObject *self); +int +windows_process_image(pgCameraObject *self, BYTE *data, DWORD buffer_size, + SDL_Surface *surf); +void +windows_dealloc_device(pgCameraObject *self); +int +windows_init_device(pgCameraObject *self); + +#endif + +#endif /* !CAMERA_H */ diff --git a/.venv/include/site/python3.11/pygame/font.h b/.venv/include/site/python3.11/pygame/font.h new file mode 100644 index 00000000..f5eedb25 --- /dev/null +++ b/.venv/include/site/python3.11/pygame/font.h @@ -0,0 +1,15 @@ +#ifndef PGFONT_INTERNAL_H +#define PGFONT_INTERNAL_H + +#include + +/* test font initialization */ +#define FONT_INIT_CHECK() \ + if (!(*(int *)PyFONT_C_API[2])) \ + return RAISE(pgExc_SDLError, "font system not initialized") + +#include "include/pygame_font.h" + +#define PYGAMEAPI_FONT_NUMSLOTS 3 + +#endif /* ~PGFONT_INTERNAL_H */ diff --git a/.venv/include/site/python3.11/pygame/freetype.h b/.venv/include/site/python3.11/pygame/freetype.h new file mode 100644 index 00000000..fd86bc21 --- /dev/null +++ b/.venv/include/site/python3.11/pygame/freetype.h @@ -0,0 +1,114 @@ +/* + pygame - Python Game Library + Copyright (C) 2009 Vicent Marti + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#ifndef _PYGAME_FREETYPE_INTERNAL_H_ +#define _PYGAME_FREETYPE_INTERNAL_H_ + +#include "pgcompat.h" +#include "pgplatform.h" + +#include +#include FT_FREETYPE_H +#include FT_CACHE_H +#include FT_XFREE86_H +#include FT_TRIGONOMETRY_H + +/********************************************************** + * Global module constants + **********************************************************/ + +/* Render styles */ +#define FT_STYLE_NORMAL 0x00 +#define FT_STYLE_STRONG 0x01 +#define FT_STYLE_OBLIQUE 0x02 +#define FT_STYLE_UNDERLINE 0x04 +#define FT_STYLE_WIDE 0x08 +#define FT_STYLE_DEFAULT 0xFF + +/* Bounding box modes */ +#define FT_BBOX_EXACT FT_GLYPH_BBOX_SUBPIXELS +#define FT_BBOX_EXACT_GRIDFIT FT_GLYPH_BBOX_GRIDFIT +#define FT_BBOX_PIXEL FT_GLYPH_BBOX_TRUNCATE +#define FT_BBOX_PIXEL_GRIDFIT FT_GLYPH_BBOX_PIXELS + +/* Rendering flags */ +#define FT_RFLAG_NONE (0) +#define FT_RFLAG_ANTIALIAS (1 << 0) +#define FT_RFLAG_AUTOHINT (1 << 1) +#define FT_RFLAG_VERTICAL (1 << 2) +#define FT_RFLAG_HINTED (1 << 3) +#define FT_RFLAG_KERNING (1 << 4) +#define FT_RFLAG_TRANSFORM (1 << 5) +#define FT_RFLAG_PAD (1 << 6) +#define FT_RFLAG_ORIGIN (1 << 7) +#define FT_RFLAG_UCS4 (1 << 8) +#define FT_RFLAG_USE_BITMAP_STRIKES (1 << 9) +#define FT_RFLAG_DEFAULTS \ + (FT_RFLAG_HINTED | FT_RFLAG_USE_BITMAP_STRIKES | FT_RFLAG_ANTIALIAS) + +#define FT_RENDER_NEWBYTEARRAY 0x0 +#define FT_RENDER_NEWSURFACE 0x1 +#define FT_RENDER_EXISTINGSURFACE 0x2 + +/********************************************************** + * Global module types + **********************************************************/ + +typedef struct _scale_s { + FT_UInt x, y; +} Scale_t; +typedef FT_Angle Angle_t; + +struct fontinternals_; +struct freetypeinstance_; + +typedef struct { + FT_Long font_index; + FT_Open_Args open_args; +} pgFontId; + +typedef struct { + PyObject_HEAD pgFontId id; + PyObject *path; + int is_scalable; + int is_bg_col_set; + + Scale_t face_size; + FT_Int16 style; + FT_Int16 render_flags; + double strength; + double underline_adjustment; + FT_UInt resolution; + Angle_t rotation; + FT_Matrix transform; + FT_Byte fgcolor[4]; + FT_Byte bgcolor[4]; + + struct freetypeinstance_ *freetype; /* Personal reference */ + struct fontinternals_ *_internals; +} pgFontObject; + +#define pgFont_IS_ALIVE(o) (((pgFontObject *)(o))->_internals != 0) + +/* import public API */ +#include "include/pygame_freetype.h" + +#define PYGAMEAPI_FREETYPE_NUMSLOTS 2 + +#endif /* ~_PYGAME_FREETYPE_INTERNAL_H_ */ diff --git a/.venv/include/site/python3.11/pygame/include/_pygame.h b/.venv/include/site/python3.11/pygame/include/_pygame.h new file mode 100644 index 00000000..c39ed46a --- /dev/null +++ b/.venv/include/site/python3.11/pygame/include/_pygame.h @@ -0,0 +1,949 @@ +/* + pygame - Python Game Library + Copyright (C) 2000-2001 Pete Shinners + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Pete Shinners + pete@shinners.org +*/ + +#ifndef _PYGAME_H +#define _PYGAME_H + +/** This header file includes all the definitions for the + ** base pygame extensions. This header only requires + ** Python includes (and SDL.h for functions that use SDL types). + ** The reason for functions prototyped with #define's is + ** to allow for maximum Python portability. It also uses + ** Python as the runtime linker, which allows for late binding. + '' For more information on this style of development, read + ** the Python docs on this subject. + ** http://www.python.org/doc/current/ext/using-cobjects.html + ** + ** If using this to build your own derived extensions, + ** you'll see that the functions available here are mainly + ** used to help convert between python objects and SDL objects. + ** Since this library doesn't add a lot of functionality to + ** the SDL library, it doesn't need to offer a lot either. + ** + ** When initializing your extension module, you must manually + ** import the modules you want to use. (this is the part about + ** using python as the runtime linker). Each module has its + ** own import_xxx() routine. You need to perform this import + ** after you have initialized your own module, and before + ** you call any routines from that module. Since every module + ** in pygame does this, there are plenty of examples. + ** + ** The base module does include some useful conversion routines + ** that you are free to use in your own extension. + **/ + +#include "pgplatform.h" +#include + +/* version macros (defined since version 1.9.5) */ +#define PG_MAJOR_VERSION 2 +#define PG_MINOR_VERSION 5 +#define PG_PATCH_VERSION 2 +#define PG_VERSIONNUM(MAJOR, MINOR, PATCH) \ + (1000 * (MAJOR) + 100 * (MINOR) + (PATCH)) +#define PG_VERSION_ATLEAST(MAJOR, MINOR, PATCH) \ + (PG_VERSIONNUM(PG_MAJOR_VERSION, PG_MINOR_VERSION, PG_PATCH_VERSION) >= \ + PG_VERSIONNUM(MAJOR, MINOR, PATCH)) + +#include "pgcompat.h" + +/* Flag indicating a pg_buffer; used for assertions within callbacks */ +#ifndef NDEBUG +#define PyBUF_PYGAME 0x4000 +#endif +#define PyBUF_HAS_FLAG(f, F) (((f) & (F)) == (F)) + +/* Array information exchange struct C type; inherits from Py_buffer + * + * Pygame uses its own Py_buffer derived C struct as an internal representation + * of an imported array buffer. The extended Py_buffer allows for a + * per-instance release callback, + */ +typedef void (*pybuffer_releaseproc)(Py_buffer *); + +typedef struct pg_bufferinfo_s { + Py_buffer view; + PyObject *consumer; /* Input: Borrowed reference */ + pybuffer_releaseproc release_buffer; +} pg_buffer; + +#include "pgimport.h" + +/* + * BASE module + */ +#ifndef PYGAMEAPI_BASE_INTERNAL +#define pgExc_SDLError ((PyObject *)PYGAMEAPI_GET_SLOT(base, 0)) + +#define pg_RegisterQuit \ + (*(void (*)(void (*)(void)))PYGAMEAPI_GET_SLOT(base, 1)) + +/** + * \brief Convert number like object *obj* to C int and in *val*. + * + * \param obj The Python object to convert. + * \param val A pointer to the C integer to store the result. + * \returns 1 if the conversion was successful, 0 otherwise. + * + * \note This function will clear any Python errors. + * \note This function will convert floats to integers. + */ +#define pg_IntFromObj \ + (*(int (*)(PyObject *, int *))PYGAMEAPI_GET_SLOT(base, 2)) + +/** + * \brief Convert number like object at position *i* in sequence *obj* + * to C int and place in argument *val*. + * + * \param obj The Python object to convert. + * \param i The index of the object to convert. + * \param val A pointer to the C integer to store the result. + * \returns 1 if the conversion was successful, 0 otherwise. + * + * \note This function will clear any Python errors. + * \note This function will convert floats to integers. + */ +#define pg_IntFromObjIndex \ + (*(int (*)(PyObject *, int, int *))PYGAMEAPI_GET_SLOT(base, 3)) + +/** + * \brief Convert the two number like objects in length 2 sequence *obj* to C + * int and place in arguments *val1* and *val2*. + * + * \param obj The Python two element sequence object to convert. + * \param val A pointer to the C integer to store the result. + * \param val2 A pointer to the C integer to store the result. + * \returns 1 if the conversion was successful, 0 otherwise. + * + * \note This function will clear any Python errors. + * \note This function will convert floats to integers. + */ +#define pg_TwoIntsFromObj \ + (*(int (*)(PyObject *, int *, int *))PYGAMEAPI_GET_SLOT(base, 4)) + +/** + * \brief Convert number like object *obj* to C float and in *val*. + * + * \param obj The Python object to convert. + * \param val A pointer to the C float to store the result. + * \returns 1 if the conversion was successful, 0 otherwise. + * + * \note This function will clear any Python errors. + */ +#define pg_FloatFromObj \ + (*(int (*)(PyObject *, float *))PYGAMEAPI_GET_SLOT(base, 5)) + +/** + * \brief Convert number like object at position *i* in sequence *obj* to C + * float and place in argument *val*. + * + * \param obj The Python object to convert. + * \param i The index of the object to convert. + * \param val A pointer to the C float to store the result. + * \returns 1 if the conversion was successful, 0 otherwise. + * + * \note This function will clear any Python errors. + */ +#define pg_FloatFromObjIndex \ + (*(int (*)(PyObject *, int, float *))PYGAMEAPI_GET_SLOT(base, 6)) + +/** + * \brief Convert the two number like objects in length 2 sequence *obj* to C + * float and place in arguments *val1* and *val2*. + * + * \param obj The Python two element sequence object to convert. + * \param val A pointer to the C float to store the result. + * \param val2 A pointer to the C float to store the result. + * \returns 1 if the conversion was successful, 0 otherwise. + * + * \note This function will clear any Python errors. + */ +#define pg_TwoFloatsFromObj \ + (*(int (*)(PyObject *, float *, float *))PYGAMEAPI_GET_SLOT(base, 7)) + +/** + * \brief Convert number like object *obj* to C Uint32 and in *val*. + * + * \param obj The Python object to convert. + * \param val A pointer to the C int to store the result. + * \returns 1 if the conversion was successful, 0 otherwise. + */ +#define pg_UintFromObj \ + (*(int (*)(PyObject *, Uint32 *))PYGAMEAPI_GET_SLOT(base, 8)) + +/** + * \brief Convert number like object at position *i* in sequence *obj* to C + * Uint32 and place in argument *val*. + * + * \param obj The Python object to convert. + * \param i The index of the object to convert. + * \param val A pointer to the C int to store the result. + * \returns 1 if the conversion was successful, 0 otherwise. + */ +#define pg_UintFromObjIndex \ + (*(int (*)(PyObject *, int, Uint32 *))PYGAMEAPI_GET_SLOT(base, 9)) + +/** + * \brief Initialize all of the pygame modules. + * \returns 1 on success, 0 on failure with PyErr set. + */ +#define pg_mod_autoinit (*(int (*)(const char *))PYGAMEAPI_GET_SLOT(base, 10)) + +/** + * \brief Quit all of the pygame modules. + */ +#define pg_mod_autoquit (*(void (*)(const char *))PYGAMEAPI_GET_SLOT(base, 11)) + +/** + * \brief Convert the color represented by object *obj* into a red, green, + * blue, alpha length 4 C array *RGBA*. + * + * The object must be a length 3 or 4 sequence of numbers having values between + * 0 and 255 inclusive. For a length 3 sequence an alpha value of 255 is + * assumed. + * + * \param obj The Python object to convert. + * \param RGBA A pointer to the C array to store the result. + * \returns 1 if the conversion was successful, 0 otherwise. + */ +#define pg_RGBAFromObj \ + (*(int (*)(PyObject *, Uint8 *))PYGAMEAPI_GET_SLOT(base, 12)) + +/** + * \brief Given a Py_buffer, return a python dictionary representing the array + * interface. + * + * \param view_p A pointer to the Py_buffer to convert to a dictionary. + * + * \returns A Python dictionary representing the array interface of the object. + */ +#define pgBuffer_AsArrayInterface \ + (*(PyObject * (*)(Py_buffer *)) PYGAMEAPI_GET_SLOT(base, 13)) + +/** + * \brief Given a Py_buffer, return a python capsule representing the array + * interface. + * + * \param view_p A pointer to the Py_buffer to convert to a capsule. + * + * \returns A Python capsule representing the array interface of the object. + */ +#define pgBuffer_AsArrayStruct \ + (*(PyObject * (*)(Py_buffer *)) PYGAMEAPI_GET_SLOT(base, 14)) + +/** + * \brief Get a buffer object from a given Python object. + * + * \param obj The Python object to get the buffer from. + * \param pg_view_p A pointer to a pg_buffer struct to store the buffer in. + * \param flags The desired buffer access mode. + * + * \returns 0 on success, -1 on failure. + * + * \note This function attempts to get a buffer object from a given Python + * object. If the object supports the buffer protocol, it will be used to + * create the buffer. If not, it will try to get an array interface or + * dictionary representation of the object and use that to create the buffer. + * If none of these methods work, it will raise a ValueError. + * + */ +#define pgObject_GetBuffer \ + (*(int (*)(PyObject *, pg_buffer *, int))PYGAMEAPI_GET_SLOT(base, 15)) + +/** + * \brief Release a pg_buffer object. + * + * \param pg_view_p The pg_buffer object to release. + * + * \note This function releases a pg_buffer object. + * \note some calls to this function expect this function to not clear + * previously set errors. + */ +#define pgBuffer_Release (*(void (*)(pg_buffer *))PYGAMEAPI_GET_SLOT(base, 16)) + +/** + * \brief Write the array interface dictionary buffer description *dict* into a + * Pygame buffer description struct *pg_view_p*. + * + * \param pg_view_p The Pygame buffer description struct to write into. + * \param dict The array interface dictionary to read from. + * \param flags The PyBUF flags describing the view type requested. + * + * \returns 0 on success, or -1 on failure. + */ +#define pgDict_AsBuffer \ + (*(int (*)(pg_buffer *, PyObject *, int))PYGAMEAPI_GET_SLOT(base, 17)) + +#define pgExc_BufferError ((PyObject *)PYGAMEAPI_GET_SLOT(base, 18)) + +/** + * \brief Get the default SDL window created by a pygame.display.set_mode() + * call, or *NULL*. + * + * \return The default window, or *NULL* if no window has been created. + */ +#define pg_GetDefaultWindow \ + (*(SDL_Window * (*)(void)) PYGAMEAPI_GET_SLOT(base, 19)) + +/** + * \brief Set the default SDL window created by a pygame.display.set_mode() + * call. The previous window, if any, is destroyed. Argument *win* may be + * *NULL*. This function is called by pygame.display.set_mode(). + * + * \param win The new default window. May be NULL. + */ +#define pg_SetDefaultWindow \ + (*(void (*)(SDL_Window *))PYGAMEAPI_GET_SLOT(base, 20)) + +/** + * \brief Return a borrowed reference to the Pygame default window display + * surface, or *NULL* if no default window is open. + * + * \return The default renderer, or *NULL* if no renderer has been created. + */ +#define pg_GetDefaultWindowSurface \ + (*(pgSurfaceObject * (*)(void)) PYGAMEAPI_GET_SLOT(base, 21)) + +/** + * \brief Set the Pygame default window display surface. The previous + * surface, if any, is destroyed. Argument *screen* may be *NULL*. This + * function is called by pygame.display.set_mode(). + * + * \param screen The new default window display surface. May be NULL. + */ +#define pg_SetDefaultWindowSurface \ + (*(void (*)(pgSurfaceObject *))PYGAMEAPI_GET_SLOT(base, 22)) + +/** + * \returns NULL if the environment variable PYGAME_BLEND_ALPHA_SDL2 is not + * set, otherwise returns a pointer to the environment variable. + */ +#define pg_EnvShouldBlendAlphaSDL2 \ + (*(char *(*)(void))PYGAMEAPI_GET_SLOT(base, 23)) + +/** + * \brief Convert number like object *obj* to C double and in *val*. + * + * \param obj The Python object to convert. + * \param val A pointer to the C double to store the result. + * \returns 1 if the conversion was successful, 0 otherwise. + * + * \note This function will clear any Python errors. + */ +#define pg_DoubleFromObj \ + (*(int (*)(PyObject *, double *))PYGAMEAPI_GET_SLOT(base, 24)) + +/** + * \brief Convert number like object at position *i* in sequence *obj* to C + * double and place in argument *val*. + * + * \param obj The Python object to convert. + * \param i The index of the object to convert. + * \param val A pointer to the C double to store the result. + * \returns 1 if the conversion was successful, 0 otherwise. + * + * \note This function will clear any Python errors. + */ +#define pg_DoubleFromObjIndex \ + (*(int (*)(PyObject *, int, double *))PYGAMEAPI_GET_SLOT(base, 25)) + +/** + * \brief Convert the two number like objects in length 2 sequence *obj* to C + * double and place in arguments *val1* and *val2*. + * + * \param obj The Python two element sequence object to convert. + * \param val A pointer to the C double to store the result. + * \param val2 A pointer to the C double to store the result. + * \returns 1 if the conversion was successful, 0 otherwise. + */ +#define pg_TwoDoublesFromObj \ + (*(int (*)(PyObject *, double *, double *))PYGAMEAPI_GET_SLOT(base, 26)) + +#define import_pygame_base() IMPORT_PYGAME_MODULE(base) +#endif /* ~PYGAMEAPI_BASE_INTERNAL */ + +typedef struct { + /** + * \brief The SDL rect wrapped by this object. + */ + PyObject_HEAD SDL_Rect r; + /** + * \brief A list of weak references to this rect. + */ + PyObject *weakreflist; +} pgRectObject; + +/** + * \brief Convert a pgRectObject to an SDL_Rect. + * + * \param obj A pgRectObject instance. + * \returns the SDL_Rect field of *obj*, a pgRect_Type instance. + * + * \note SDL_Rect pgRect_AsRect(PyObject *obj) + */ +#define pgRect_AsRect(x) (((pgRectObject *)x)->r) + +#ifndef PYGAMEAPI_RECT_INTERNAL + +/** + * \brief The Pygame rectangle object type pygame.Rect. + */ +#define pgRect_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(rect, 0)) + +/** + * \brief Check if *obj* is a `pygame.Rect` instance. + * + * \returns true if *obj* is a `pygame.Rect` instance + */ +#define pgRect_Check(obj) ((obj)->ob_type == &pgRect_Type) + +/** + * \brief Create a new `pygame.Rect` instance. + * + * \param r A pointer to an SDL_Rect struct. + * \returns a new `pygame.Rect` object for the SDL_Rect *r*. + * Returns *NULL* on error. + * + * \note PyObject* pgRect_New(SDL_Rect *r) + */ +#define pgRect_New (*(PyObject * (*)(SDL_Rect *)) PYGAMEAPI_GET_SLOT(rect, 1)) + +/** + * \brief Create a new `pygame.Rect` instance from x, y, w, h. + * + * \param x The x coordinate of the rectangle. + * \param y The y coordinate of the rectangle. + * \param w The width of the rectangle. + * \param h The height of the rectangle. + * \returns a new `pygame.Rect` object. Returns *NULL* on error. + * + * \note PyObject* pgRect_New4(int x, int y, int w, int h) + */ +#define pgRect_New4 \ + (*(PyObject * (*)(int, int, int, int)) PYGAMEAPI_GET_SLOT(rect, 2)) + +/** + * \brief Convert a Python object to a `pygame.Rect` instance. + * + * \param obj A Python object. + * A rectangle can be a length 4 sequence integers (x, y, w, h), or a length 2 + * sequence of position (x, y) and size (w, h), or a length 1 tuple containing + * a rectangle representation, or have a method *rect* that returns a + * rectangle. + * + * \param temp A pointer to an SDL_Rect struct to store the result in. + * \returns a pointer to the SDL_Rect field of the `pygame.Rect` instance + * *obj*. Returns *NULL* on error. + * + * \note This function will clear any Python errors. + * \note SDL_Rect* pgRect_FromObject(PyObject *obj, SDL_Rect *temp) + */ +#define pgRect_FromObject \ + (*(SDL_Rect * (*)(PyObject *, SDL_Rect *)) PYGAMEAPI_GET_SLOT(rect, 3)) + +/** + * \brief Normalize a `pygame.Rect` instance. A rect with a negative size + * (negative width and/or height) will be adjusted to have a positive size. + * + * \param rect A pointer to a `pygame.Rect` instance. + * \returns *rect* normalized with positive values only. + * + * \note void pgRect_Normalize(SDL_Rect *rect) + */ +#define pgRect_Normalize (*(void (*)(SDL_Rect *))PYGAMEAPI_GET_SLOT(rect, 4)) + +#define import_pygame_rect() IMPORT_PYGAME_MODULE(rect) +#endif /* ~PYGAMEAPI_RECT_INTERNAL */ + +/* + * JOYSTICK module + */ +typedef struct pgJoystickObject { + PyObject_HEAD int id; + SDL_Joystick *joy; + + /* Joysticks form an intrusive linked list. + * + * Note that we don't maintain refcounts for these so they are weakrefs + * from the Python side. + */ + struct pgJoystickObject *next; + struct pgJoystickObject *prev; +} pgJoystickObject; + +#define pgJoystick_AsID(x) (((pgJoystickObject *)x)->id) +#define pgJoystick_AsSDL(x) (((pgJoystickObject *)x)->joy) + +#ifndef PYGAMEAPI_JOYSTICK_INTERNAL +#define pgJoystick_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(joystick, 0)) + +#define pgJoystick_Check(x) ((x)->ob_type == &pgJoystick_Type) +#define pgJoystick_New (*(PyObject * (*)(int)) PYGAMEAPI_GET_SLOT(joystick, 1)) + +#define import_pygame_joystick() IMPORT_PYGAME_MODULE(joystick) +#endif + +/* + * DISPLAY module + */ + +typedef struct { + Uint32 hw_available : 1; + Uint32 wm_available : 1; + Uint32 blit_hw : 1; + Uint32 blit_hw_CC : 1; + Uint32 blit_hw_A : 1; + Uint32 blit_sw : 1; + Uint32 blit_sw_CC : 1; + Uint32 blit_sw_A : 1; + Uint32 blit_fill : 1; + Uint32 video_mem; + SDL_PixelFormat *vfmt; + SDL_PixelFormat vfmt_data; + int current_w; + int current_h; +} pg_VideoInfo; + +/** + * A pygame object that wraps an SDL_VideoInfo struct. + * The object returned by `pygame.display.Info()` + */ +typedef struct { + PyObject_HEAD pg_VideoInfo info; +} pgVidInfoObject; + +/** + * \brief Convert a pgVidInfoObject to an SDL_VideoInfo. + * + * \note SDL_VideoInfo pgVidInfo_AsVidInfo(PyObject *obj) + * + * \returns the SDL_VideoInfo field of *obj*, a pgVidInfo_Type instance. + * \param obj A pgVidInfo_Type instance. + * + * \note Does not check that *obj* is not `NULL` or an `pgVidInfoObject` + * object. + */ +#define pgVidInfo_AsVidInfo(x) (((pgVidInfoObject *)x)->info) + +#ifndef PYGAMEAPI_DISPLAY_INTERNAL +/** + * \brief The pgVidInfoObject object Python type. + * \note pgVideoInfo_Type is used for the `pygame.display.Info()` object. + */ +#define pgVidInfo_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(display, 0)) + +/** + * \brief Check if *obj* is a pgVidInfoObject. + * + * \returns true if *x* is a `pgVidInfo_Type` instance + * \note Will return false if *x* is a subclass of `pgVidInfo_Type`. + * \note This macro does not check that *x* is not ``NULL``. + * \note int pgVidInfo_Check(PyObject *x) + */ +#define pgVidInfo_Check(x) ((x)->ob_type == &pgVidInfo_Type) + +/** + * \brief Create a new pgVidInfoObject. + * + * \param i A pointer to an SDL_VideoInfo struct. + * \returns a new `pgVidInfoObject` object for the SDL_VideoInfo *i*. + * + * \note PyObject* pgVidInfo_New(SDL_VideoInfo *i) + * \note On failure, raise a Python exception and return `NULL`. + */ +#define pgVidInfo_New \ + (*(PyObject * (*)(pg_VideoInfo *)) PYGAMEAPI_GET_SLOT(display, 1)) + +#define import_pygame_display() IMPORT_PYGAME_MODULE(display) +#endif /* ~PYGAMEAPI_DISPLAY_INTERNAL */ + +/* + * SURFACE module + */ +struct pgSubSurface_Data; +struct SDL_Surface; + +/** + * \brief A pygame object that wraps an SDL_Surface. A `pygame.Surface` + * instance. + */ +typedef struct { + PyObject_HEAD struct SDL_Surface *surf; + /** + * \brief If true, the surface will be freed when the python object is + * destroyed. + */ + int owner; + /** + * \brief The subsurface data for this surface (if a subsurface). + */ + struct pgSubSurface_Data *subsurface; + /** + * \brief A list of weak references to this surface. + */ + PyObject *weakreflist; + /** + * \brief A list of locks for this surface. + */ + PyObject *locklist; + /** + * \brief Usually a buffer object which the surface gets its data from. + */ + PyObject *dependency; +} pgSurfaceObject; + +/** + * \brief Convert a `pygame.Surface` instance to an SDL_Surface. + * + * \param x A `pygame.Surface` instance. + * \returns the SDL_Surface field of *x*, a `pygame.Surface` instance. + * + * \note SDL_Surface* pgSurface_AsSurface(PyObject *x) + */ +#define pgSurface_AsSurface(x) (((pgSurfaceObject *)x)->surf) + +#ifndef PYGAMEAPI_SURFACE_INTERNAL +/** + * \brief The `pygame.Surface` object Python type. + */ +#define pgSurface_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(surface, 0)) + +/** + * \brief Check if *x* is a `pygame.Surface` instance. + * + * \param x The object to check. + * \returns true if *x* is a `pygame.Surface` instance + * + * \note Will return false if *x* is a subclass of `pygame.Surface`. + * \note This macro does not check that *x* is not ``NULL``. + * \note int pgSurface_Check(PyObject *x) + */ +#define pgSurface_Check(x) \ + (PyObject_IsInstance((x), (PyObject *)&pgSurface_Type)) + +/** + * \brief Create a new `pygame.Surface` instance. + * + * \param s The SDL surface to wrap in a python object. + * \param owner If true, the surface will be freed when the python object is + * destroyed. \returns A new new pygame surface instance for SDL surface *s*. + * Returns *NULL* on error. + * + * \note pgSurfaceObject* pgSurface_New2(SDL_Surface *s, int owner) + */ +#define pgSurface_New2 \ + (*(pgSurfaceObject * (*)(SDL_Surface *, int)) \ + PYGAMEAPI_GET_SLOT(surface, 1)) + +/** + * \brief Sets the SDL surface for a `pygame.Surface` instance. + * + * \param self The `pygame.Surface` instance to set the surface for. + * \param s The SDL surface to set. + * \param owner If true, the surface will be freed when the python object is + * destroyed. \returns 0 on success, -1 on failure. + * + * \note int pgSurface_SetSurface(pgSurfaceObject *self, SDL_Surface *s, int + * owner) + */ +#define pgSurface_SetSurface \ + (*(int (*)(pgSurfaceObject *, SDL_Surface *, int))PYGAMEAPI_GET_SLOT( \ + surface, 3)) + +/** + * \brief Blit one surface onto another. + * + * \param dstobj The destination surface. + * \param srcobj The source surface. + * \param dstrect The destination rectangle. + * \param srcrect The source rectangle. + * \param the_args The blit flags. + * \return 0 for success, -1 or -2 for error. + * + * \note Is accessible through the C api. + * \note int pgSurface_Blit(PyObject *dstobj, PyObject *srcobj, SDL_Rect + * *dstrect, SDL_Rect *srcrect, int the_args) + */ +#define pgSurface_Blit \ + (*(int (*)(pgSurfaceObject *, pgSurfaceObject *, SDL_Rect *, SDL_Rect *, \ + int))PYGAMEAPI_GET_SLOT(surface, 2)) + +#define import_pygame_surface() \ + do { \ + IMPORT_PYGAME_MODULE(surface); \ + if (PyErr_Occurred() != NULL) \ + break; \ + IMPORT_PYGAME_MODULE(surflock); \ + } while (0) + +#define pgSurface_New(surface) pgSurface_New2((surface), 1) +#define pgSurface_NewNoOwn(surface) pgSurface_New2((surface), 0) + +#endif /* ~PYGAMEAPI_SURFACE_INTERNAL */ + +/* + * SURFLOCK module + * auto imported/initialized by surface + */ +#ifndef PYGAMEAPI_SURFLOCK_INTERNAL +#define pgLifetimeLock_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(surflock, 0)) + +#define pgLifetimeLock_Check(x) ((x)->ob_type == &pgLifetimeLock_Type) + +#define pgSurface_Prep(x) \ + if ((x)->subsurface) \ + (*(*(void (*)(pgSurfaceObject *))PYGAMEAPI_GET_SLOT(surflock, 1)))(x) + +#define pgSurface_Unprep(x) \ + if ((x)->subsurface) \ + (*(*(void (*)(pgSurfaceObject *))PYGAMEAPI_GET_SLOT(surflock, 2)))(x) + +#define pgSurface_Lock \ + (*(int (*)(pgSurfaceObject *))PYGAMEAPI_GET_SLOT(surflock, 3)) + +#define pgSurface_Unlock \ + (*(int (*)(pgSurfaceObject *))PYGAMEAPI_GET_SLOT(surflock, 4)) + +#define pgSurface_LockBy \ + (*(int (*)(pgSurfaceObject *, PyObject *))PYGAMEAPI_GET_SLOT(surflock, 5)) + +#define pgSurface_UnlockBy \ + (*(int (*)(pgSurfaceObject *, PyObject *))PYGAMEAPI_GET_SLOT(surflock, 6)) + +#define pgSurface_LockLifetime \ + (*(PyObject * (*)(PyObject *, PyObject *)) PYGAMEAPI_GET_SLOT(surflock, 7)) +#endif + +/* + * EVENT module + */ +typedef struct pgEventObject pgEventObject; + +#ifndef PYGAMEAPI_EVENT_INTERNAL +#define pgEvent_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(event, 0)) + +#define pgEvent_Check(x) ((x)->ob_type == &pgEvent_Type) + +#define pgEvent_New \ + (*(PyObject * (*)(SDL_Event *)) PYGAMEAPI_GET_SLOT(event, 1)) + +#define pgEvent_New2 \ + (*(PyObject * (*)(int, PyObject *)) PYGAMEAPI_GET_SLOT(event, 2)) + +#define pgEvent_FillUserEvent \ + (*(int (*)(pgEventObject *, SDL_Event *))PYGAMEAPI_GET_SLOT(event, 3)) + +#define pg_EnableKeyRepeat (*(int (*)(int, int))PYGAMEAPI_GET_SLOT(event, 4)) + +#define pg_GetKeyRepeat (*(void (*)(int *, int *))PYGAMEAPI_GET_SLOT(event, 5)) + +#define import_pygame_event() IMPORT_PYGAME_MODULE(event) +#endif + +/* + * RWOBJECT module + * the rwobject are only needed for C side work, not accessible from python. + */ +#ifndef PYGAMEAPI_RWOBJECT_INTERNAL +#define pgRWops_FromObject \ + (*(SDL_RWops * (*)(PyObject *, char **)) PYGAMEAPI_GET_SLOT(rwobject, 0)) + +#define pgRWops_IsFileObject \ + (*(int (*)(SDL_RWops *))PYGAMEAPI_GET_SLOT(rwobject, 1)) + +#define pg_EncodeFilePath \ + (*(PyObject * (*)(PyObject *, PyObject *)) PYGAMEAPI_GET_SLOT(rwobject, 2)) + +#define pg_EncodeString \ + (*(PyObject * (*)(PyObject *, const char *, const char *, PyObject *)) \ + PYGAMEAPI_GET_SLOT(rwobject, 3)) + +#define pgRWops_FromFileObject \ + (*(SDL_RWops * (*)(PyObject *)) PYGAMEAPI_GET_SLOT(rwobject, 4)) + +#define pgRWops_ReleaseObject \ + (*(int (*)(SDL_RWops *))PYGAMEAPI_GET_SLOT(rwobject, 5)) + +#define import_pygame_rwobject() IMPORT_PYGAME_MODULE(rwobject) + +#endif + +/* + * PixelArray module + */ +#ifndef PYGAMEAPI_PIXELARRAY_INTERNAL +#define PyPixelArray_Type ((PyTypeObject *)PYGAMEAPI_GET_SLOT(pixelarray, 0)) + +#define PyPixelArray_Check(x) ((x)->ob_type == &PyPixelArray_Type) +#define PyPixelArray_New (*(PyObject * (*)) PYGAMEAPI_GET_SLOT(pixelarray, 1)) + +#define import_pygame_pixelarray() IMPORT_PYGAME_MODULE(pixelarray) +#endif /* PYGAMEAPI_PIXELARRAY_INTERNAL */ + +/* + * Color module + */ +typedef struct pgColorObject pgColorObject; + +#ifndef PYGAMEAPI_COLOR_INTERNAL +#define pgColor_Type (*(PyObject *)PYGAMEAPI_GET_SLOT(color, 0)) + +#define pgColor_Check(x) ((x)->ob_type == &pgColor_Type) +#define pgColor_New (*(PyObject * (*)(Uint8 *)) PYGAMEAPI_GET_SLOT(color, 1)) + +#define pgColor_NewLength \ + (*(PyObject * (*)(Uint8 *, Uint8)) PYGAMEAPI_GET_SLOT(color, 3)) + +#define pg_RGBAFromColorObj \ + (*(int (*)(PyObject *, Uint8 *))PYGAMEAPI_GET_SLOT(color, 2)) + +#define pg_RGBAFromFuzzyColorObj \ + (*(int (*)(PyObject *, Uint8 *))PYGAMEAPI_GET_SLOT(color, 4)) + +#define pgColor_AsArray(x) (((pgColorObject *)x)->data) +#define pgColor_NumComponents(x) (((pgColorObject *)x)->len) + +#define import_pygame_color() IMPORT_PYGAME_MODULE(color) +#endif /* PYGAMEAPI_COLOR_INTERNAL */ + +/* + * Math module + */ +#ifndef PYGAMEAPI_MATH_INTERNAL +#define pgVector2_Check(x) \ + ((x)->ob_type == (PyTypeObject *)PYGAMEAPI_GET_SLOT(math, 0)) + +#define pgVector3_Check(x) \ + ((x)->ob_type == (PyTypeObject *)PYGAMEAPI_GET_SLOT(math, 1)) +/* +#define pgVector2_New \ + (*(PyObject*(*)) \ + PYGAMEAPI_GET_SLOT(PyGAME_C_API, 1)) +*/ +#define import_pygame_math() IMPORT_PYGAME_MODULE(math) +#endif /* PYGAMEAPI_MATH_INTERNAL */ + +#define IMPORT_PYGAME_MODULE _IMPORT_PYGAME_MODULE + +/* + * base pygame API slots + * disable slots with NO_PYGAME_C_API + */ +#ifdef PYGAME_H +PYGAMEAPI_DEFINE_SLOTS(base); +PYGAMEAPI_DEFINE_SLOTS(rect); +PYGAMEAPI_DEFINE_SLOTS(cdrom); +PYGAMEAPI_DEFINE_SLOTS(joystick); +PYGAMEAPI_DEFINE_SLOTS(display); +PYGAMEAPI_DEFINE_SLOTS(surface); +PYGAMEAPI_DEFINE_SLOTS(surflock); +PYGAMEAPI_DEFINE_SLOTS(event); +PYGAMEAPI_DEFINE_SLOTS(rwobject); +PYGAMEAPI_DEFINE_SLOTS(pixelarray); +PYGAMEAPI_DEFINE_SLOTS(color); +PYGAMEAPI_DEFINE_SLOTS(math); +#else /* ~PYGAME_H */ +PYGAMEAPI_EXTERN_SLOTS(base); +PYGAMEAPI_EXTERN_SLOTS(rect); +PYGAMEAPI_EXTERN_SLOTS(cdrom); +PYGAMEAPI_EXTERN_SLOTS(joystick); +PYGAMEAPI_EXTERN_SLOTS(display); +PYGAMEAPI_EXTERN_SLOTS(surface); +PYGAMEAPI_EXTERN_SLOTS(surflock); +PYGAMEAPI_EXTERN_SLOTS(event); +PYGAMEAPI_EXTERN_SLOTS(rwobject); +PYGAMEAPI_EXTERN_SLOTS(pixelarray); +PYGAMEAPI_EXTERN_SLOTS(color); +PYGAMEAPI_EXTERN_SLOTS(math); +#endif /* ~PYGAME_H */ + +#endif /* PYGAME_H */ + +/* Use the end of this file for other cross module inline utility + * functions There seems to be no good reason to stick to macro only + * functions in Python 3. + */ + +static PG_INLINE PyObject * +pg_tuple_couple_from_values_int(int val1, int val2) +{ + /* This function turns two input integers into a python tuple object. + * Currently, 5th November 2022, this is faster than using Py_BuildValue + * to do the same thing. + */ + PyObject *tup = PyTuple_New(2); + if (!tup) { + return NULL; + } + + PyObject *tmp = PyLong_FromLong(val1); + if (!tmp) { + Py_DECREF(tup); + return NULL; + } + PyTuple_SET_ITEM(tup, 0, tmp); + + tmp = PyLong_FromLong(val2); + if (!tmp) { + Py_DECREF(tup); + return NULL; + } + PyTuple_SET_ITEM(tup, 1, tmp); + + return tup; +} + +static PG_INLINE PyObject * +pg_tuple_triple_from_values_int(int val1, int val2, int val3) +{ + /* This function turns three input integers into a python tuple object. + * Currently, 5th November 2022, this is faster than using Py_BuildValue + * to do the same thing. + */ + PyObject *tup = PyTuple_New(3); + if (!tup) { + return NULL; + } + + PyObject *tmp = PyLong_FromLong(val1); + if (!tmp) { + Py_DECREF(tup); + return NULL; + } + PyTuple_SET_ITEM(tup, 0, tmp); + + tmp = PyLong_FromLong(val2); + if (!tmp) { + Py_DECREF(tup); + return NULL; + } + PyTuple_SET_ITEM(tup, 1, tmp); + + tmp = PyLong_FromLong(val3); + if (!tmp) { + Py_DECREF(tup); + return NULL; + } + PyTuple_SET_ITEM(tup, 2, tmp); + + return tup; +} diff --git a/.venv/include/site/python3.11/pygame/include/bitmask.h b/.venv/include/site/python3.11/pygame/include/bitmask.h new file mode 100644 index 00000000..eee09b70 --- /dev/null +++ b/.venv/include/site/python3.11/pygame/include/bitmask.h @@ -0,0 +1,171 @@ +/* + Bitmask 1.7 - A pixel-perfect collision detection library. + + Copyright (C) 2002-2005 Ulf Ekstrom except for the bitcount + function which is copyright (C) Donald W. Gillies, 1992. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef BITMASK_H +#define BITMASK_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +/* Define INLINE for different compilers. If your compiler does not + support inlining then there might be a performance hit in + bitmask_overlap_area(). +*/ +#ifndef INLINE +#ifdef __GNUC__ +#define INLINE inline +#else +#ifdef _MSC_VER +#define INLINE __inline +#else +#define INLINE +#endif +#endif +#endif + +#define BITMASK_W unsigned long int +#define BITMASK_W_LEN (sizeof(BITMASK_W) * CHAR_BIT) +#define BITMASK_W_MASK (BITMASK_W_LEN - 1) +#define BITMASK_N(n) ((BITMASK_W)1 << (n)) + +typedef struct bitmask { + int w, h; + BITMASK_W bits[1]; +} bitmask_t; + +/* Creates a bitmask of width w and height h, where + w and h must both be greater than or equal to 0. + The mask is automatically cleared when created. + */ +bitmask_t * +bitmask_create(int w, int h); + +/* Frees all the memory allocated by bitmask_create for m. */ +void +bitmask_free(bitmask_t *m); + +/* Create a copy of the given bitmask. */ +bitmask_t * +bitmask_copy(bitmask_t *m); + +/* Clears all bits in the mask */ +void +bitmask_clear(bitmask_t *m); + +/* Sets all bits in the mask */ +void +bitmask_fill(bitmask_t *m); + +/* Flips all bits in the mask */ +void +bitmask_invert(bitmask_t *m); + +/* Counts the bits in the mask */ +unsigned int +bitmask_count(bitmask_t *m); + +/* Returns nonzero if the bit at (x,y) is set. Coordinates start at + (0,0) */ +static INLINE int +bitmask_getbit(const bitmask_t *m, int x, int y) +{ + return (m->bits[x / BITMASK_W_LEN * m->h + y] & + BITMASK_N(x & BITMASK_W_MASK)) != 0; +} + +/* Sets the bit at (x,y) */ +static INLINE void +bitmask_setbit(bitmask_t *m, int x, int y) +{ + m->bits[x / BITMASK_W_LEN * m->h + y] |= BITMASK_N(x & BITMASK_W_MASK); +} + +/* Clears the bit at (x,y) */ +static INLINE void +bitmask_clearbit(bitmask_t *m, int x, int y) +{ + m->bits[x / BITMASK_W_LEN * m->h + y] &= ~BITMASK_N(x & BITMASK_W_MASK); +} + +/* Returns nonzero if the masks overlap with the given offset. + The overlap tests uses the following offsets (which may be negative): + + +----+----------.. + |A | yoffset + | +-+----------.. + +--|B + |xoffset + | | + : : +*/ +int +bitmask_overlap(const bitmask_t *a, const bitmask_t *b, int xoffset, + int yoffset); + +/* Like bitmask_overlap(), but will also give a point of intersection. + x and y are given in the coordinates of mask a, and are untouched + if there is no overlap. */ +int +bitmask_overlap_pos(const bitmask_t *a, const bitmask_t *b, int xoffset, + int yoffset, int *x, int *y); + +/* Returns the number of overlapping 'pixels' */ +int +bitmask_overlap_area(const bitmask_t *a, const bitmask_t *b, int xoffset, + int yoffset); + +/* Fills a mask with the overlap of two other masks. A bitwise AND. */ +void +bitmask_overlap_mask(const bitmask_t *a, const bitmask_t *b, bitmask_t *c, + int xoffset, int yoffset); + +/* Draws mask b onto mask a (bitwise OR). Can be used to compose large + (game background?) mask from several submasks, which may speed up + the testing. */ + +void +bitmask_draw(bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset); + +void +bitmask_erase(bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset); + +/* Return a new scaled bitmask, with dimensions w*h. The quality of the + scaling may not be perfect for all circumstances, but it should + be reasonable. If either w or h is 0 a clear 1x1 mask is returned. */ +bitmask_t * +bitmask_scale(const bitmask_t *m, int w, int h); + +/* Convolve b into a, drawing the output into o, shifted by offset. If offset + * is 0, then the (x,y) bit will be set if and only if + * bitmask_overlap(a, b, x - b->w - 1, y - b->h - 1) returns true. + * + * Modifies bits o[xoffset ... xoffset + a->w + b->w - 1) + * [yoffset ... yoffset + a->h + b->h - 1). */ +void +bitmask_convolve(const bitmask_t *a, const bitmask_t *b, bitmask_t *o, + int xoffset, int yoffset); + +#ifdef __cplusplus +} /* End of extern "C" { */ +#endif + +#endif diff --git a/.venv/include/site/python3.11/pygame/include/pgcompat.h b/.venv/include/site/python3.11/pygame/include/pgcompat.h new file mode 100644 index 00000000..3c09099a --- /dev/null +++ b/.venv/include/site/python3.11/pygame/include/pgcompat.h @@ -0,0 +1,102 @@ +#if !defined(PGCOMPAT_H) +#define PGCOMPAT_H + +#include + +/* In CPython, Py_Exit finalises the python interpreter before calling C exit() + * This does not exist on PyPy, so use exit() directly here */ +#ifdef PYPY_VERSION +#define PG_EXIT(n) exit(n) +#else +#define PG_EXIT(n) Py_Exit(n) +#endif + +/* define common types where SDL is not included */ +#ifndef SDL_VERSION_ATLEAST +#ifdef _MSC_VER +typedef unsigned __int8 uint8_t; +typedef unsigned __int32 uint32_t; +#else +#include +#endif +typedef uint32_t Uint32; +typedef uint8_t Uint8; +#endif /* no SDL */ + +#if defined(SDL_VERSION_ATLEAST) + +#ifndef SDL_WINDOW_VULKAN +#define SDL_WINDOW_VULKAN 0 +#endif + +#ifndef SDL_WINDOW_ALWAYS_ON_TOP +#define SDL_WINDOW_ALWAYS_ON_TOP 0 +#endif + +#ifndef SDL_WINDOW_SKIP_TASKBAR +#define SDL_WINDOW_SKIP_TASKBAR 0 +#endif + +#ifndef SDL_WINDOW_UTILITY +#define SDL_WINDOW_UTILITY 0 +#endif + +#ifndef SDL_WINDOW_TOOLTIP +#define SDL_WINDOW_TOOLTIP 0 +#endif + +#ifndef SDL_WINDOW_POPUP_MENU +#define SDL_WINDOW_POPUP_MENU 0 +#endif + +#ifndef SDL_WINDOW_INPUT_GRABBED +#define SDL_WINDOW_INPUT_GRABBED 0 +#endif + +#ifndef SDL_WINDOW_INPUT_FOCUS +#define SDL_WINDOW_INPUT_FOCUS 0 +#endif + +#ifndef SDL_WINDOW_MOUSE_FOCUS +#define SDL_WINDOW_MOUSE_FOCUS 0 +#endif + +#ifndef SDL_WINDOW_FOREIGN +#define SDL_WINDOW_FOREIGN 0 +#endif + +#ifndef SDL_WINDOW_ALLOW_HIGHDPI +#define SDL_WINDOW_ALLOW_HIGHDPI 0 +#endif + +#ifndef SDL_WINDOW_MOUSE_CAPTURE +#define SDL_WINDOW_MOUSE_CAPTURE 0 +#endif + +#ifndef SDL_WINDOW_ALWAYS_ON_TOP +#define SDL_WINDOW_ALWAYS_ON_TOP 0 +#endif + +#ifndef SDL_WINDOW_SKIP_TASKBAR +#define SDL_WINDOW_SKIP_TASKBAR 0 +#endif + +#ifndef SDL_WINDOW_UTILITY +#define SDL_WINDOW_UTILITY 0 +#endif + +#ifndef SDL_WINDOW_TOOLTIP +#define SDL_WINDOW_TOOLTIP 0 +#endif + +#ifndef SDL_WINDOW_POPUP_MENU +#define SDL_WINDOW_POPUP_MENU 0 +#endif + +#ifndef SDL_MOUSEWHEEL_FLIPPED +#define NO_SDL_MOUSEWHEEL_FLIPPED +#endif + +#endif /* defined(SDL_VERSION_ATLEAST) */ + +#endif /* ~defined(PGCOMPAT_H) */ diff --git a/.venv/include/site/python3.11/pygame/include/pgimport.h b/.venv/include/site/python3.11/pygame/include/pgimport.h new file mode 100644 index 00000000..2c2e8cfb --- /dev/null +++ b/.venv/include/site/python3.11/pygame/include/pgimport.h @@ -0,0 +1,67 @@ +#ifndef PGIMPORT_H +#define PGIMPORT_H + +/* Prefix when importing module */ +#define IMPPREFIX "pygame." + +#include "pgcompat.h" + +#define PYGAMEAPI_LOCAL_ENTRY "_PYGAME_C_API" +#define PG_CAPSULE_NAME(m) (IMPPREFIX m "." PYGAMEAPI_LOCAL_ENTRY) + +/* + * fill API slots defined by PYGAMEAPI_DEFINE_SLOTS/PYGAMEAPI_EXTERN_SLOTS + */ +#define _IMPORT_PYGAME_MODULE(module) \ + { \ + PyObject *_mod_##module = PyImport_ImportModule(IMPPREFIX #module); \ + \ + if (_mod_##module != NULL) { \ + PyObject *_c_api = \ + PyObject_GetAttrString(_mod_##module, PYGAMEAPI_LOCAL_ENTRY); \ + \ + Py_DECREF(_mod_##module); \ + if (_c_api != NULL && PyCapsule_CheckExact(_c_api)) { \ + void **localptr = (void **)PyCapsule_GetPointer( \ + _c_api, PG_CAPSULE_NAME(#module)); \ + _PGSLOTS_##module = localptr; \ + } \ + Py_XDECREF(_c_api); \ + } \ + } + +#define PYGAMEAPI_IS_IMPORTED(module) (_PGSLOTS_##module != NULL) + +/* + * source file must include one of these in order to use _IMPORT_PYGAME_MODULE. + * this is set by import_pygame_*() functions. + * disable with NO_PYGAME_C_API + */ +#define PYGAMEAPI_DEFINE_SLOTS(module) void **_PGSLOTS_##module = NULL +#define PYGAMEAPI_EXTERN_SLOTS(module) extern void **_PGSLOTS_##module +#define PYGAMEAPI_GET_SLOT(module, index) _PGSLOTS_##module[(index)] + +/* + * disabled API with NO_PYGAME_C_API; do nothing instead + */ +#ifdef NO_PYGAME_C_API + +#undef PYGAMEAPI_DEFINE_SLOTS +#undef PYGAMEAPI_EXTERN_SLOTS + +#define PYGAMEAPI_DEFINE_SLOTS(module) +#define PYGAMEAPI_EXTERN_SLOTS(module) + +/* intentionally leave this defined to cause a compiler error * +#define PYGAMEAPI_GET_SLOT(api_root, index) +#undef PYGAMEAPI_GET_SLOT*/ + +#undef _IMPORT_PYGAME_MODULE +#define _IMPORT_PYGAME_MODULE(module) + +#endif /* NO_PYGAME_C_API */ + +#define encapsulate_api(ptr, module) \ + PyCapsule_New(ptr, PG_CAPSULE_NAME(module), NULL) + +#endif /* ~PGIMPORT_H */ diff --git a/.venv/include/site/python3.11/pygame/include/pgplatform.h b/.venv/include/site/python3.11/pygame/include/pgplatform.h new file mode 100644 index 00000000..4c299dac --- /dev/null +++ b/.venv/include/site/python3.11/pygame/include/pgplatform.h @@ -0,0 +1,87 @@ +/* platform/compiler adjustments */ +#ifndef PG_PLATFORM_H +#define PG_PLATFORM_H + +#if defined(HAVE_SNPRINTF) /* defined in python.h (pyerrors.h) and SDL.h \ + (SDL_config.h) */ +#undef HAVE_SNPRINTF /* remove GCC redefine warning */ +#endif /* HAVE_SNPRINTF */ + +#ifndef PG_INLINE +#if defined(__clang__) +#define PG_INLINE __inline__ __attribute__((__unused__)) +#elif defined(__GNUC__) +#define PG_INLINE __inline__ +#elif defined(_MSC_VER) +#define PG_INLINE __inline +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define PG_INLINE inline +#else +#define PG_INLINE +#endif +#endif /* ~PG_INLINE */ + +// Worth trying this on MSVC/win32 builds to see if provides any speed up +#ifndef PG_FORCEINLINE +#if defined(__clang__) +#define PG_FORCEINLINE __inline__ __attribute__((__unused__)) +#elif defined(__GNUC__) +#define PG_FORCEINLINE __inline__ +#elif defined(_MSC_VER) +#define PG_FORCEINLINE __forceinline +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define PG_FORCEINLINE inline +#else +#define PG_FORCEINLINE +#endif +#endif /* ~PG_FORCEINLINE */ + +/* This is unconditionally defined in Python.h */ +#if defined(_POSIX_C_SOURCE) +#undef _POSIX_C_SOURCE +#endif + +#if defined(HAVE_SNPRINTF) +#undef HAVE_SNPRINTF +#endif + +/* SDL needs WIN32 */ +#if !defined(WIN32) && \ + (defined(MS_WIN32) || defined(_WIN32) || defined(__WIN32) || \ + defined(__WIN32__) || defined(_WINDOWS)) +#define WIN32 +#endif + +/* Commenting out SSE4_2 stuff because it does not do runtime detection. +#ifndef PG_TARGET_SSE4_2 +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ == 4 && +__GNUC_MINOR__ >= 9) || __GNUC__ >= 5 )) +//The old gcc 4.8 on centos used by manylinux1 does not seem to get sse4.2 +intrinsics #define PG_FUNCTION_TARGET_SSE4_2 __attribute__((target("sse4.2"))) +// No else; we define the fallback later +#endif +#endif +*/ +/* ~PG_TARGET_SSE4_2 */ + +/* +#ifdef PG_FUNCTION_TARGET_SSE4_2 +#if !defined(__SSE4_2__) && !defined(PG_COMPILE_SSE4_2) +#if defined(__x86_64__) || defined(__i386__) +#define PG_COMPILE_SSE4_2 1 +#endif +#endif +#endif +*/ +/* ~PG_TARGET_SSE4_2 */ + +/* Fallback definition of target attribute */ +#ifndef PG_FUNCTION_TARGET_SSE4_2 +#define PG_FUNCTION_TARGET_SSE4_2 +#endif + +#ifndef PG_COMPILE_SSE4_2 +#define PG_COMPILE_SSE4_2 0 +#endif + +#endif /* ~PG_PLATFORM_H */ diff --git a/.venv/include/site/python3.11/pygame/include/pygame.h b/.venv/include/site/python3.11/pygame/include/pygame.h new file mode 100644 index 00000000..3772ae6a --- /dev/null +++ b/.venv/include/site/python3.11/pygame/include/pygame.h @@ -0,0 +1,34 @@ +/* + pygame - Python Game Library + Copyright (C) 2000-2001 Pete Shinners + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Pete Shinners + pete@shinners.org +*/ + +/* To allow the Pygame C api to be globally shared by all code within an + * extension module built from multiple C files, only include the pygame.h + * header within the top level C file, the one which calls the + * 'import_pygame_*' macros. All other C source files of the module should + * include _pygame.h instead. + */ +#ifndef PYGAME_H +#define PYGAME_H + +#include "_pygame.h" + +#endif diff --git a/.venv/include/site/python3.11/pygame/include/pygame_bufferproxy.h b/.venv/include/site/python3.11/pygame/include/pygame_bufferproxy.h new file mode 100644 index 00000000..9284ff29 --- /dev/null +++ b/.venv/include/site/python3.11/pygame/include/pygame_bufferproxy.h @@ -0,0 +1,56 @@ +/* + pygame - Python Game Library + Copyright (C) 2000-2001 Pete Shinners + Copyright (C) 2007 Rene Dudfield, Richard Goedeken + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Pete Shinners + pete@shinners.org +*/ + +/* Bufferproxy module C api. */ +#if !defined(PG_BUFPROXY_HEADER) +#define PG_BUFPROXY_HEADER + +#include + +typedef PyObject *(*_pgbufproxy_new_t)(PyObject *, getbufferproc); +typedef PyObject *(*_pgbufproxy_get_obj_t)(PyObject *); +typedef int (*_pgbufproxy_trip_t)(PyObject *); + +#ifndef PYGAMEAPI_BUFPROXY_INTERNAL + +#include "pgimport.h" + +PYGAMEAPI_DEFINE_SLOTS(bufferproxy); + +#define pgBufproxy_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(bufferproxy, 0)) + +#define pgBufproxy_Check(x) ((x)->ob_type == &pgBufproxy_Type) + +#define pgBufproxy_New (*(_pgbufproxy_new_t)PYGAMEAPI_GET_SLOT(bufferproxy, 1)) + +#define pgBufproxy_GetParent \ + (*(_pgbufproxy_get_obj_t)PYGAMEAPI_GET_SLOT(bufferproxy, 2)) + +#define pgBufproxy_Trip \ + (*(_pgbufproxy_trip_t)PYGAMEAPI_GET_SLOT(bufferproxy, 3)) + +#define import_pygame_bufferproxy() _IMPORT_PYGAME_MODULE(bufferproxy) + +#endif /* ~PYGAMEAPI_BUFPROXY_INTERNAL */ + +#endif /* ~defined(PG_BUFPROXY_HEADER) */ diff --git a/.venv/include/site/python3.11/pygame/include/pygame_font.h b/.venv/include/site/python3.11/pygame/include/pygame_font.h new file mode 100644 index 00000000..aae41bf9 --- /dev/null +++ b/.venv/include/site/python3.11/pygame/include/pygame_font.h @@ -0,0 +1,50 @@ +/* + pygame - Python Game Library + Copyright (C) 2000-2001 Pete Shinners + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Pete Shinners + pete@shinners.org +*/ + +#include +#include "pgplatform.h" + +struct TTF_Font; + +typedef struct { + PyObject_HEAD TTF_Font *font; + PyObject *weakreflist; + unsigned int ttf_init_generation; +} PyFontObject; +#define PyFont_AsFont(x) (((PyFontObject *)x)->font) + +#ifndef PYGAMEAPI_FONT_INTERNAL + +#include "pgimport.h" + +PYGAMEAPI_DEFINE_SLOTS(font); + +#define PyFont_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(font, 0)) +#define PyFont_Check(x) ((x)->ob_type == &PyFont_Type) + +#define PyFont_New (*(PyObject * (*)(TTF_Font *)) PYGAMEAPI_GET_SLOT(font, 1)) + +/*slot 2 taken by FONT_INIT_CHECK*/ + +#define import_pygame_font() _IMPORT_PYGAME_MODULE(font) + +#endif diff --git a/.venv/include/site/python3.11/pygame/include/pygame_freetype.h b/.venv/include/site/python3.11/pygame/include/pygame_freetype.h new file mode 100644 index 00000000..90172ccf --- /dev/null +++ b/.venv/include/site/python3.11/pygame/include/pygame_freetype.h @@ -0,0 +1,42 @@ +/* + pygame - Python Game Library + Copyright (C) 2009 Vicent Marti + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#ifndef PYGAME_FREETYPE_H_ +#define PYGAME_FREETYPE_H_ + +#include "pgplatform.h" +#include "pgimport.h" +#include "pgcompat.h" + +#ifndef PYGAME_FREETYPE_INTERNAL + +PYGAMEAPI_DEFINE_SLOTS(_freetype); + +#define pgFont_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(_freetype, 0)) + +#define pgFont_Check(x) ((x)->ob_type == &pgFont_Type) + +#define pgFont_New \ + (*(PyObject * (*)(const char *, long)) PYGAMEAPI_GET_SLOT(_freetype, 1)) + +#define import_pygame_freetype() _IMPORT_PYGAME_MODULE(_freetype) + +#endif /* PYGAME_FREETYPE_INTERNAL */ + +#endif /* PYGAME_FREETYPE_H_ */ diff --git a/.venv/include/site/python3.11/pygame/include/pygame_mask.h b/.venv/include/site/python3.11/pygame/include/pygame_mask.h new file mode 100644 index 00000000..8dd8f170 --- /dev/null +++ b/.venv/include/site/python3.11/pygame/include/pygame_mask.h @@ -0,0 +1,45 @@ +/* + pygame - Python Game Library + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef PGMASK_H +#define PGMASK_H + +#include +#include "bitmask.h" + +typedef struct { + PyObject_HEAD bitmask_t *mask; + void *bufdata; +} pgMaskObject; + +#define pgMask_AsBitmap(x) (((pgMaskObject *)x)->mask) + +#ifndef PYGAMEAPI_MASK_INTERNAL + +#include "pgimport.h" + +PYGAMEAPI_DEFINE_SLOTS(mask); + +#define pgMask_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(mask, 0)) +#define pgMask_Check(x) ((x)->ob_type == &pgMask_Type) + +#define import_pygame_mask() _IMPORT_PYGAME_MODULE(mask) + +#endif /* ~PYGAMEAPI_MASK_INTERNAL */ + +#endif /* ~PGMASK_H */ diff --git a/.venv/include/site/python3.11/pygame/include/pygame_mixer.h b/.venv/include/site/python3.11/pygame/include/pygame_mixer.h new file mode 100644 index 00000000..e19d273b --- /dev/null +++ b/.venv/include/site/python3.11/pygame/include/pygame_mixer.h @@ -0,0 +1,71 @@ +/* + pygame - Python Game Library + Copyright (C) 2000-2001 Pete Shinners + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Pete Shinners + pete@shinners.org +*/ + +#ifndef PGMIXER_H +#define PGMIXER_H + +#include +#include + +#include "pgcompat.h" + +struct Mix_Chunk; + +typedef struct { + PyObject_HEAD Mix_Chunk *chunk; + Uint8 *mem; + PyObject *weakreflist; +} pgSoundObject; + +typedef struct { + PyObject_HEAD int chan; +} pgChannelObject; + +#define pgSound_AsChunk(x) (((pgSoundObject *)x)->chunk) +#define pgChannel_AsInt(x) (((pgChannelObject *)x)->chan) + +#include "pgimport.h" + +#ifndef PYGAMEAPI_MIXER_INTERNAL + +PYGAMEAPI_DEFINE_SLOTS(mixer); + +#define pgSound_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(mixer, 0)) + +#define pgSound_Check(x) ((x)->ob_type == &pgSound_Type) + +#define pgSound_New \ + (*(PyObject * (*)(Mix_Chunk *)) PYGAMEAPI_GET_SLOT(mixer, 1)) + +#define pgSound_Play \ + (*(PyObject * (*)(PyObject *, PyObject *)) PYGAMEAPI_GET_SLOT(mixer, 2)) + +#define pgChannel_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(mixer, 3)) +#define pgChannel_Check(x) ((x)->ob_type == &pgChannel_Type) + +#define pgChannel_New (*(PyObject * (*)(int)) PYGAMEAPI_GET_SLOT(mixer, 4)) + +#define import_pygame_mixer() _IMPORT_PYGAME_MODULE(mixer) + +#endif /* PYGAMEAPI_MIXER_INTERNAL */ + +#endif /* ~PGMIXER_H */ diff --git a/.venv/include/site/python3.11/pygame/include/sse2neon.h b/.venv/include/site/python3.11/pygame/include/sse2neon.h new file mode 100644 index 00000000..a3e3ac0d --- /dev/null +++ b/.venv/include/site/python3.11/pygame/include/sse2neon.h @@ -0,0 +1,6203 @@ +#ifndef SSE2NEON_H +#define SSE2NEON_H + +// This header file provides a simple API translation layer +// between SSE intrinsics to their corresponding Arm/Aarch64 NEON versions +// +// This header file does not yet translate all of the SSE intrinsics. +// +// Contributors to this work are: +// John W. Ratcliff +// Brandon Rowlett +// Ken Fast +// Eric van Beurden +// Alexander Potylitsin +// Hasindu Gamaarachchi +// Jim Huang +// Mark Cheng +// Malcolm James MacLeod +// Devin Hussey (easyaspi314) +// Sebastian Pop +// Developer Ecosystem Engineering +// Danila Kutenin +// François Turban (JishinMaster) +// Pei-Hsuan Hung +// Yang-Hao Yuan + +/* + * sse2neon is freely redistributable under the MIT License. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Tunable configurations */ + +/* Enable precise implementation of _mm_min_ps and _mm_max_ps + * This would slow down the computation a bit, but gives consistent result with + * x86 SSE2. (e.g. would solve a hole or NaN pixel in the rendering result) + */ +#ifndef SSE2NEON_PRECISE_MINMAX +#define SSE2NEON_PRECISE_MINMAX (0) +#endif + +#if defined(__GNUC__) || defined(__clang__) +#pragma push_macro("FORCE_INLINE") +#pragma push_macro("ALIGN_STRUCT") +#define FORCE_INLINE static inline __attribute__((always_inline)) +#define ALIGN_STRUCT(x) __attribute__((aligned(x))) +#else +#error "Macro name collisions may happen with unsupported compiler." +#ifdef FORCE_INLINE +#undef FORCE_INLINE +#endif +#define FORCE_INLINE static inline +#ifndef ALIGN_STRUCT +#define ALIGN_STRUCT(x) __declspec(align(x)) +#endif +#endif + +#include +#include + +// These cause the build to fail on raspberry pi with 'unsupported target' +// and don't seem to do anything particularly useful +///* Architecture-specific build options */ +///* FIXME: #pragma GCC push_options is only available on GCC */ +//#if defined(__GNUC__) +//#if defined(__arm__) && __ARM_ARCH == 7 +///* According to ARM C Language Extensions Architecture specification, +// * __ARM_NEON is defined to a value indicating the Advanced SIMD (NEON) +// * architecture supported. +// */ +//#if !defined(__ARM_NEON) || !defined(__ARM_NEON__) +//#error "You must enable NEON instructions (e.g. -mfpu=neon) to use SSE2NEON." +//#endif +//#pragma GCC push_options +//#pragma GCC target("fpu=neon") +//#elif defined(__aarch64__) +//#pragma GCC push_options +//#pragma GCC target("+simd") +//#else +//#error "Unsupported target. Must be either ARMv7-A+NEON or ARMv8-A." +//#endif +//#endif + +#include + +/* Rounding functions require either Aarch64 instructions or libm failback */ +#if !defined(__aarch64__) +#include +#endif + +/* "__has_builtin" can be used to query support for built-in functions + * provided by gcc/clang and other compilers that support it. + */ +#ifndef __has_builtin /* GCC prior to 10 or non-clang compilers */ +/* Compatibility with gcc <= 9 */ +#if __GNUC__ <= 9 +#define __has_builtin(x) HAS##x +#define HAS__builtin_popcount 1 +#define HAS__builtin_popcountll 1 +#else +#define __has_builtin(x) 0 +#endif +#endif + +/** + * MACRO for shuffle parameter for _mm_shuffle_ps(). + * Argument fp3 is a digit[0123] that represents the fp from argument "b" + * of mm_shuffle_ps that will be placed in fp3 of result. fp2 is the same + * for fp2 in result. fp1 is a digit[0123] that represents the fp from + * argument "a" of mm_shuffle_ps that will be places in fp1 of result. + * fp0 is the same for fp0 of result. + */ +#define _MM_SHUFFLE(fp3, fp2, fp1, fp0) \ + (((fp3) << 6) | ((fp2) << 4) | ((fp1) << 2) | ((fp0))) + +/* Rounding mode macros. */ +#define _MM_FROUND_TO_NEAREST_INT 0x00 +#define _MM_FROUND_TO_NEG_INF 0x01 +#define _MM_FROUND_TO_POS_INF 0x02 +#define _MM_FROUND_TO_ZERO 0x03 +#define _MM_FROUND_CUR_DIRECTION 0x04 +#define _MM_FROUND_NO_EXC 0x08 + +/* indicate immediate constant argument in a given range */ +#define __constrange(a, b) const + +/* A few intrinsics accept traditional data types like ints or floats, but + * most operate on data types that are specific to SSE. + * If a vector type ends in d, it contains doubles, and if it does not have + * a suffix, it contains floats. An integer vector type can contain any type + * of integer, from chars to shorts to unsigned long longs. + */ +typedef int64x1_t __m64; +typedef float32x4_t __m128; /* 128-bit vector containing 4 floats */ +// On ARM 32-bit architecture, the float64x2_t is not supported. +// The data type __m128d should be represented in a different way for related +// intrinsic conversion. +#if defined(__aarch64__) +typedef float64x2_t __m128d; /* 128-bit vector containing 2 doubles */ +#else +typedef float32x4_t __m128d; +#endif +typedef int64x2_t __m128i; /* 128-bit vector containing integers */ + +/* type-safe casting between types */ + +#define vreinterpretq_m128_f16(x) vreinterpretq_f32_f16(x) +#define vreinterpretq_m128_f32(x) (x) +#define vreinterpretq_m128_f64(x) vreinterpretq_f32_f64(x) + +#define vreinterpretq_m128_u8(x) vreinterpretq_f32_u8(x) +#define vreinterpretq_m128_u16(x) vreinterpretq_f32_u16(x) +#define vreinterpretq_m128_u32(x) vreinterpretq_f32_u32(x) +#define vreinterpretq_m128_u64(x) vreinterpretq_f32_u64(x) + +#define vreinterpretq_m128_s8(x) vreinterpretq_f32_s8(x) +#define vreinterpretq_m128_s16(x) vreinterpretq_f32_s16(x) +#define vreinterpretq_m128_s32(x) vreinterpretq_f32_s32(x) +#define vreinterpretq_m128_s64(x) vreinterpretq_f32_s64(x) + +#define vreinterpretq_f16_m128(x) vreinterpretq_f16_f32(x) +#define vreinterpretq_f32_m128(x) (x) +#define vreinterpretq_f64_m128(x) vreinterpretq_f64_f32(x) + +#define vreinterpretq_u8_m128(x) vreinterpretq_u8_f32(x) +#define vreinterpretq_u16_m128(x) vreinterpretq_u16_f32(x) +#define vreinterpretq_u32_m128(x) vreinterpretq_u32_f32(x) +#define vreinterpretq_u64_m128(x) vreinterpretq_u64_f32(x) + +#define vreinterpretq_s8_m128(x) vreinterpretq_s8_f32(x) +#define vreinterpretq_s16_m128(x) vreinterpretq_s16_f32(x) +#define vreinterpretq_s32_m128(x) vreinterpretq_s32_f32(x) +#define vreinterpretq_s64_m128(x) vreinterpretq_s64_f32(x) + +#define vreinterpretq_m128i_s8(x) vreinterpretq_s64_s8(x) +#define vreinterpretq_m128i_s16(x) vreinterpretq_s64_s16(x) +#define vreinterpretq_m128i_s32(x) vreinterpretq_s64_s32(x) +#define vreinterpretq_m128i_s64(x) (x) + +#define vreinterpretq_m128i_u8(x) vreinterpretq_s64_u8(x) +#define vreinterpretq_m128i_u16(x) vreinterpretq_s64_u16(x) +#define vreinterpretq_m128i_u32(x) vreinterpretq_s64_u32(x) +#define vreinterpretq_m128i_u64(x) vreinterpretq_s64_u64(x) + +#define vreinterpretq_s8_m128i(x) vreinterpretq_s8_s64(x) +#define vreinterpretq_s16_m128i(x) vreinterpretq_s16_s64(x) +#define vreinterpretq_s32_m128i(x) vreinterpretq_s32_s64(x) +#define vreinterpretq_s64_m128i(x) (x) + +#define vreinterpretq_u8_m128i(x) vreinterpretq_u8_s64(x) +#define vreinterpretq_u16_m128i(x) vreinterpretq_u16_s64(x) +#define vreinterpretq_u32_m128i(x) vreinterpretq_u32_s64(x) +#define vreinterpretq_u64_m128i(x) vreinterpretq_u64_s64(x) + +#define vreinterpret_m64_s8(x) vreinterpret_s64_s8(x) +#define vreinterpret_m64_s16(x) vreinterpret_s64_s16(x) +#define vreinterpret_m64_s32(x) vreinterpret_s64_s32(x) +#define vreinterpret_m64_s64(x) (x) + +#define vreinterpret_m64_u8(x) vreinterpret_s64_u8(x) +#define vreinterpret_m64_u16(x) vreinterpret_s64_u16(x) +#define vreinterpret_m64_u32(x) vreinterpret_s64_u32(x) +#define vreinterpret_m64_u64(x) vreinterpret_s64_u64(x) + +#define vreinterpret_m64_f16(x) vreinterpret_s64_f16(x) +#define vreinterpret_m64_f32(x) vreinterpret_s64_f32(x) +#define vreinterpret_m64_f64(x) vreinterpret_s64_f64(x) + +#define vreinterpret_u8_m64(x) vreinterpret_u8_s64(x) +#define vreinterpret_u16_m64(x) vreinterpret_u16_s64(x) +#define vreinterpret_u32_m64(x) vreinterpret_u32_s64(x) +#define vreinterpret_u64_m64(x) vreinterpret_u64_s64(x) + +#define vreinterpret_s8_m64(x) vreinterpret_s8_s64(x) +#define vreinterpret_s16_m64(x) vreinterpret_s16_s64(x) +#define vreinterpret_s32_m64(x) vreinterpret_s32_s64(x) +#define vreinterpret_s64_m64(x) (x) + +#define vreinterpret_f32_m64(x) vreinterpret_f32_s64(x) + +#if defined(__aarch64__) +#define vreinterpretq_m128d_s32(x) vreinterpretq_f64_s32(x) +#define vreinterpretq_m128d_s64(x) vreinterpretq_f64_s64(x) + +#define vreinterpretq_m128d_f64(x) (x) + +#define vreinterpretq_s64_m128d(x) vreinterpretq_s64_f64(x) + +#define vreinterpretq_f64_m128d(x) (x) +#else +#define vreinterpretq_m128d_s32(x) vreinterpretq_f32_s32(x) +#define vreinterpretq_m128d_s64(x) vreinterpretq_f32_s64(x) + +#define vreinterpretq_m128d_f32(x) (x) + +#define vreinterpretq_s64_m128d(x) vreinterpretq_s64_f32(x) + +#define vreinterpretq_f32_m128d(x) (x) +#endif + +// A struct is defined in this header file called 'SIMDVec' which can be used +// by applications which attempt to access the contents of an _m128 struct +// directly. It is important to note that accessing the __m128 struct directly +// is bad coding practice by Microsoft: @see: +// https://msdn.microsoft.com/en-us/library/ayeb3ayc.aspx +// +// However, some legacy source code may try to access the contents of an __m128 +// struct directly so the developer can use the SIMDVec as an alias for it. Any +// casting must be done manually by the developer, as you cannot cast or +// otherwise alias the base NEON data type for intrinsic operations. +// +// union intended to allow direct access to an __m128 variable using the names +// that the MSVC compiler provides. This union should really only be used when +// trying to access the members of the vector as integer values. GCC/clang +// allow native access to the float members through a simple array access +// operator (in C since 4.6, in C++ since 4.8). +// +// Ideally direct accesses to SIMD vectors should not be used since it can cause +// a performance hit. If it really is needed however, the original __m128 +// variable can be aliased with a pointer to this union and used to access +// individual components. The use of this union should be hidden behind a macro +// that is used throughout the codebase to access the members instead of always +// declaring this type of variable. +typedef union ALIGN_STRUCT(16) SIMDVec { + float m128_f32[4]; // as floats - DON'T USE. Added for convenience. + int8_t m128_i8[16]; // as signed 8-bit integers. + int16_t m128_i16[8]; // as signed 16-bit integers. + int32_t m128_i32[4]; // as signed 32-bit integers. + int64_t m128_i64[2]; // as signed 64-bit integers. + uint8_t m128_u8[16]; // as unsigned 8-bit integers. + uint16_t m128_u16[8]; // as unsigned 16-bit integers. + uint32_t m128_u32[4]; // as unsigned 32-bit integers. + uint64_t m128_u64[2]; // as unsigned 64-bit integers. +} SIMDVec; + +// casting using SIMDVec +#define vreinterpretq_nth_u64_m128i(x, n) (((SIMDVec *) &x)->m128_u64[n]) +#define vreinterpretq_nth_u32_m128i(x, n) (((SIMDVec *) &x)->m128_u32[n]) +#define vreinterpretq_nth_u8_m128i(x, n) (((SIMDVec *) &x)->m128_u8[n]) + +/* Backwards compatibility for compilers with lack of specific type support */ + +// Older gcc does not define vld1q_u8_x4 type +#if defined(__GNUC__) && !defined(__clang__) +#if __GNUC__ <= 9 +FORCE_INLINE uint8x16x4_t vld1q_u8_x4(const uint8_t *p) +{ + uint8x16x4_t ret; + ret.val[0] = vld1q_u8(p + 0); + ret.val[1] = vld1q_u8(p + 16); + ret.val[2] = vld1q_u8(p + 32); + ret.val[3] = vld1q_u8(p + 48); + return ret; +} +#endif +#endif + +/* Function Naming Conventions + * The naming convention of SSE intrinsics is straightforward. A generic SSE + * intrinsic function is given as follows: + * _mm__ + * + * The parts of this format are given as follows: + * 1. describes the operation performed by the intrinsic + * 2. identifies the data type of the function's primary arguments + * + * This last part, , is a little complicated. It identifies the + * content of the input values, and can be set to any of the following values: + * + ps - vectors contain floats (ps stands for packed single-precision) + * + pd - vectors cantain doubles (pd stands for packed double-precision) + * + epi8/epi16/epi32/epi64 - vectors contain 8-bit/16-bit/32-bit/64-bit + * signed integers + * + epu8/epu16/epu32/epu64 - vectors contain 8-bit/16-bit/32-bit/64-bit + * unsigned integers + * + si128 - unspecified 128-bit vector or 256-bit vector + * + m128/m128i/m128d - identifies input vector types when they are different + * than the type of the returned vector + * + * For example, _mm_setzero_ps. The _mm implies that the function returns + * a 128-bit vector. The _ps at the end implies that the argument vectors + * contain floats. + * + * A complete example: Byte Shuffle - pshufb (_mm_shuffle_epi8) + * // Set packed 16-bit integers. 128 bits, 8 short, per 16 bits + * __m128i v_in = _mm_setr_epi16(1, 2, 3, 4, 5, 6, 7, 8); + * // Set packed 8-bit integers + * // 128 bits, 16 chars, per 8 bits + * __m128i v_perm = _mm_setr_epi8(1, 0, 2, 3, 8, 9, 10, 11, + * 4, 5, 12, 13, 6, 7, 14, 15); + * // Shuffle packed 8-bit integers + * __m128i v_out = _mm_shuffle_epi8(v_in, v_perm); // pshufb + * + * Data (Number, Binary, Byte Index): + +------+------+-------------+------+------+-------------+ + | 1 | 2 | 3 | 4 | Number + +------+------+------+------+------+------+------+------+ + | 0000 | 0001 | 0000 | 0010 | 0000 | 0011 | 0000 | 0100 | Binary + +------+------+------+------+------+------+------+------+ + | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | Index + +------+------+------+------+------+------+------+------+ + + +------+------+------+------+------+------+------+------+ + | 5 | 6 | 7 | 8 | Number + +------+------+------+------+------+------+------+------+ + | 0000 | 0101 | 0000 | 0110 | 0000 | 0111 | 0000 | 1000 | Binary + +------+------+------+------+------+------+------+------+ + | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | Index + +------+------+------+------+------+------+------+------+ + * Index (Byte Index): + +------+------+------+------+------+------+------+------+ + | 1 | 0 | 2 | 3 | 8 | 9 | 10 | 11 | + +------+------+------+------+------+------+------+------+ + + +------+------+------+------+------+------+------+------+ + | 4 | 5 | 12 | 13 | 6 | 7 | 14 | 15 | + +------+------+------+------+------+------+------+------+ + * Result: + +------+------+------+------+------+------+------+------+ + | 1 | 0 | 2 | 3 | 8 | 9 | 10 | 11 | Index + +------+------+------+------+------+------+------+------+ + | 0001 | 0000 | 0000 | 0010 | 0000 | 0101 | 0000 | 0110 | Binary + +------+------+------+------+------+------+------+------+ + | 256 | 2 | 5 | 6 | Number + +------+------+------+------+------+------+------+------+ + + +------+------+------+------+------+------+------+------+ + | 4 | 5 | 12 | 13 | 6 | 7 | 14 | 15 | Index + +------+------+------+------+------+------+------+------+ + | 0000 | 0011 | 0000 | 0111 | 0000 | 0100 | 0000 | 1000 | Binary + +------+------+------+------+------+------+------+------+ + | 3 | 7 | 4 | 8 | Number + +------+------+------+------+------+------+-------------+ + */ + +/* Set/get methods */ + +/* Constants for use with _mm_prefetch. */ +enum _mm_hint { + _MM_HINT_NTA = 0, /* load data to L1 and L2 cache, mark it as NTA */ + _MM_HINT_T0 = 1, /* load data to L1 and L2 cache */ + _MM_HINT_T1 = 2, /* load data to L2 cache only */ + _MM_HINT_T2 = 3, /* load data to L2 cache only, mark it as NTA */ + _MM_HINT_ENTA = 4, /* exclusive version of _MM_HINT_NTA */ + _MM_HINT_ET0 = 5, /* exclusive version of _MM_HINT_T0 */ + _MM_HINT_ET1 = 6, /* exclusive version of _MM_HINT_T1 */ + _MM_HINT_ET2 = 7 /* exclusive version of _MM_HINT_T2 */ +}; + +// Loads one cache line of data from address p to a location closer to the +// processor. https://msdn.microsoft.com/en-us/library/84szxsww(v=vs.100).aspx +FORCE_INLINE void _mm_prefetch(const void *p, int i) +{ + (void) i; + __builtin_prefetch(p); +} + +// Copy the lower single-precision (32-bit) floating-point element of a to dst. +// +// dst[31:0] := a[31:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtss_f32 +FORCE_INLINE float _mm_cvtss_f32(__m128 a) +{ + return vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); +} + +// Convert the lower single-precision (32-bit) floating-point element in a to a +// 32-bit integer, and store the result in dst. +// +// dst[31:0] := Convert_FP32_To_Int32(a[31:0]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtss_si32 +#define _mm_cvtss_si32(a) _mm_cvt_ss2si(a) + +// Convert the lower single-precision (32-bit) floating-point element in a to a +// 64-bit integer, and store the result in dst. +// +// dst[63:0] := Convert_FP32_To_Int64(a[31:0]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtss_si64 +FORCE_INLINE int _mm_cvtss_si64(__m128 a) +{ +#if defined(__aarch64__) + return vgetq_lane_s64( + vreinterpretq_s64_s32(vcvtnq_s32_f32(vreinterpretq_f32_m128(a))), 0); +#else + float32_t data = vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); + float32_t diff = data - floor(data); + if (diff > 0.5) + return (int64_t) ceil(data); + if (diff == 0.5) { + int64_t f = (int64_t) floor(data); + int64_t c = (int64_t) ceil(data); + return c & 1 ? f : c; + } + return (int64_t) floor(data); +#endif +} + +// Convert packed single-precision (32-bit) floating-point elements in a to +// packed 32-bit integers with truncation, and store the results in dst. +// +// FOR j := 0 to 1 +// i := 32*j +// dst[i+31:i] := Convert_FP32_To_Int32_Truncate(a[i+31:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtt_ps2pi +FORCE_INLINE __m64 _mm_cvtt_ps2pi(__m128 a) +{ + return vreinterpret_m64_s32( + vget_low_s32(vcvtq_s32_f32(vreinterpretq_f32_m128(a)))); +} + +// Convert the lower single-precision (32-bit) floating-point element in a to a +// 32-bit integer with truncation, and store the result in dst. +// +// dst[31:0] := Convert_FP32_To_Int32_Truncate(a[31:0]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtt_ss2si +FORCE_INLINE int _mm_cvtt_ss2si(__m128 a) +{ + return vgetq_lane_s32(vcvtq_s32_f32(vreinterpretq_f32_m128(a)), 0); +} + +// Convert packed single-precision (32-bit) floating-point elements in a to +// packed 32-bit integers with truncation, and store the results in dst. +// +// FOR j := 0 to 1 +// i := 32*j +// dst[i+31:i] := Convert_FP32_To_Int32_Truncate(a[i+31:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvttps_pi32 +#define _mm_cvttps_pi32(a) _mm_cvtt_ps2pi(a) + +// Convert the lower single-precision (32-bit) floating-point element in a to a +// 32-bit integer with truncation, and store the result in dst. +// +// dst[31:0] := Convert_FP32_To_Int32_Truncate(a[31:0]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvttss_si32 +#define _mm_cvttss_si32(a) _mm_cvtt_ss2si(a) + +// Convert the lower single-precision (32-bit) floating-point element in a to a +// 64-bit integer with truncation, and store the result in dst. +// +// dst[63:0] := Convert_FP32_To_Int64_Truncate(a[31:0]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvttss_si64 +FORCE_INLINE int64_t _mm_cvttss_si64(__m128 a) +{ + return vgetq_lane_s64( + vmovl_s32(vget_low_s32(vcvtq_s32_f32(vreinterpretq_f32_m128(a)))), 0); +} + +// Sets the 128-bit value to zero +// https://msdn.microsoft.com/en-us/library/vstudio/ys7dw0kh(v=vs.100).aspx +FORCE_INLINE __m128i _mm_setzero_si128(void) +{ + return vreinterpretq_m128i_s32(vdupq_n_s32(0)); +} + +// Clears the four single-precision, floating-point values. +// https://msdn.microsoft.com/en-us/library/vstudio/tk1t2tbz(v=vs.100).aspx +FORCE_INLINE __m128 _mm_setzero_ps(void) +{ + return vreinterpretq_m128_f32(vdupq_n_f32(0)); +} + +// Return vector of type __m128d with all elements set to zero. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_setzero_pd +FORCE_INLINE __m128d _mm_setzero_pd(void) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64(vdupq_n_f64(0)); +#else + return vreinterpretq_m128d_f32(vdupq_n_f32(0)); +#endif +} + +// Sets the four single-precision, floating-point values to w. +// +// r0 := r1 := r2 := r3 := w +// +// https://msdn.microsoft.com/en-us/library/vstudio/2x1se8ha(v=vs.100).aspx +FORCE_INLINE __m128 _mm_set1_ps(float _w) +{ + return vreinterpretq_m128_f32(vdupq_n_f32(_w)); +} + +// Sets the four single-precision, floating-point values to w. +// https://msdn.microsoft.com/en-us/library/vstudio/2x1se8ha(v=vs.100).aspx +FORCE_INLINE __m128 _mm_set_ps1(float _w) +{ + return vreinterpretq_m128_f32(vdupq_n_f32(_w)); +} + +// Sets the four single-precision, floating-point values to the four inputs. +// https://msdn.microsoft.com/en-us/library/vstudio/afh0zf75(v=vs.100).aspx +FORCE_INLINE __m128 _mm_set_ps(float w, float z, float y, float x) +{ + float ALIGN_STRUCT(16) data[4] = {x, y, z, w}; + return vreinterpretq_m128_f32(vld1q_f32(data)); +} + +// Copy single-precision (32-bit) floating-point element a to the lower element +// of dst, and zero the upper 3 elements. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_set_ss +FORCE_INLINE __m128 _mm_set_ss(float a) +{ + float ALIGN_STRUCT(16) data[4] = {a, 0, 0, 0}; + return vreinterpretq_m128_f32(vld1q_f32(data)); +} + +// Sets the four single-precision, floating-point values to the four inputs in +// reverse order. +// https://msdn.microsoft.com/en-us/library/vstudio/d2172ct3(v=vs.100).aspx +FORCE_INLINE __m128 _mm_setr_ps(float w, float z, float y, float x) +{ + float ALIGN_STRUCT(16) data[4] = {w, z, y, x}; + return vreinterpretq_m128_f32(vld1q_f32(data)); +} + +// Sets the 8 signed 16-bit integer values in reverse order. +// +// Return Value +// r0 := w0 +// r1 := w1 +// ... +// r7 := w7 +FORCE_INLINE __m128i _mm_setr_epi16(short w0, + short w1, + short w2, + short w3, + short w4, + short w5, + short w6, + short w7) +{ + int16_t ALIGN_STRUCT(16) data[8] = {w0, w1, w2, w3, w4, w5, w6, w7}; + return vreinterpretq_m128i_s16(vld1q_s16((int16_t *) data)); +} + +// Sets the 4 signed 32-bit integer values in reverse order +// https://technet.microsoft.com/en-us/library/security/27yb3ee5(v=vs.90).aspx +FORCE_INLINE __m128i _mm_setr_epi32(int i3, int i2, int i1, int i0) +{ + int32_t ALIGN_STRUCT(16) data[4] = {i3, i2, i1, i0}; + return vreinterpretq_m128i_s32(vld1q_s32(data)); +} + +// Set packed 64-bit integers in dst with the supplied values in reverse order. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_setr_epi64 +FORCE_INLINE __m128i _mm_setr_epi64(__m64 e1, __m64 e0) +{ + return vreinterpretq_m128i_s64(vcombine_s64(e1, e0)); +} + +// Sets the 16 signed 8-bit integer values to b. +// +// r0 := b +// r1 := b +// ... +// r15 := b +// +// https://msdn.microsoft.com/en-us/library/6e14xhyf(v=vs.100).aspx +FORCE_INLINE __m128i _mm_set1_epi8(signed char w) +{ + return vreinterpretq_m128i_s8(vdupq_n_s8(w)); +} + +// Sets the 8 signed 16-bit integer values to w. +// +// r0 := w +// r1 := w +// ... +// r7 := w +// +// https://msdn.microsoft.com/en-us/library/k0ya3x0e(v=vs.90).aspx +FORCE_INLINE __m128i _mm_set1_epi16(short w) +{ + return vreinterpretq_m128i_s16(vdupq_n_s16(w)); +} + +// Sets the 16 signed 8-bit integer values. +// https://msdn.microsoft.com/en-us/library/x0cx8zd3(v=vs.90).aspx +FORCE_INLINE __m128i _mm_set_epi8(signed char b15, + signed char b14, + signed char b13, + signed char b12, + signed char b11, + signed char b10, + signed char b9, + signed char b8, + signed char b7, + signed char b6, + signed char b5, + signed char b4, + signed char b3, + signed char b2, + signed char b1, + signed char b0) +{ + int8_t ALIGN_STRUCT(16) + data[16] = {(int8_t) b0, (int8_t) b1, (int8_t) b2, (int8_t) b3, + (int8_t) b4, (int8_t) b5, (int8_t) b6, (int8_t) b7, + (int8_t) b8, (int8_t) b9, (int8_t) b10, (int8_t) b11, + (int8_t) b12, (int8_t) b13, (int8_t) b14, (int8_t) b15}; + return (__m128i) vld1q_s8(data); +} + +// Sets the 8 signed 16-bit integer values. +// https://msdn.microsoft.com/en-au/library/3e0fek84(v=vs.90).aspx +FORCE_INLINE __m128i _mm_set_epi16(short i7, + short i6, + short i5, + short i4, + short i3, + short i2, + short i1, + short i0) +{ + int16_t ALIGN_STRUCT(16) data[8] = {i0, i1, i2, i3, i4, i5, i6, i7}; + return vreinterpretq_m128i_s16(vld1q_s16(data)); +} + +// Sets the 16 signed 8-bit integer values in reverse order. +// https://msdn.microsoft.com/en-us/library/2khb9c7k(v=vs.90).aspx +FORCE_INLINE __m128i _mm_setr_epi8(signed char b0, + signed char b1, + signed char b2, + signed char b3, + signed char b4, + signed char b5, + signed char b6, + signed char b7, + signed char b8, + signed char b9, + signed char b10, + signed char b11, + signed char b12, + signed char b13, + signed char b14, + signed char b15) +{ + int8_t ALIGN_STRUCT(16) + data[16] = {(int8_t) b0, (int8_t) b1, (int8_t) b2, (int8_t) b3, + (int8_t) b4, (int8_t) b5, (int8_t) b6, (int8_t) b7, + (int8_t) b8, (int8_t) b9, (int8_t) b10, (int8_t) b11, + (int8_t) b12, (int8_t) b13, (int8_t) b14, (int8_t) b15}; + return (__m128i) vld1q_s8(data); +} + +// Sets the 4 signed 32-bit integer values to i. +// +// r0 := i +// r1 := i +// r2 := i +// r3 := I +// +// https://msdn.microsoft.com/en-us/library/vstudio/h4xscxat(v=vs.100).aspx +FORCE_INLINE __m128i _mm_set1_epi32(int _i) +{ + return vreinterpretq_m128i_s32(vdupq_n_s32(_i)); +} + +// Sets the 2 signed 64-bit integer values to i. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/whtfzhzk(v=vs.100) +FORCE_INLINE __m128i _mm_set1_epi64(__m64 _i) +{ + return vreinterpretq_m128i_s64(vdupq_n_s64((int64_t) _i)); +} + +// Sets the 2 signed 64-bit integer values to i. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_set1_epi64x +FORCE_INLINE __m128i _mm_set1_epi64x(int64_t _i) +{ + return vreinterpretq_m128i_s64(vdupq_n_s64(_i)); +} + +// Sets the 4 signed 32-bit integer values. +// https://msdn.microsoft.com/en-us/library/vstudio/019beekt(v=vs.100).aspx +FORCE_INLINE __m128i _mm_set_epi32(int i3, int i2, int i1, int i0) +{ + int32_t ALIGN_STRUCT(16) data[4] = {i0, i1, i2, i3}; + return vreinterpretq_m128i_s32(vld1q_s32(data)); +} + +// Returns the __m128i structure with its two 64-bit integer values +// initialized to the values of the two 64-bit integers passed in. +// https://msdn.microsoft.com/en-us/library/dk2sdw0h(v=vs.120).aspx +FORCE_INLINE __m128i _mm_set_epi64x(int64_t i1, int64_t i2) +{ + int64_t ALIGN_STRUCT(16) data[2] = {i2, i1}; + return vreinterpretq_m128i_s64(vld1q_s64(data)); +} + +// Returns the __m128i structure with its two 64-bit integer values +// initialized to the values of the two 64-bit integers passed in. +// https://msdn.microsoft.com/en-us/library/dk2sdw0h(v=vs.120).aspx +FORCE_INLINE __m128i _mm_set_epi64(__m64 i1, __m64 i2) +{ + return _mm_set_epi64x((int64_t) i1, (int64_t) i2); +} + +// Set packed double-precision (64-bit) floating-point elements in dst with the +// supplied values. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_set_pd +FORCE_INLINE __m128d _mm_set_pd(double e1, double e0) +{ + double ALIGN_STRUCT(16) data[2] = {e0, e1}; +#if defined(__aarch64__) + return vreinterpretq_m128d_f64(vld1q_f64((float64_t *) data)); +#else + return vreinterpretq_m128d_f32(vld1q_f32((float32_t *) data)); +#endif +} + +// Stores four single-precision, floating-point values. +// https://msdn.microsoft.com/en-us/library/vstudio/s3h4ay6y(v=vs.100).aspx +FORCE_INLINE void _mm_store_ps(float *p, __m128 a) +{ + vst1q_f32(p, vreinterpretq_f32_m128(a)); +} + +// Stores four single-precision, floating-point values. +// https://msdn.microsoft.com/en-us/library/44e30x22(v=vs.100).aspx +FORCE_INLINE void _mm_storeu_ps(float *p, __m128 a) +{ + vst1q_f32(p, vreinterpretq_f32_m128(a)); +} + +// Stores four 32-bit integer values as (as a __m128i value) at the address p. +// https://msdn.microsoft.com/en-us/library/vstudio/edk11s13(v=vs.100).aspx +FORCE_INLINE void _mm_store_si128(__m128i *p, __m128i a) +{ + vst1q_s32((int32_t *) p, vreinterpretq_s32_m128i(a)); +} + +// Stores four 32-bit integer values as (as a __m128i value) at the address p. +// https://msdn.microsoft.com/en-us/library/vstudio/edk11s13(v=vs.100).aspx +FORCE_INLINE void _mm_storeu_si128(__m128i *p, __m128i a) +{ + vst1q_s32((int32_t *) p, vreinterpretq_s32_m128i(a)); +} + +// Stores the lower single - precision, floating - point value. +// https://msdn.microsoft.com/en-us/library/tzz10fbx(v=vs.100).aspx +FORCE_INLINE void _mm_store_ss(float *p, __m128 a) +{ + vst1q_lane_f32(p, vreinterpretq_f32_m128(a), 0); +} + +// Store 128-bits (composed of 2 packed double-precision (64-bit) floating-point +// elements) from a into memory. mem_addr must be aligned on a 16-byte boundary +// or a general-protection exception may be generated. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_store_pd +FORCE_INLINE void _mm_store_pd(double *mem_addr, __m128d a) +{ +#if defined(__aarch64__) + vst1q_f64((float64_t *) mem_addr, vreinterpretq_f64_m128d(a)); +#else + vst1q_f32((float32_t *) mem_addr, vreinterpretq_f32_m128d(a)); +#endif +} + +// Store 128-bits (composed of 2 packed double-precision (64-bit) floating-point +// elements) from a into memory. mem_addr does not need to be aligned on any +// particular boundary. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_storeu_pd +FORCE_INLINE void _mm_storeu_pd(double *mem_addr, __m128d a) +{ + _mm_store_pd(mem_addr, a); +} + +// Reads the lower 64 bits of b and stores them into the lower 64 bits of a. +// https://msdn.microsoft.com/en-us/library/hhwf428f%28v=vs.90%29.aspx +FORCE_INLINE void _mm_storel_epi64(__m128i *a, __m128i b) +{ + uint64x1_t hi = vget_high_u64(vreinterpretq_u64_m128i(*a)); + uint64x1_t lo = vget_low_u64(vreinterpretq_u64_m128i(b)); + *a = vreinterpretq_m128i_u64(vcombine_u64(lo, hi)); +} + +// Stores the lower two single-precision floating point values of a to the +// address p. +// +// *p0 := a0 +// *p1 := a1 +// +// https://msdn.microsoft.com/en-us/library/h54t98ks(v=vs.90).aspx +FORCE_INLINE void _mm_storel_pi(__m64 *p, __m128 a) +{ + *p = vreinterpret_m64_f32(vget_low_f32(a)); +} + +// Stores the upper two single-precision, floating-point values of a to the +// address p. +// +// *p0 := a2 +// *p1 := a3 +// +// https://msdn.microsoft.com/en-us/library/a7525fs8(v%3dvs.90).aspx +FORCE_INLINE void _mm_storeh_pi(__m64 *p, __m128 a) +{ + *p = vreinterpret_m64_f32(vget_high_f32(a)); +} + +// Loads a single single-precision, floating-point value, copying it into all +// four words +// https://msdn.microsoft.com/en-us/library/vstudio/5cdkf716(v=vs.100).aspx +FORCE_INLINE __m128 _mm_load1_ps(const float *p) +{ + return vreinterpretq_m128_f32(vld1q_dup_f32(p)); +} + +// Load a single-precision (32-bit) floating-point element from memory into all +// elements of dst. +// +// dst[31:0] := MEM[mem_addr+31:mem_addr] +// dst[63:32] := MEM[mem_addr+31:mem_addr] +// dst[95:64] := MEM[mem_addr+31:mem_addr] +// dst[127:96] := MEM[mem_addr+31:mem_addr] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_load_ps1 +#define _mm_load_ps1 _mm_load1_ps + +// Sets the lower two single-precision, floating-point values with 64 +// bits of data loaded from the address p; the upper two values are passed +// through from a. +// +// Return Value +// r0 := *p0 +// r1 := *p1 +// r2 := a2 +// r3 := a3 +// +// https://msdn.microsoft.com/en-us/library/s57cyak2(v=vs.100).aspx +FORCE_INLINE __m128 _mm_loadl_pi(__m128 a, __m64 const *p) +{ + return vreinterpretq_m128_f32( + vcombine_f32(vld1_f32((const float32_t *) p), vget_high_f32(a))); +} + +// Load 4 single-precision (32-bit) floating-point elements from memory into dst +// in reverse order. mem_addr must be aligned on a 16-byte boundary or a +// general-protection exception may be generated. +// +// dst[31:0] := MEM[mem_addr+127:mem_addr+96] +// dst[63:32] := MEM[mem_addr+95:mem_addr+64] +// dst[95:64] := MEM[mem_addr+63:mem_addr+32] +// dst[127:96] := MEM[mem_addr+31:mem_addr] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_loadr_ps +FORCE_INLINE __m128 _mm_loadr_ps(const float *p) +{ + float32x4_t v = vrev64q_f32(vld1q_f32(p)); + return vreinterpretq_m128_f32(vextq_f32(v, v, 2)); +} + +// Sets the upper two single-precision, floating-point values with 64 +// bits of data loaded from the address p; the lower two values are passed +// through from a. +// +// r0 := a0 +// r1 := a1 +// r2 := *p0 +// r3 := *p1 +// +// https://msdn.microsoft.com/en-us/library/w92wta0x(v%3dvs.100).aspx +FORCE_INLINE __m128 _mm_loadh_pi(__m128 a, __m64 const *p) +{ + return vreinterpretq_m128_f32( + vcombine_f32(vget_low_f32(a), vld1_f32((const float32_t *) p))); +} + +// Loads four single-precision, floating-point values. +// https://msdn.microsoft.com/en-us/library/vstudio/zzd50xxt(v=vs.100).aspx +FORCE_INLINE __m128 _mm_load_ps(const float *p) +{ + return vreinterpretq_m128_f32(vld1q_f32(p)); +} + +// Loads four single-precision, floating-point values. +// https://msdn.microsoft.com/en-us/library/x1b16s7z%28v=vs.90%29.aspx +FORCE_INLINE __m128 _mm_loadu_ps(const float *p) +{ + // for neon, alignment doesn't matter, so _mm_load_ps and _mm_loadu_ps are + // equivalent for neon + return vreinterpretq_m128_f32(vld1q_f32(p)); +} + +// Load unaligned 16-bit integer from memory into the first element of dst. +// +// dst[15:0] := MEM[mem_addr+15:mem_addr] +// dst[MAX:16] := 0 +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_loadu_si16 +FORCE_INLINE __m128i _mm_loadu_si16(const void *p) +{ + return vreinterpretq_m128i_s16( + vsetq_lane_s16(*(const int16_t *) p, vdupq_n_s16(0), 0)); +} + +// Load unaligned 64-bit integer from memory into the first element of dst. +// +// dst[63:0] := MEM[mem_addr+63:mem_addr] +// dst[MAX:64] := 0 +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_loadu_si64 +FORCE_INLINE __m128i _mm_loadu_si64(const void *p) +{ + return vreinterpretq_m128i_s64( + vcombine_s64(vld1_s64((const int64_t *) p), vdup_n_s64(0))); +} + +// Load a double-precision (64-bit) floating-point element from memory into the +// lower of dst, and zero the upper element. mem_addr does not need to be +// aligned on any particular boundary. +// +// dst[63:0] := MEM[mem_addr+63:mem_addr] +// dst[127:64] := 0 +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_load_sd +FORCE_INLINE __m128d _mm_load_sd(const double *p) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64(vsetq_lane_f64(*p, vdupq_n_f64(0), 0)); +#else + const float *fp = (const float *) p; + float ALIGN_STRUCT(16) data[4] = {fp[0], fp[1], 0, 0}; + return vreinterpretq_m128d_f32(vld1q_f32(data)); +#endif +} + +// Loads two double-precision from 16-byte aligned memory, floating-point +// values. +// +// dst[127:0] := MEM[mem_addr+127:mem_addr] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_load_pd +FORCE_INLINE __m128d _mm_load_pd(const double *p) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64(vld1q_f64(p)); +#else + const float *fp = (const float *) p; + float ALIGN_STRUCT(16) data[4] = {fp[0], fp[1], fp[2], fp[3]}; + return vreinterpretq_m128d_f32(vld1q_f32(data)); +#endif +} + +// Loads two double-precision from unaligned memory, floating-point values. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_loadu_pd +FORCE_INLINE __m128d _mm_loadu_pd(const double *p) +{ + return _mm_load_pd(p); +} + +// Loads an single - precision, floating - point value into the low word and +// clears the upper three words. +// https://msdn.microsoft.com/en-us/library/548bb9h4%28v=vs.90%29.aspx +FORCE_INLINE __m128 _mm_load_ss(const float *p) +{ + return vreinterpretq_m128_f32(vsetq_lane_f32(*p, vdupq_n_f32(0), 0)); +} + +FORCE_INLINE __m128i _mm_loadl_epi64(__m128i const *p) +{ + /* Load the lower 64 bits of the value pointed to by p into the + * lower 64 bits of the result, zeroing the upper 64 bits of the result. + */ + return vreinterpretq_m128i_s32( + vcombine_s32(vld1_s32((int32_t const *) p), vcreate_s32(0))); +} + +// Load a double-precision (64-bit) floating-point element from memory into the +// lower element of dst, and copy the upper element from a to dst. mem_addr does +// not need to be aligned on any particular boundary. +// +// dst[63:0] := MEM[mem_addr+63:mem_addr] +// dst[127:64] := a[127:64] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_loadl_pd +FORCE_INLINE __m128d _mm_loadl_pd(__m128d a, const double *p) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64( + vcombine_f64(vld1_f64(p), vget_high_f64(vreinterpretq_f64_m128d(a)))); +#else + return vreinterpretq_m128d_f32( + vcombine_f32(vld1_f32((const float *) p), + vget_high_f32(vreinterpretq_f32_m128d(a)))); +#endif +} + +// Load 2 double-precision (64-bit) floating-point elements from memory into dst +// in reverse order. mem_addr must be aligned on a 16-byte boundary or a +// general-protection exception may be generated. +// +// dst[63:0] := MEM[mem_addr+127:mem_addr+64] +// dst[127:64] := MEM[mem_addr+63:mem_addr] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_loadr_pd +FORCE_INLINE __m128d _mm_loadr_pd(const double *p) +{ +#if defined(__aarch64__) + float64x2_t v = vld1q_f64(p); + return vreinterpretq_m128d_f64(vextq_f64(v, v, 1)); +#else + int64x2_t v = vld1q_s64((const int64_t *) p); + return vreinterpretq_m128d_s64(vextq_s64(v, v, 1)); +#endif +} + +// Sets the low word to the single-precision, floating-point value of b +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/35hdzazd(v=vs.100) +FORCE_INLINE __m128 _mm_move_ss(__m128 a, __m128 b) +{ + return vreinterpretq_m128_f32( + vsetq_lane_f32(vgetq_lane_f32(vreinterpretq_f32_m128(b), 0), + vreinterpretq_f32_m128(a), 0)); +} + +// Copy the lower 64-bit integer in a to the lower element of dst, and zero the +// upper element. +// +// dst[63:0] := a[63:0] +// dst[127:64] := 0 +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_move_epi64 +FORCE_INLINE __m128i _mm_move_epi64(__m128i a) +{ + return vreinterpretq_m128i_s64( + vsetq_lane_s64(0, vreinterpretq_s64_m128i(a), 1)); +} + +// Return vector of type __m128 with undefined elements. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_undefined_ps +FORCE_INLINE __m128 _mm_undefined_ps(void) +{ + __m128 a; + return a; +} + +/* Logic/Binary operations */ + +// Computes the bitwise AND-NOT of the four single-precision, floating-point +// values of a and b. +// +// r0 := ~a0 & b0 +// r1 := ~a1 & b1 +// r2 := ~a2 & b2 +// r3 := ~a3 & b3 +// +// https://msdn.microsoft.com/en-us/library/vstudio/68h7wd02(v=vs.100).aspx +FORCE_INLINE __m128 _mm_andnot_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_s32( + vbicq_s32(vreinterpretq_s32_m128(b), + vreinterpretq_s32_m128(a))); // *NOTE* argument swap +} + +// Compute the bitwise NOT of packed double-precision (64-bit) floating-point +// elements in a and then AND with b, and store the results in dst. +// +// FOR j := 0 to 1 +// i := j*64 +// dst[i+63:i] := ((NOT a[i+63:i]) AND b[i+63:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_andnot_pd +FORCE_INLINE __m128d _mm_andnot_pd(__m128d a, __m128d b) +{ + // *NOTE* argument swap + return vreinterpretq_m128d_s64( + vbicq_s64(vreinterpretq_s64_m128d(b), vreinterpretq_s64_m128d(a))); +} + +// Computes the bitwise AND of the 128-bit value in b and the bitwise NOT of the +// 128-bit value in a. +// +// r := (~a) & b +// +// https://msdn.microsoft.com/en-us/library/vstudio/1beaceh8(v=vs.100).aspx +FORCE_INLINE __m128i _mm_andnot_si128(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vbicq_s32(vreinterpretq_s32_m128i(b), + vreinterpretq_s32_m128i(a))); // *NOTE* argument swap +} + +// Computes the bitwise AND of the 128-bit value in a and the 128-bit value in +// b. +// +// r := a & b +// +// https://msdn.microsoft.com/en-us/library/vstudio/6d1txsa8(v=vs.100).aspx +FORCE_INLINE __m128i _mm_and_si128(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vandq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Computes the bitwise AND of the four single-precision, floating-point values +// of a and b. +// +// r0 := a0 & b0 +// r1 := a1 & b1 +// r2 := a2 & b2 +// r3 := a3 & b3 +// +// https://msdn.microsoft.com/en-us/library/vstudio/73ck1xc5(v=vs.100).aspx +FORCE_INLINE __m128 _mm_and_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_s32( + vandq_s32(vreinterpretq_s32_m128(a), vreinterpretq_s32_m128(b))); +} + +// Compute the bitwise AND of packed double-precision (64-bit) floating-point +// elements in a and b, and store the results in dst. +// +// FOR j := 0 to 1 +// i := j*64 +// dst[i+63:i] := a[i+63:i] AND b[i+63:i] +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_and_pd +FORCE_INLINE __m128d _mm_and_pd(__m128d a, __m128d b) +{ + return vreinterpretq_m128d_s64( + vandq_s64(vreinterpretq_s64_m128d(a), vreinterpretq_s64_m128d(b))); +} + +// Computes the bitwise OR of the four single-precision, floating-point values +// of a and b. +// https://msdn.microsoft.com/en-us/library/vstudio/7ctdsyy0(v=vs.100).aspx +FORCE_INLINE __m128 _mm_or_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_s32( + vorrq_s32(vreinterpretq_s32_m128(a), vreinterpretq_s32_m128(b))); +} + +// Computes bitwise EXOR (exclusive-or) of the four single-precision, +// floating-point values of a and b. +// https://msdn.microsoft.com/en-us/library/ss6k3wk8(v=vs.100).aspx +FORCE_INLINE __m128 _mm_xor_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_s32( + veorq_s32(vreinterpretq_s32_m128(a), vreinterpretq_s32_m128(b))); +} + +// Compute the bitwise XOR of packed double-precision (64-bit) floating-point +// elements in a and b, and store the results in dst. +// +// FOR j := 0 to 1 +// i := j*64 +// dst[i+63:i] := a[i+63:i] XOR b[i+63:i] +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_xor_pd +FORCE_INLINE __m128d _mm_xor_pd(__m128d a, __m128d b) +{ + return vreinterpretq_m128d_s64( + veorq_s64(vreinterpretq_s64_m128d(a), vreinterpretq_s64_m128d(b))); +} + +// Computes the bitwise OR of the 128-bit value in a and the 128-bit value in b. +// +// r := a | b +// +// https://msdn.microsoft.com/en-us/library/vstudio/ew8ty0db(v=vs.100).aspx +FORCE_INLINE __m128i _mm_or_si128(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vorrq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Computes the bitwise XOR of the 128-bit value in a and the 128-bit value in +// b. https://msdn.microsoft.com/en-us/library/fzt08www(v=vs.100).aspx +FORCE_INLINE __m128i _mm_xor_si128(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + veorq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Duplicate odd-indexed single-precision (32-bit) floating-point elements +// from a, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_movehdup_ps +FORCE_INLINE __m128 _mm_movehdup_ps(__m128 a) +{ +#if __has_builtin(__builtin_shufflevector) + return vreinterpretq_m128_f32(__builtin_shufflevector( + vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a), 1, 1, 3, 3)); +#else + float32_t a1 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 1); + float32_t a3 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 3); + float ALIGN_STRUCT(16) data[4] = {a1, a1, a3, a3}; + return vreinterpretq_m128_f32(vld1q_f32(data)); +#endif +} + +// Duplicate even-indexed single-precision (32-bit) floating-point elements +// from a, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_moveldup_ps +FORCE_INLINE __m128 _mm_moveldup_ps(__m128 a) +{ +#if __has_builtin(__builtin_shufflevector) + return vreinterpretq_m128_f32(__builtin_shufflevector( + vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a), 0, 0, 2, 2)); +#else + float32_t a0 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); + float32_t a2 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 2); + float ALIGN_STRUCT(16) data[4] = {a0, a0, a2, a2}; + return vreinterpretq_m128_f32(vld1q_f32(data)); +#endif +} + +// Moves the upper two values of B into the lower two values of A. +// +// r3 := a3 +// r2 := a2 +// r1 := b3 +// r0 := b2 +FORCE_INLINE __m128 _mm_movehl_ps(__m128 __A, __m128 __B) +{ + float32x2_t a32 = vget_high_f32(vreinterpretq_f32_m128(__A)); + float32x2_t b32 = vget_high_f32(vreinterpretq_f32_m128(__B)); + return vreinterpretq_m128_f32(vcombine_f32(b32, a32)); +} + +// Moves the lower two values of B into the upper two values of A. +// +// r3 := b1 +// r2 := b0 +// r1 := a1 +// r0 := a0 +FORCE_INLINE __m128 _mm_movelh_ps(__m128 __A, __m128 __B) +{ + float32x2_t a10 = vget_low_f32(vreinterpretq_f32_m128(__A)); + float32x2_t b10 = vget_low_f32(vreinterpretq_f32_m128(__B)); + return vreinterpretq_m128_f32(vcombine_f32(a10, b10)); +} + +// Compute the absolute value of packed signed 32-bit integers in a, and store +// the unsigned results in dst. +// +// FOR j := 0 to 3 +// i := j*32 +// dst[i+31:i] := ABS(a[i+31:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_abs_epi32 +FORCE_INLINE __m128i _mm_abs_epi32(__m128i a) +{ + return vreinterpretq_m128i_s32(vabsq_s32(vreinterpretq_s32_m128i(a))); +} + +// Compute the absolute value of packed signed 16-bit integers in a, and store +// the unsigned results in dst. +// +// FOR j := 0 to 7 +// i := j*16 +// dst[i+15:i] := ABS(a[i+15:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_abs_epi16 +FORCE_INLINE __m128i _mm_abs_epi16(__m128i a) +{ + return vreinterpretq_m128i_s16(vabsq_s16(vreinterpretq_s16_m128i(a))); +} + +// Compute the absolute value of packed signed 8-bit integers in a, and store +// the unsigned results in dst. +// +// FOR j := 0 to 15 +// i := j*8 +// dst[i+7:i] := ABS(a[i+7:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_abs_epi8 +FORCE_INLINE __m128i _mm_abs_epi8(__m128i a) +{ + return vreinterpretq_m128i_s8(vabsq_s8(vreinterpretq_s8_m128i(a))); +} + +// Compute the absolute value of packed signed 32-bit integers in a, and store +// the unsigned results in dst. +// +// FOR j := 0 to 1 +// i := j*32 +// dst[i+31:i] := ABS(a[i+31:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_abs_pi32 +FORCE_INLINE __m64 _mm_abs_pi32(__m64 a) +{ + return vreinterpret_m64_s32(vabs_s32(vreinterpret_s32_m64(a))); +} + +// Compute the absolute value of packed signed 16-bit integers in a, and store +// the unsigned results in dst. +// +// FOR j := 0 to 3 +// i := j*16 +// dst[i+15:i] := ABS(a[i+15:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_abs_pi16 +FORCE_INLINE __m64 _mm_abs_pi16(__m64 a) +{ + return vreinterpret_m64_s16(vabs_s16(vreinterpret_s16_m64(a))); +} + +// Compute the absolute value of packed signed 8-bit integers in a, and store +// the unsigned results in dst. +// +// FOR j := 0 to 7 +// i := j*8 +// dst[i+7:i] := ABS(a[i+7:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_abs_pi8 +FORCE_INLINE __m64 _mm_abs_pi8(__m64 a) +{ + return vreinterpret_m64_s8(vabs_s8(vreinterpret_s8_m64(a))); +} + +// Takes the upper 64 bits of a and places it in the low end of the result +// Takes the lower 64 bits of b and places it into the high end of the result. +FORCE_INLINE __m128 _mm_shuffle_ps_1032(__m128 a, __m128 b) +{ + float32x2_t a32 = vget_high_f32(vreinterpretq_f32_m128(a)); + float32x2_t b10 = vget_low_f32(vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_f32(vcombine_f32(a32, b10)); +} + +// takes the lower two 32-bit values from a and swaps them and places in high +// end of result takes the higher two 32 bit values from b and swaps them and +// places in low end of result. +FORCE_INLINE __m128 _mm_shuffle_ps_2301(__m128 a, __m128 b) +{ + float32x2_t a01 = vrev64_f32(vget_low_f32(vreinterpretq_f32_m128(a))); + float32x2_t b23 = vrev64_f32(vget_high_f32(vreinterpretq_f32_m128(b))); + return vreinterpretq_m128_f32(vcombine_f32(a01, b23)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_0321(__m128 a, __m128 b) +{ + float32x2_t a21 = vget_high_f32( + vextq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a), 3)); + float32x2_t b03 = vget_low_f32( + vextq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b), 3)); + return vreinterpretq_m128_f32(vcombine_f32(a21, b03)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_2103(__m128 a, __m128 b) +{ + float32x2_t a03 = vget_low_f32( + vextq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a), 3)); + float32x2_t b21 = vget_high_f32( + vextq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b), 3)); + return vreinterpretq_m128_f32(vcombine_f32(a03, b21)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_1010(__m128 a, __m128 b) +{ + float32x2_t a10 = vget_low_f32(vreinterpretq_f32_m128(a)); + float32x2_t b10 = vget_low_f32(vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_f32(vcombine_f32(a10, b10)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_1001(__m128 a, __m128 b) +{ + float32x2_t a01 = vrev64_f32(vget_low_f32(vreinterpretq_f32_m128(a))); + float32x2_t b10 = vget_low_f32(vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_f32(vcombine_f32(a01, b10)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_0101(__m128 a, __m128 b) +{ + float32x2_t a01 = vrev64_f32(vget_low_f32(vreinterpretq_f32_m128(a))); + float32x2_t b01 = vrev64_f32(vget_low_f32(vreinterpretq_f32_m128(b))); + return vreinterpretq_m128_f32(vcombine_f32(a01, b01)); +} + +// keeps the low 64 bits of b in the low and puts the high 64 bits of a in the +// high +FORCE_INLINE __m128 _mm_shuffle_ps_3210(__m128 a, __m128 b) +{ + float32x2_t a10 = vget_low_f32(vreinterpretq_f32_m128(a)); + float32x2_t b32 = vget_high_f32(vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_f32(vcombine_f32(a10, b32)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_0011(__m128 a, __m128 b) +{ + float32x2_t a11 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(a)), 1); + float32x2_t b00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 0); + return vreinterpretq_m128_f32(vcombine_f32(a11, b00)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_0022(__m128 a, __m128 b) +{ + float32x2_t a22 = + vdup_lane_f32(vget_high_f32(vreinterpretq_f32_m128(a)), 0); + float32x2_t b00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 0); + return vreinterpretq_m128_f32(vcombine_f32(a22, b00)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_2200(__m128 a, __m128 b) +{ + float32x2_t a00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(a)), 0); + float32x2_t b22 = + vdup_lane_f32(vget_high_f32(vreinterpretq_f32_m128(b)), 0); + return vreinterpretq_m128_f32(vcombine_f32(a00, b22)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_3202(__m128 a, __m128 b) +{ + float32_t a0 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); + float32x2_t a22 = + vdup_lane_f32(vget_high_f32(vreinterpretq_f32_m128(a)), 0); + float32x2_t a02 = vset_lane_f32(a0, a22, 1); /* TODO: use vzip ?*/ + float32x2_t b32 = vget_high_f32(vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_f32(vcombine_f32(a02, b32)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_1133(__m128 a, __m128 b) +{ + float32x2_t a33 = + vdup_lane_f32(vget_high_f32(vreinterpretq_f32_m128(a)), 1); + float32x2_t b11 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 1); + return vreinterpretq_m128_f32(vcombine_f32(a33, b11)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_2010(__m128 a, __m128 b) +{ + float32x2_t a10 = vget_low_f32(vreinterpretq_f32_m128(a)); + float32_t b2 = vgetq_lane_f32(vreinterpretq_f32_m128(b), 2); + float32x2_t b00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 0); + float32x2_t b20 = vset_lane_f32(b2, b00, 1); + return vreinterpretq_m128_f32(vcombine_f32(a10, b20)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_2001(__m128 a, __m128 b) +{ + float32x2_t a01 = vrev64_f32(vget_low_f32(vreinterpretq_f32_m128(a))); + float32_t b2 = vgetq_lane_f32(b, 2); + float32x2_t b00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 0); + float32x2_t b20 = vset_lane_f32(b2, b00, 1); + return vreinterpretq_m128_f32(vcombine_f32(a01, b20)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_2032(__m128 a, __m128 b) +{ + float32x2_t a32 = vget_high_f32(vreinterpretq_f32_m128(a)); + float32_t b2 = vgetq_lane_f32(b, 2); + float32x2_t b00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 0); + float32x2_t b20 = vset_lane_f32(b2, b00, 1); + return vreinterpretq_m128_f32(vcombine_f32(a32, b20)); +} + +// NEON does not support a general purpose permute intrinsic +// Selects four specific single-precision, floating-point values from a and b, +// based on the mask i. +// +// C equivalent: +// __m128 _mm_shuffle_ps_default(__m128 a, __m128 b, +// __constrange(0, 255) int imm) { +// __m128 ret; +// ret[0] = a[imm & 0x3]; ret[1] = a[(imm >> 2) & 0x3]; +// ret[2] = b[(imm >> 4) & 0x03]; ret[3] = b[(imm >> 6) & 0x03]; +// return ret; +// } +// +// https://msdn.microsoft.com/en-us/library/vstudio/5f0858x0(v=vs.100).aspx +#define _mm_shuffle_ps_default(a, b, imm) \ + __extension__({ \ + float32x4_t ret; \ + ret = vmovq_n_f32( \ + vgetq_lane_f32(vreinterpretq_f32_m128(a), (imm) & (0x3))); \ + ret = vsetq_lane_f32( \ + vgetq_lane_f32(vreinterpretq_f32_m128(a), ((imm) >> 2) & 0x3), \ + ret, 1); \ + ret = vsetq_lane_f32( \ + vgetq_lane_f32(vreinterpretq_f32_m128(b), ((imm) >> 4) & 0x3), \ + ret, 2); \ + ret = vsetq_lane_f32( \ + vgetq_lane_f32(vreinterpretq_f32_m128(b), ((imm) >> 6) & 0x3), \ + ret, 3); \ + vreinterpretq_m128_f32(ret); \ + }) + +// FORCE_INLINE __m128 _mm_shuffle_ps(__m128 a, __m128 b, __constrange(0,255) +// int imm) +#if __has_builtin(__builtin_shufflevector) +#define _mm_shuffle_ps(a, b, imm) \ + __extension__({ \ + float32x4_t _input1 = vreinterpretq_f32_m128(a); \ + float32x4_t _input2 = vreinterpretq_f32_m128(b); \ + float32x4_t _shuf = __builtin_shufflevector( \ + _input1, _input2, (imm) & (0x3), ((imm) >> 2) & 0x3, \ + (((imm) >> 4) & 0x3) + 4, (((imm) >> 6) & 0x3) + 4); \ + vreinterpretq_m128_f32(_shuf); \ + }) +#else // generic +#define _mm_shuffle_ps(a, b, imm) \ + __extension__({ \ + __m128 ret; \ + switch (imm) { \ + case _MM_SHUFFLE(1, 0, 3, 2): \ + ret = _mm_shuffle_ps_1032((a), (b)); \ + break; \ + case _MM_SHUFFLE(2, 3, 0, 1): \ + ret = _mm_shuffle_ps_2301((a), (b)); \ + break; \ + case _MM_SHUFFLE(0, 3, 2, 1): \ + ret = _mm_shuffle_ps_0321((a), (b)); \ + break; \ + case _MM_SHUFFLE(2, 1, 0, 3): \ + ret = _mm_shuffle_ps_2103((a), (b)); \ + break; \ + case _MM_SHUFFLE(1, 0, 1, 0): \ + ret = _mm_movelh_ps((a), (b)); \ + break; \ + case _MM_SHUFFLE(1, 0, 0, 1): \ + ret = _mm_shuffle_ps_1001((a), (b)); \ + break; \ + case _MM_SHUFFLE(0, 1, 0, 1): \ + ret = _mm_shuffle_ps_0101((a), (b)); \ + break; \ + case _MM_SHUFFLE(3, 2, 1, 0): \ + ret = _mm_shuffle_ps_3210((a), (b)); \ + break; \ + case _MM_SHUFFLE(0, 0, 1, 1): \ + ret = _mm_shuffle_ps_0011((a), (b)); \ + break; \ + case _MM_SHUFFLE(0, 0, 2, 2): \ + ret = _mm_shuffle_ps_0022((a), (b)); \ + break; \ + case _MM_SHUFFLE(2, 2, 0, 0): \ + ret = _mm_shuffle_ps_2200((a), (b)); \ + break; \ + case _MM_SHUFFLE(3, 2, 0, 2): \ + ret = _mm_shuffle_ps_3202((a), (b)); \ + break; \ + case _MM_SHUFFLE(3, 2, 3, 2): \ + ret = _mm_movehl_ps((b), (a)); \ + break; \ + case _MM_SHUFFLE(1, 1, 3, 3): \ + ret = _mm_shuffle_ps_1133((a), (b)); \ + break; \ + case _MM_SHUFFLE(2, 0, 1, 0): \ + ret = _mm_shuffle_ps_2010((a), (b)); \ + break; \ + case _MM_SHUFFLE(2, 0, 0, 1): \ + ret = _mm_shuffle_ps_2001((a), (b)); \ + break; \ + case _MM_SHUFFLE(2, 0, 3, 2): \ + ret = _mm_shuffle_ps_2032((a), (b)); \ + break; \ + default: \ + ret = _mm_shuffle_ps_default((a), (b), (imm)); \ + break; \ + } \ + ret; \ + }) +#endif + +// Takes the upper 64 bits of a and places it in the low end of the result +// Takes the lower 64 bits of a and places it into the high end of the result. +FORCE_INLINE __m128i _mm_shuffle_epi_1032(__m128i a) +{ + int32x2_t a32 = vget_high_s32(vreinterpretq_s32_m128i(a)); + int32x2_t a10 = vget_low_s32(vreinterpretq_s32_m128i(a)); + return vreinterpretq_m128i_s32(vcombine_s32(a32, a10)); +} + +// takes the lower two 32-bit values from a and swaps them and places in low end +// of result takes the higher two 32 bit values from a and swaps them and places +// in high end of result. +FORCE_INLINE __m128i _mm_shuffle_epi_2301(__m128i a) +{ + int32x2_t a01 = vrev64_s32(vget_low_s32(vreinterpretq_s32_m128i(a))); + int32x2_t a23 = vrev64_s32(vget_high_s32(vreinterpretq_s32_m128i(a))); + return vreinterpretq_m128i_s32(vcombine_s32(a01, a23)); +} + +// rotates the least significant 32 bits into the most signficant 32 bits, and +// shifts the rest down +FORCE_INLINE __m128i _mm_shuffle_epi_0321(__m128i a) +{ + return vreinterpretq_m128i_s32( + vextq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(a), 1)); +} + +// rotates the most significant 32 bits into the least signficant 32 bits, and +// shifts the rest up +FORCE_INLINE __m128i _mm_shuffle_epi_2103(__m128i a) +{ + return vreinterpretq_m128i_s32( + vextq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(a), 3)); +} + +// gets the lower 64 bits of a, and places it in the upper 64 bits +// gets the lower 64 bits of a and places it in the lower 64 bits +FORCE_INLINE __m128i _mm_shuffle_epi_1010(__m128i a) +{ + int32x2_t a10 = vget_low_s32(vreinterpretq_s32_m128i(a)); + return vreinterpretq_m128i_s32(vcombine_s32(a10, a10)); +} + +// gets the lower 64 bits of a, swaps the 0 and 1 elements, and places it in the +// lower 64 bits gets the lower 64 bits of a, and places it in the upper 64 bits +FORCE_INLINE __m128i _mm_shuffle_epi_1001(__m128i a) +{ + int32x2_t a01 = vrev64_s32(vget_low_s32(vreinterpretq_s32_m128i(a))); + int32x2_t a10 = vget_low_s32(vreinterpretq_s32_m128i(a)); + return vreinterpretq_m128i_s32(vcombine_s32(a01, a10)); +} + +// gets the lower 64 bits of a, swaps the 0 and 1 elements and places it in the +// upper 64 bits gets the lower 64 bits of a, swaps the 0 and 1 elements, and +// places it in the lower 64 bits +FORCE_INLINE __m128i _mm_shuffle_epi_0101(__m128i a) +{ + int32x2_t a01 = vrev64_s32(vget_low_s32(vreinterpretq_s32_m128i(a))); + return vreinterpretq_m128i_s32(vcombine_s32(a01, a01)); +} + +FORCE_INLINE __m128i _mm_shuffle_epi_2211(__m128i a) +{ + int32x2_t a11 = vdup_lane_s32(vget_low_s32(vreinterpretq_s32_m128i(a)), 1); + int32x2_t a22 = vdup_lane_s32(vget_high_s32(vreinterpretq_s32_m128i(a)), 0); + return vreinterpretq_m128i_s32(vcombine_s32(a11, a22)); +} + +FORCE_INLINE __m128i _mm_shuffle_epi_0122(__m128i a) +{ + int32x2_t a22 = vdup_lane_s32(vget_high_s32(vreinterpretq_s32_m128i(a)), 0); + int32x2_t a01 = vrev64_s32(vget_low_s32(vreinterpretq_s32_m128i(a))); + return vreinterpretq_m128i_s32(vcombine_s32(a22, a01)); +} + +FORCE_INLINE __m128i _mm_shuffle_epi_3332(__m128i a) +{ + int32x2_t a32 = vget_high_s32(vreinterpretq_s32_m128i(a)); + int32x2_t a33 = vdup_lane_s32(vget_high_s32(vreinterpretq_s32_m128i(a)), 1); + return vreinterpretq_m128i_s32(vcombine_s32(a32, a33)); +} + +// Shuffle packed 8-bit integers in a according to shuffle control mask in the +// corresponding 8-bit element of b, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_shuffle_epi8 +FORCE_INLINE __m128i _mm_shuffle_epi8(__m128i a, __m128i b) +{ + int8x16_t tbl = vreinterpretq_s8_m128i(a); // input a + uint8x16_t idx = vreinterpretq_u8_m128i(b); // input b + uint8x16_t idx_masked = + vandq_u8(idx, vdupq_n_u8(0x8F)); // avoid using meaningless bits +#if defined(__aarch64__) + return vreinterpretq_m128i_s8(vqtbl1q_s8(tbl, idx_masked)); +#elif defined(__GNUC__) + int8x16_t ret; + // %e and %f represent the even and odd D registers + // respectively. + __asm__ __volatile__( + "vtbl.8 %e[ret], {%e[tbl], %f[tbl]}, %e[idx]\n" + "vtbl.8 %f[ret], {%e[tbl], %f[tbl]}, %f[idx]\n" + : [ret] "=&w"(ret) + : [tbl] "w"(tbl), [idx] "w"(idx_masked)); + return vreinterpretq_m128i_s8(ret); +#else + // use this line if testing on aarch64 + int8x8x2_t a_split = {vget_low_s8(tbl), vget_high_s8(tbl)}; + return vreinterpretq_m128i_s8( + vcombine_s8(vtbl2_s8(a_split, vget_low_u8(idx_masked)), + vtbl2_s8(a_split, vget_high_u8(idx_masked)))); +#endif +} + +// C equivalent: +// __m128i _mm_shuffle_epi32_default(__m128i a, +// __constrange(0, 255) int imm) { +// __m128i ret; +// ret[0] = a[imm & 0x3]; ret[1] = a[(imm >> 2) & 0x3]; +// ret[2] = a[(imm >> 4) & 0x03]; ret[3] = a[(imm >> 6) & 0x03]; +// return ret; +// } +#define _mm_shuffle_epi32_default(a, imm) \ + __extension__({ \ + int32x4_t ret; \ + ret = vmovq_n_s32( \ + vgetq_lane_s32(vreinterpretq_s32_m128i(a), (imm) & (0x3))); \ + ret = vsetq_lane_s32( \ + vgetq_lane_s32(vreinterpretq_s32_m128i(a), ((imm) >> 2) & 0x3), \ + ret, 1); \ + ret = vsetq_lane_s32( \ + vgetq_lane_s32(vreinterpretq_s32_m128i(a), ((imm) >> 4) & 0x3), \ + ret, 2); \ + ret = vsetq_lane_s32( \ + vgetq_lane_s32(vreinterpretq_s32_m128i(a), ((imm) >> 6) & 0x3), \ + ret, 3); \ + vreinterpretq_m128i_s32(ret); \ + }) + +// FORCE_INLINE __m128i _mm_shuffle_epi32_splat(__m128i a, __constrange(0,255) +// int imm) +#if defined(__aarch64__) +#define _mm_shuffle_epi32_splat(a, imm) \ + __extension__({ \ + vreinterpretq_m128i_s32( \ + vdupq_laneq_s32(vreinterpretq_s32_m128i(a), (imm))); \ + }) +#else +#define _mm_shuffle_epi32_splat(a, imm) \ + __extension__({ \ + vreinterpretq_m128i_s32( \ + vdupq_n_s32(vgetq_lane_s32(vreinterpretq_s32_m128i(a), (imm)))); \ + }) +#endif + +// Shuffles the 4 signed or unsigned 32-bit integers in a as specified by imm. +// https://msdn.microsoft.com/en-us/library/56f67xbk%28v=vs.90%29.aspx +// FORCE_INLINE __m128i _mm_shuffle_epi32(__m128i a, +// __constrange(0,255) int imm) +#if __has_builtin(__builtin_shufflevector) +#define _mm_shuffle_epi32(a, imm) \ + __extension__({ \ + int32x4_t _input = vreinterpretq_s32_m128i(a); \ + int32x4_t _shuf = __builtin_shufflevector( \ + _input, _input, (imm) & (0x3), ((imm) >> 2) & 0x3, \ + ((imm) >> 4) & 0x3, ((imm) >> 6) & 0x3); \ + vreinterpretq_m128i_s32(_shuf); \ + }) +#else // generic +#define _mm_shuffle_epi32(a, imm) \ + __extension__({ \ + __m128i ret; \ + switch (imm) { \ + case _MM_SHUFFLE(1, 0, 3, 2): \ + ret = _mm_shuffle_epi_1032((a)); \ + break; \ + case _MM_SHUFFLE(2, 3, 0, 1): \ + ret = _mm_shuffle_epi_2301((a)); \ + break; \ + case _MM_SHUFFLE(0, 3, 2, 1): \ + ret = _mm_shuffle_epi_0321((a)); \ + break; \ + case _MM_SHUFFLE(2, 1, 0, 3): \ + ret = _mm_shuffle_epi_2103((a)); \ + break; \ + case _MM_SHUFFLE(1, 0, 1, 0): \ + ret = _mm_shuffle_epi_1010((a)); \ + break; \ + case _MM_SHUFFLE(1, 0, 0, 1): \ + ret = _mm_shuffle_epi_1001((a)); \ + break; \ + case _MM_SHUFFLE(0, 1, 0, 1): \ + ret = _mm_shuffle_epi_0101((a)); \ + break; \ + case _MM_SHUFFLE(2, 2, 1, 1): \ + ret = _mm_shuffle_epi_2211((a)); \ + break; \ + case _MM_SHUFFLE(0, 1, 2, 2): \ + ret = _mm_shuffle_epi_0122((a)); \ + break; \ + case _MM_SHUFFLE(3, 3, 3, 2): \ + ret = _mm_shuffle_epi_3332((a)); \ + break; \ + case _MM_SHUFFLE(0, 0, 0, 0): \ + ret = _mm_shuffle_epi32_splat((a), 0); \ + break; \ + case _MM_SHUFFLE(1, 1, 1, 1): \ + ret = _mm_shuffle_epi32_splat((a), 1); \ + break; \ + case _MM_SHUFFLE(2, 2, 2, 2): \ + ret = _mm_shuffle_epi32_splat((a), 2); \ + break; \ + case _MM_SHUFFLE(3, 3, 3, 3): \ + ret = _mm_shuffle_epi32_splat((a), 3); \ + break; \ + default: \ + ret = _mm_shuffle_epi32_default((a), (imm)); \ + break; \ + } \ + ret; \ + }) +#endif + +// Shuffles the lower 4 signed or unsigned 16-bit integers in a as specified +// by imm. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/y41dkk37(v=vs.100) +// FORCE_INLINE __m128i _mm_shufflelo_epi16_function(__m128i a, +// __constrange(0,255) int +// imm) +#define _mm_shufflelo_epi16_function(a, imm) \ + __extension__({ \ + int16x8_t ret = vreinterpretq_s16_m128i(a); \ + int16x4_t lowBits = vget_low_s16(ret); \ + ret = vsetq_lane_s16(vget_lane_s16(lowBits, (imm) & (0x3)), ret, 0); \ + ret = vsetq_lane_s16(vget_lane_s16(lowBits, ((imm) >> 2) & 0x3), ret, \ + 1); \ + ret = vsetq_lane_s16(vget_lane_s16(lowBits, ((imm) >> 4) & 0x3), ret, \ + 2); \ + ret = vsetq_lane_s16(vget_lane_s16(lowBits, ((imm) >> 6) & 0x3), ret, \ + 3); \ + vreinterpretq_m128i_s16(ret); \ + }) + +// FORCE_INLINE __m128i _mm_shufflelo_epi16(__m128i a, +// __constrange(0,255) int imm) +#if __has_builtin(__builtin_shufflevector) +#define _mm_shufflelo_epi16(a, imm) \ + __extension__({ \ + int16x8_t _input = vreinterpretq_s16_m128i(a); \ + int16x8_t _shuf = __builtin_shufflevector( \ + _input, _input, ((imm) & (0x3)), (((imm) >> 2) & 0x3), \ + (((imm) >> 4) & 0x3), (((imm) >> 6) & 0x3), 4, 5, 6, 7); \ + vreinterpretq_m128i_s16(_shuf); \ + }) +#else // generic +#define _mm_shufflelo_epi16(a, imm) _mm_shufflelo_epi16_function((a), (imm)) +#endif + +// Shuffles the upper 4 signed or unsigned 16-bit integers in a as specified +// by imm. +// https://msdn.microsoft.com/en-us/library/13ywktbs(v=vs.100).aspx +// FORCE_INLINE __m128i _mm_shufflehi_epi16_function(__m128i a, +// __constrange(0,255) int +// imm) +#define _mm_shufflehi_epi16_function(a, imm) \ + __extension__({ \ + int16x8_t ret = vreinterpretq_s16_m128i(a); \ + int16x4_t highBits = vget_high_s16(ret); \ + ret = vsetq_lane_s16(vget_lane_s16(highBits, (imm) & (0x3)), ret, 4); \ + ret = vsetq_lane_s16(vget_lane_s16(highBits, ((imm) >> 2) & 0x3), ret, \ + 5); \ + ret = vsetq_lane_s16(vget_lane_s16(highBits, ((imm) >> 4) & 0x3), ret, \ + 6); \ + ret = vsetq_lane_s16(vget_lane_s16(highBits, ((imm) >> 6) & 0x3), ret, \ + 7); \ + vreinterpretq_m128i_s16(ret); \ + }) + +// FORCE_INLINE __m128i _mm_shufflehi_epi16(__m128i a, +// __constrange(0,255) int imm) +#if __has_builtin(__builtin_shufflevector) +#define _mm_shufflehi_epi16(a, imm) \ + __extension__({ \ + int16x8_t _input = vreinterpretq_s16_m128i(a); \ + int16x8_t _shuf = __builtin_shufflevector( \ + _input, _input, 0, 1, 2, 3, ((imm) & (0x3)) + 4, \ + (((imm) >> 2) & 0x3) + 4, (((imm) >> 4) & 0x3) + 4, \ + (((imm) >> 6) & 0x3) + 4); \ + vreinterpretq_m128i_s16(_shuf); \ + }) +#else // generic +#define _mm_shufflehi_epi16(a, imm) _mm_shufflehi_epi16_function((a), (imm)) +#endif + +// Blend packed 16-bit integers from a and b using control mask imm8, and store +// the results in dst. +// +// FOR j := 0 to 7 +// i := j*16 +// IF imm8[j] +// dst[i+15:i] := b[i+15:i] +// ELSE +// dst[i+15:i] := a[i+15:i] +// FI +// ENDFOR +// FORCE_INLINE __m128i _mm_blend_epi16(__m128i a, __m128i b, +// __constrange(0,255) int imm) +#define _mm_blend_epi16(a, b, imm) \ + __extension__({ \ + const uint16_t _mask[8] = {((imm) & (1 << 0)) ? 0xFFFF : 0x0000, \ + ((imm) & (1 << 1)) ? 0xFFFF : 0x0000, \ + ((imm) & (1 << 2)) ? 0xFFFF : 0x0000, \ + ((imm) & (1 << 3)) ? 0xFFFF : 0x0000, \ + ((imm) & (1 << 4)) ? 0xFFFF : 0x0000, \ + ((imm) & (1 << 5)) ? 0xFFFF : 0x0000, \ + ((imm) & (1 << 6)) ? 0xFFFF : 0x0000, \ + ((imm) & (1 << 7)) ? 0xFFFF : 0x0000}; \ + uint16x8_t _mask_vec = vld1q_u16(_mask); \ + uint16x8_t _a = vreinterpretq_u16_m128i(a); \ + uint16x8_t _b = vreinterpretq_u16_m128i(b); \ + vreinterpretq_m128i_u16(vbslq_u16(_mask_vec, _b, _a)); \ + }) + +// Blend packed 8-bit integers from a and b using mask, and store the results in +// dst. +// +// FOR j := 0 to 15 +// i := j*8 +// IF mask[i+7] +// dst[i+7:i] := b[i+7:i] +// ELSE +// dst[i+7:i] := a[i+7:i] +// FI +// ENDFOR +FORCE_INLINE __m128i _mm_blendv_epi8(__m128i _a, __m128i _b, __m128i _mask) +{ + // Use a signed shift right to create a mask with the sign bit + uint8x16_t mask = + vreinterpretq_u8_s8(vshrq_n_s8(vreinterpretq_s8_m128i(_mask), 7)); + uint8x16_t a = vreinterpretq_u8_m128i(_a); + uint8x16_t b = vreinterpretq_u8_m128i(_b); + return vreinterpretq_m128i_u8(vbslq_u8(mask, b, a)); +} + +/* Shifts */ + + +// Shift packed 16-bit integers in a right by imm while shifting in sign +// bits, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_srai_epi16 +FORCE_INLINE __m128i _mm_srai_epi16(__m128i a, int imm) +{ + const int count = (imm & ~15) ? 15 : imm; + return (__m128i) vshlq_s16((int16x8_t) a, vdupq_n_s16(-count)); +} + +// Shifts the 8 signed or unsigned 16-bit integers in a left by count bits while +// shifting in zeros. +// +// r0 := a0 << count +// r1 := a1 << count +// ... +// r7 := a7 << count +// +// https://msdn.microsoft.com/en-us/library/es73bcsy(v=vs.90).aspx +#define _mm_slli_epi16(a, imm) \ + __extension__({ \ + __m128i ret; \ + if ((imm) <= 0) { \ + ret = a; \ + } else if ((imm) > 15) { \ + ret = _mm_setzero_si128(); \ + } else { \ + ret = vreinterpretq_m128i_s16( \ + vshlq_n_s16(vreinterpretq_s16_m128i(a), (imm))); \ + } \ + ret; \ + }) + +// Shifts the 4 signed or unsigned 32-bit integers in a left by count bits while +// shifting in zeros. : +// https://msdn.microsoft.com/en-us/library/z2k3bbtb%28v=vs.90%29.aspx +// FORCE_INLINE __m128i _mm_slli_epi32(__m128i a, __constrange(0,255) int imm) +FORCE_INLINE __m128i _mm_slli_epi32(__m128i a, int imm) +{ + if (imm <= 0) /* TODO: add constant range macro: [0, 255] */ + return a; + if (imm > 31) /* TODO: add unlikely macro */ + return _mm_setzero_si128(); + return vreinterpretq_m128i_s32( + vshlq_s32(vreinterpretq_s32_m128i(a), vdupq_n_s32(imm))); +} + +// Shift packed 64-bit integers in a left by imm8 while shifting in zeros, and +// store the results in dst. +FORCE_INLINE __m128i _mm_slli_epi64(__m128i a, int imm) +{ + if (imm <= 0) /* TODO: add constant range macro: [0, 255] */ + return a; + if (imm > 63) /* TODO: add unlikely macro */ + return _mm_setzero_si128(); + return vreinterpretq_m128i_s64( + vshlq_s64(vreinterpretq_s64_m128i(a), vdupq_n_s64(imm))); +} + +// Shift packed 16-bit integers in a right by imm8 while shifting in zeros, and +// store the results in dst. +// +// FOR j := 0 to 7 +// i := j*16 +// IF imm8[7:0] > 15 +// dst[i+15:i] := 0 +// ELSE +// dst[i+15:i] := ZeroExtend16(a[i+15:i] >> imm8[7:0]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_srli_epi16 +#define _mm_srli_epi16(a, imm) \ + __extension__({ \ + __m128i ret; \ + if ((imm) == 0) { \ + ret = a; \ + } else if (0 < (imm) && (imm) < 16) { \ + ret = vreinterpretq_m128i_u16( \ + vshlq_u16(vreinterpretq_u16_m128i(a), vdupq_n_s16(-imm))); \ + } else { \ + ret = _mm_setzero_si128(); \ + } \ + ret; \ + }) + +// Shift packed 32-bit integers in a right by imm8 while shifting in zeros, and +// store the results in dst. +// +// FOR j := 0 to 3 +// i := j*32 +// IF imm8[7:0] > 31 +// dst[i+31:i] := 0 +// ELSE +// dst[i+31:i] := ZeroExtend32(a[i+31:i] >> imm8[7:0]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_srli_epi32 +// FORCE_INLINE __m128i _mm_srli_epi32(__m128i a, __constrange(0,255) int imm) +#define _mm_srli_epi32(a, imm) \ + __extension__({ \ + __m128i ret; \ + if ((imm) == 0) { \ + ret = a; \ + } else if (0 < (imm) && (imm) < 32) { \ + ret = vreinterpretq_m128i_u32( \ + vshlq_u32(vreinterpretq_u32_m128i(a), vdupq_n_s32(-imm))); \ + } else { \ + ret = _mm_setzero_si128(); \ + } \ + ret; \ + }) + +// Shift packed 64-bit integers in a right by imm8 while shifting in zeros, and +// store the results in dst. +// +// FOR j := 0 to 1 +// i := j*64 +// IF imm8[7:0] > 63 +// dst[i+63:i] := 0 +// ELSE +// dst[i+63:i] := ZeroExtend64(a[i+63:i] >> imm8[7:0]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_srli_epi64 +#define _mm_srli_epi64(a, imm) \ + __extension__({ \ + __m128i ret; \ + if ((imm) == 0) { \ + ret = a; \ + } else if (0 < (imm) && (imm) < 64) { \ + ret = vreinterpretq_m128i_u64( \ + vshlq_u64(vreinterpretq_u64_m128i(a), vdupq_n_s64(-imm))); \ + } else { \ + ret = _mm_setzero_si128(); \ + } \ + ret; \ + }) + +// Shift packed 32-bit integers in a right by imm8 while shifting in sign bits, +// and store the results in dst. +// +// FOR j := 0 to 3 +// i := j*32 +// IF imm8[7:0] > 31 +// dst[i+31:i] := (a[i+31] ? 0xFFFFFFFF : 0x0) +// ELSE +// dst[i+31:i] := SignExtend32(a[i+31:i] >> imm8[7:0]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_srai_epi32 +// FORCE_INLINE __m128i _mm_srai_epi32(__m128i a, __constrange(0,255) int imm) +#define _mm_srai_epi32(a, imm) \ + __extension__({ \ + __m128i ret; \ + if ((imm) == 0) { \ + ret = a; \ + } else if (0 < (imm) && (imm) < 32) { \ + ret = vreinterpretq_m128i_s32( \ + vshlq_s32(vreinterpretq_s32_m128i(a), vdupq_n_s32(-imm))); \ + } else { \ + ret = vreinterpretq_m128i_s32( \ + vshrq_n_s32(vreinterpretq_s32_m128i(a), 31)); \ + } \ + ret; \ + }) + +// Shifts the 128 - bit value in a right by imm bytes while shifting in +// zeros.imm must be an immediate. +// +// r := srl(a, imm*8) +// +// https://msdn.microsoft.com/en-us/library/305w28yz(v=vs.100).aspx +// FORCE_INLINE _mm_srli_si128(__m128i a, __constrange(0,255) int imm) +#define _mm_srli_si128(a, imm) \ + __extension__({ \ + __m128i ret; \ + if ((imm) <= 0) { \ + ret = a; \ + } else if ((imm) > 15) { \ + ret = _mm_setzero_si128(); \ + } else { \ + ret = vreinterpretq_m128i_s8( \ + vextq_s8(vreinterpretq_s8_m128i(a), vdupq_n_s8(0), (imm))); \ + } \ + ret; \ + }) + +// Shifts the 128-bit value in a left by imm bytes while shifting in zeros. imm +// must be an immediate. +// +// r := a << (imm * 8) +// +// https://msdn.microsoft.com/en-us/library/34d3k2kt(v=vs.100).aspx +// FORCE_INLINE __m128i _mm_slli_si128(__m128i a, __constrange(0,255) int imm) +#define _mm_slli_si128(a, imm) \ + __extension__({ \ + __m128i ret; \ + if ((imm) <= 0) { \ + ret = a; \ + } else if ((imm) > 15) { \ + ret = _mm_setzero_si128(); \ + } else { \ + ret = vreinterpretq_m128i_s8(vextq_s8( \ + vdupq_n_s8(0), vreinterpretq_s8_m128i(a), 16 - (imm))); \ + } \ + ret; \ + }) + +// Shifts the 8 signed or unsigned 16-bit integers in a left by count bits while +// shifting in zeros. +// +// r0 := a0 << count +// r1 := a1 << count +// ... +// r7 := a7 << count +// +// https://msdn.microsoft.com/en-us/library/c79w388h(v%3dvs.90).aspx +FORCE_INLINE __m128i _mm_sll_epi16(__m128i a, __m128i count) +{ + uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); + if (c > 15) + return _mm_setzero_si128(); + + int16x8_t vc = vdupq_n_s16((int16_t) c); + return vreinterpretq_m128i_s16(vshlq_s16(vreinterpretq_s16_m128i(a), vc)); +} + +// Shifts the 4 signed or unsigned 32-bit integers in a left by count bits while +// shifting in zeros. +// +// r0 := a0 << count +// r1 := a1 << count +// r2 := a2 << count +// r3 := a3 << count +// +// https://msdn.microsoft.com/en-us/library/6fe5a6s9(v%3dvs.90).aspx +FORCE_INLINE __m128i _mm_sll_epi32(__m128i a, __m128i count) +{ + uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); + if (c > 31) + return _mm_setzero_si128(); + + int32x4_t vc = vdupq_n_s32((int32_t) c); + return vreinterpretq_m128i_s32(vshlq_s32(vreinterpretq_s32_m128i(a), vc)); +} + +// Shifts the 2 signed or unsigned 64-bit integers in a left by count bits while +// shifting in zeros. +// +// r0 := a0 << count +// r1 := a1 << count +// +// https://msdn.microsoft.com/en-us/library/6ta9dffd(v%3dvs.90).aspx +FORCE_INLINE __m128i _mm_sll_epi64(__m128i a, __m128i count) +{ + uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); + if (c > 63) + return _mm_setzero_si128(); + + int64x2_t vc = vdupq_n_s64((int64_t) c); + return vreinterpretq_m128i_s64(vshlq_s64(vreinterpretq_s64_m128i(a), vc)); +} + +// Shifts the 8 signed or unsigned 16-bit integers in a right by count bits +// while shifting in zeros. +// +// r0 := srl(a0, count) +// r1 := srl(a1, count) +// ... +// r7 := srl(a7, count) +// +// https://msdn.microsoft.com/en-us/library/wd5ax830(v%3dvs.90).aspx +FORCE_INLINE __m128i _mm_srl_epi16(__m128i a, __m128i count) +{ + uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); + if (c > 15) + return _mm_setzero_si128(); + + int16x8_t vc = vdupq_n_s16(-(int16_t) c); + return vreinterpretq_m128i_u16(vshlq_u16(vreinterpretq_u16_m128i(a), vc)); +} + +// Shifts the 4 signed or unsigned 32-bit integers in a right by count bits +// while shifting in zeros. +// +// r0 := srl(a0, count) +// r1 := srl(a1, count) +// r2 := srl(a2, count) +// r3 := srl(a3, count) +// +// https://msdn.microsoft.com/en-us/library/a9cbttf4(v%3dvs.90).aspx +FORCE_INLINE __m128i _mm_srl_epi32(__m128i a, __m128i count) +{ + uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); + if (c > 31) + return _mm_setzero_si128(); + + int32x4_t vc = vdupq_n_s32(-(int32_t) c); + return vreinterpretq_m128i_u32(vshlq_u32(vreinterpretq_u32_m128i(a), vc)); +} + +// Shifts the 2 signed or unsigned 64-bit integers in a right by count bits +// while shifting in zeros. +// +// r0 := srl(a0, count) +// r1 := srl(a1, count) +// +// https://msdn.microsoft.com/en-us/library/yf6cf9k8(v%3dvs.90).aspx +FORCE_INLINE __m128i _mm_srl_epi64(__m128i a, __m128i count) +{ + uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); + if (c > 63) + return _mm_setzero_si128(); + + int64x2_t vc = vdupq_n_s64(-(int64_t) c); + return vreinterpretq_m128i_u64(vshlq_u64(vreinterpretq_u64_m128i(a), vc)); +} + +// NEON does not provide a version of this function. +// Creates a 16-bit mask from the most significant bits of the 16 signed or +// unsigned 8-bit integers in a and zero extends the upper bits. +// https://msdn.microsoft.com/en-us/library/vstudio/s090c8fk(v=vs.100).aspx +FORCE_INLINE int _mm_movemask_epi8(__m128i a) +{ +#if defined(__aarch64__) + uint8x16_t input = vreinterpretq_u8_m128i(a); + const int8_t ALIGN_STRUCT(16) + xr[16] = {-7, -6, -5, -4, -3, -2, -1, 0, -7, -6, -5, -4, -3, -2, -1, 0}; + const uint8x16_t mask_and = vdupq_n_u8(0x80); + const int8x16_t mask_shift = vld1q_s8(xr); + const uint8x16_t mask_result = + vshlq_u8(vandq_u8(input, mask_and), mask_shift); + uint8x8_t lo = vget_low_u8(mask_result); + uint8x8_t hi = vget_high_u8(mask_result); + + return vaddv_u8(lo) + (vaddv_u8(hi) << 8); +#else + // Use increasingly wide shifts+adds to collect the sign bits + // together. + // Since the widening shifts would be rather confusing to follow in little + // endian, everything will be illustrated in big endian order instead. This + // has a different result - the bits would actually be reversed on a big + // endian machine. + + // Starting input (only half the elements are shown): + // 89 ff 1d c0 00 10 99 33 + uint8x16_t input = vreinterpretq_u8_m128i(a); + + // Shift out everything but the sign bits with an unsigned shift right. + // + // Bytes of the vector:: + // 89 ff 1d c0 00 10 99 33 + // \ \ \ \ \ \ \ \ high_bits = (uint16x4_t)(input >> 7) + // | | | | | | | | + // 01 01 00 01 00 00 01 00 + // + // Bits of first important lane(s): + // 10001001 (89) + // \______ + // | + // 00000001 (01) + uint16x8_t high_bits = vreinterpretq_u16_u8(vshrq_n_u8(input, 7)); + + // Merge the even lanes together with a 16-bit unsigned shift right + add. + // 'xx' represents garbage data which will be ignored in the final result. + // In the important bytes, the add functions like a binary OR. + // + // 01 01 00 01 00 00 01 00 + // \_ | \_ | \_ | \_ | paired16 = (uint32x4_t)(input + (input >> 7)) + // \| \| \| \| + // xx 03 xx 01 xx 00 xx 02 + // + // 00000001 00000001 (01 01) + // \_______ | + // \| + // xxxxxxxx xxxxxx11 (xx 03) + uint32x4_t paired16 = + vreinterpretq_u32_u16(vsraq_n_u16(high_bits, high_bits, 7)); + + // Repeat with a wider 32-bit shift + add. + // xx 03 xx 01 xx 00 xx 02 + // \____ | \____ | paired32 = (uint64x1_t)(paired16 + (paired16 >> + // 14)) + // \| \| + // xx xx xx 0d xx xx xx 02 + // + // 00000011 00000001 (03 01) + // \\_____ || + // '----.\|| + // xxxxxxxx xxxx1101 (xx 0d) + uint64x2_t paired32 = + vreinterpretq_u64_u32(vsraq_n_u32(paired16, paired16, 14)); + + // Last, an even wider 64-bit shift + add to get our result in the low 8 bit + // lanes. xx xx xx 0d xx xx xx 02 + // \_________ | paired64 = (uint8x8_t)(paired32 + (paired32 >> + // 28)) + // \| + // xx xx xx xx xx xx xx d2 + // + // 00001101 00000010 (0d 02) + // \ \___ | | + // '---. \| | + // xxxxxxxx 11010010 (xx d2) + uint8x16_t paired64 = + vreinterpretq_u8_u64(vsraq_n_u64(paired32, paired32, 28)); + + // Extract the low 8 bits from each 64-bit lane with 2 8-bit extracts. + // xx xx xx xx xx xx xx d2 + // || return paired64[0] + // d2 + // Note: Little endian would return the correct value 4b (01001011) instead. + return vgetq_lane_u8(paired64, 0) | ((int) vgetq_lane_u8(paired64, 8) << 8); +#endif +} + +// Copy the lower 64-bit integer in a to dst. +// +// dst[63:0] := a[63:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_movepi64_pi64 +FORCE_INLINE __m64 _mm_movepi64_pi64(__m128i a) +{ + return vreinterpret_m64_s64(vget_low_s64(vreinterpretq_s64_m128i(a))); +} + +// Copy the 64-bit integer a to the lower element of dst, and zero the upper +// element. +// +// dst[63:0] := a[63:0] +// dst[127:64] := 0 +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_movpi64_epi64 +FORCE_INLINE __m128i _mm_movpi64_epi64(__m64 a) +{ + return vreinterpretq_m128i_s64( + vcombine_s64(vreinterpret_s64_m64(a), vdup_n_s64(0))); +} + +// NEON does not provide this method +// Creates a 4-bit mask from the most significant bits of the four +// single-precision, floating-point values. +// https://msdn.microsoft.com/en-us/library/vstudio/4490ys29(v=vs.100).aspx +FORCE_INLINE int _mm_movemask_ps(__m128 a) +{ + uint32x4_t input = vreinterpretq_u32_m128(a); +#if defined(__aarch64__) + static const int32x4_t shift = {0, 1, 2, 3}; + uint32x4_t tmp = vshrq_n_u32(input, 31); + return vaddvq_u32(vshlq_u32(tmp, shift)); +#else + // Uses the exact same method as _mm_movemask_epi8, see that for details. + // Shift out everything but the sign bits with a 32-bit unsigned shift + // right. + uint64x2_t high_bits = vreinterpretq_u64_u32(vshrq_n_u32(input, 31)); + // Merge the two pairs together with a 64-bit unsigned shift right + add. + uint8x16_t paired = + vreinterpretq_u8_u64(vsraq_n_u64(high_bits, high_bits, 31)); + // Extract the result. + return vgetq_lane_u8(paired, 0) | (vgetq_lane_u8(paired, 8) << 2); +#endif +} + +// Compute the bitwise NOT of a and then AND with a 128-bit vector containing +// all 1's, and return 1 if the result is zero, otherwise return 0. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_test_all_ones +FORCE_INLINE int _mm_test_all_ones(__m128i a) +{ + return (uint64_t)(vgetq_lane_s64(a, 0) & vgetq_lane_s64(a, 1)) == + ~(uint64_t) 0; +} + +// Compute the bitwise AND of 128 bits (representing integer data) in a and +// mask, and return 1 if the result is zero, otherwise return 0. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_test_all_zeros +FORCE_INLINE int _mm_test_all_zeros(__m128i a, __m128i mask) +{ + int64x2_t a_and_mask = + vandq_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(mask)); + return (vgetq_lane_s64(a_and_mask, 0) | vgetq_lane_s64(a_and_mask, 1)) ? 0 + : 1; +} + +/* Math operations */ + +// Subtracts the four single-precision, floating-point values of a and b. +// +// r0 := a0 - b0 +// r1 := a1 - b1 +// r2 := a2 - b2 +// r3 := a3 - b3 +// +// https://msdn.microsoft.com/en-us/library/vstudio/1zad2k61(v=vs.100).aspx +FORCE_INLINE __m128 _mm_sub_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_f32( + vsubq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Subtract the lower single-precision (32-bit) floating-point element in b from +// the lower single-precision (32-bit) floating-point element in a, store the +// result in the lower element of dst, and copy the upper 3 packed elements from +// a to the upper elements of dst. +// +// dst[31:0] := a[31:0] - b[31:0] +// dst[127:32] := a[127:32] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sub_ss +FORCE_INLINE __m128 _mm_sub_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_sub_ps(a, b)); +} + +// Subtract 2 packed 64-bit integers in b from 2 packed 64-bit integers in a, +// and store the results in dst. +// r0 := a0 - b0 +// r1 := a1 - b1 +FORCE_INLINE __m128i _mm_sub_epi64(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s64( + vsubq_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(b))); +} + +// Subtracts the 4 signed or unsigned 32-bit integers of b from the 4 signed or +// unsigned 32-bit integers of a. +// +// r0 := a0 - b0 +// r1 := a1 - b1 +// r2 := a2 - b2 +// r3 := a3 - b3 +// +// https://msdn.microsoft.com/en-us/library/vstudio/fhh866h0(v=vs.100).aspx +FORCE_INLINE __m128i _mm_sub_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vsubq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +FORCE_INLINE __m128i _mm_sub_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vsubq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +FORCE_INLINE __m128i _mm_sub_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s8( + vsubq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Subtract 64-bit integer b from 64-bit integer a, and store the result in dst. +// +// dst[63:0] := a[63:0] - b[63:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sub_si64 +FORCE_INLINE __m64 _mm_sub_si64(__m64 a, __m64 b) +{ + return vreinterpret_m64_s64( + vsub_s64(vreinterpret_s64_m64(a), vreinterpret_s64_m64(b))); +} + +// Subtracts the 8 unsigned 16-bit integers of bfrom the 8 unsigned 16-bit +// integers of a and saturates.. +// https://technet.microsoft.com/en-us/subscriptions/index/f44y0s19(v=vs.90).aspx +FORCE_INLINE __m128i _mm_subs_epu16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vqsubq_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b))); +} + +// Subtracts the 16 unsigned 8-bit integers of b from the 16 unsigned 8-bit +// integers of a and saturates. +// +// r0 := UnsignedSaturate(a0 - b0) +// r1 := UnsignedSaturate(a1 - b1) +// ... +// r15 := UnsignedSaturate(a15 - b15) +// +// https://technet.microsoft.com/en-us/subscriptions/yadkxc18(v=vs.90) +FORCE_INLINE __m128i _mm_subs_epu8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vqsubq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b))); +} + +// Subtracts the 16 signed 8-bit integers of b from the 16 signed 8-bit integers +// of a and saturates. +// +// r0 := SignedSaturate(a0 - b0) +// r1 := SignedSaturate(a1 - b1) +// ... +// r15 := SignedSaturate(a15 - b15) +// +// https://technet.microsoft.com/en-us/subscriptions/by7kzks1(v=vs.90) +FORCE_INLINE __m128i _mm_subs_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s8( + vqsubq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Subtracts the 8 signed 16-bit integers of b from the 8 signed 16-bit integers +// of a and saturates. +// +// r0 := SignedSaturate(a0 - b0) +// r1 := SignedSaturate(a1 - b1) +// ... +// r7 := SignedSaturate(a7 - b7) +// +// https://technet.microsoft.com/en-us/subscriptions/3247z5b8(v=vs.90) +FORCE_INLINE __m128i _mm_subs_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vqsubq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +FORCE_INLINE __m128i _mm_adds_epu16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vqaddq_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b))); +} + +// Negate packed 8-bit integers in a when the corresponding signed +// 8-bit integer in b is negative, and store the results in dst. +// Element in dst are zeroed out when the corresponding element +// in b is zero. +// +// for i in 0..15 +// if b[i] < 0 +// r[i] := -a[i] +// else if b[i] == 0 +// r[i] := 0 +// else +// r[i] := a[i] +// fi +// done +FORCE_INLINE __m128i _mm_sign_epi8(__m128i _a, __m128i _b) +{ + int8x16_t a = vreinterpretq_s8_m128i(_a); + int8x16_t b = vreinterpretq_s8_m128i(_b); + + // signed shift right: faster than vclt + // (b < 0) ? 0xFF : 0 + uint8x16_t ltMask = vreinterpretq_u8_s8(vshrq_n_s8(b, 7)); + + // (b == 0) ? 0xFF : 0 +#if defined(__aarch64__) + int8x16_t zeroMask = vreinterpretq_s8_u8(vceqzq_s8(b)); +#else + int8x16_t zeroMask = vreinterpretq_s8_u8(vceqq_s8(b, vdupq_n_s8(0))); +#endif + + // bitwise select either a or nagative 'a' (vnegq_s8(a) return nagative 'a') + // based on ltMask + int8x16_t masked = vbslq_s8(ltMask, vnegq_s8(a), a); + // res = masked & (~zeroMask) + int8x16_t res = vbicq_s8(masked, zeroMask); + + return vreinterpretq_m128i_s8(res); +} + +// Negate packed 16-bit integers in a when the corresponding signed +// 16-bit integer in b is negative, and store the results in dst. +// Element in dst are zeroed out when the corresponding element +// in b is zero. +// +// for i in 0..7 +// if b[i] < 0 +// r[i] := -a[i] +// else if b[i] == 0 +// r[i] := 0 +// else +// r[i] := a[i] +// fi +// done +FORCE_INLINE __m128i _mm_sign_epi16(__m128i _a, __m128i _b) +{ + int16x8_t a = vreinterpretq_s16_m128i(_a); + int16x8_t b = vreinterpretq_s16_m128i(_b); + + // signed shift right: faster than vclt + // (b < 0) ? 0xFFFF : 0 + uint16x8_t ltMask = vreinterpretq_u16_s16(vshrq_n_s16(b, 15)); + // (b == 0) ? 0xFFFF : 0 +#if defined(__aarch64__) + int16x8_t zeroMask = vreinterpretq_s16_u16(vceqzq_s16(b)); +#else + int16x8_t zeroMask = vreinterpretq_s16_u16(vceqq_s16(b, vdupq_n_s16(0))); +#endif + + // bitwise select either a or negative 'a' (vnegq_s16(a) equals to negative + // 'a') based on ltMask + int16x8_t masked = vbslq_s16(ltMask, vnegq_s16(a), a); + // res = masked & (~zeroMask) + int16x8_t res = vbicq_s16(masked, zeroMask); + return vreinterpretq_m128i_s16(res); +} + +// Negate packed 32-bit integers in a when the corresponding signed +// 32-bit integer in b is negative, and store the results in dst. +// Element in dst are zeroed out when the corresponding element +// in b is zero. +// +// for i in 0..3 +// if b[i] < 0 +// r[i] := -a[i] +// else if b[i] == 0 +// r[i] := 0 +// else +// r[i] := a[i] +// fi +// done +FORCE_INLINE __m128i _mm_sign_epi32(__m128i _a, __m128i _b) +{ + int32x4_t a = vreinterpretq_s32_m128i(_a); + int32x4_t b = vreinterpretq_s32_m128i(_b); + + // signed shift right: faster than vclt + // (b < 0) ? 0xFFFFFFFF : 0 + uint32x4_t ltMask = vreinterpretq_u32_s32(vshrq_n_s32(b, 31)); + + // (b == 0) ? 0xFFFFFFFF : 0 +#if defined(__aarch64__) + int32x4_t zeroMask = vreinterpretq_s32_u32(vceqzq_s32(b)); +#else + int32x4_t zeroMask = vreinterpretq_s32_u32(vceqq_s32(b, vdupq_n_s32(0))); +#endif + + // bitwise select either a or negative 'a' (vnegq_s32(a) equals to negative + // 'a') based on ltMask + int32x4_t masked = vbslq_s32(ltMask, vnegq_s32(a), a); + // res = masked & (~zeroMask) + int32x4_t res = vbicq_s32(masked, zeroMask); + return vreinterpretq_m128i_s32(res); +} + +// Negate packed 16-bit integers in a when the corresponding signed 16-bit +// integer in b is negative, and store the results in dst. Element in dst are +// zeroed out when the corresponding element in b is zero. +// +// FOR j := 0 to 3 +// i := j*16 +// IF b[i+15:i] < 0 +// dst[i+15:i] := -(a[i+15:i]) +// ELSE IF b[i+15:i] == 0 +// dst[i+15:i] := 0 +// ELSE +// dst[i+15:i] := a[i+15:i] +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sign_pi16 +FORCE_INLINE __m64 _mm_sign_pi16(__m64 _a, __m64 _b) +{ + int16x4_t a = vreinterpret_s16_m64(_a); + int16x4_t b = vreinterpret_s16_m64(_b); + + // signed shift right: faster than vclt + // (b < 0) ? 0xFFFF : 0 + uint16x4_t ltMask = vreinterpret_u16_s16(vshr_n_s16(b, 15)); + + // (b == 0) ? 0xFFFF : 0 +#if defined(__aarch64__) + int16x4_t zeroMask = vreinterpret_s16_u16(vceqz_s16(b)); +#else + int16x4_t zeroMask = vreinterpret_s16_u16(vceq_s16(b, vdup_n_s16(0))); +#endif + + // bitwise select either a or nagative 'a' (vneg_s16(a) return nagative 'a') + // based on ltMask + int16x4_t masked = vbsl_s16(ltMask, vneg_s16(a), a); + // res = masked & (~zeroMask) + int16x4_t res = vbic_s16(masked, zeroMask); + + return vreinterpret_m64_s16(res); +} + +// Negate packed 32-bit integers in a when the corresponding signed 32-bit +// integer in b is negative, and store the results in dst. Element in dst are +// zeroed out when the corresponding element in b is zero. +// +// FOR j := 0 to 1 +// i := j*32 +// IF b[i+31:i] < 0 +// dst[i+31:i] := -(a[i+31:i]) +// ELSE IF b[i+31:i] == 0 +// dst[i+31:i] := 0 +// ELSE +// dst[i+31:i] := a[i+31:i] +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sign_pi32 +FORCE_INLINE __m64 _mm_sign_pi32(__m64 _a, __m64 _b) +{ + int32x2_t a = vreinterpret_s32_m64(_a); + int32x2_t b = vreinterpret_s32_m64(_b); + + // signed shift right: faster than vclt + // (b < 0) ? 0xFFFFFFFF : 0 + uint32x2_t ltMask = vreinterpret_u32_s32(vshr_n_s32(b, 31)); + + // (b == 0) ? 0xFFFFFFFF : 0 +#if defined(__aarch64__) + int32x2_t zeroMask = vreinterpret_s32_u32(vceqz_s32(b)); +#else + int32x2_t zeroMask = vreinterpret_s32_u32(vceq_s32(b, vdup_n_s32(0))); +#endif + + // bitwise select either a or nagative 'a' (vneg_s32(a) return nagative 'a') + // based on ltMask + int32x2_t masked = vbsl_s32(ltMask, vneg_s32(a), a); + // res = masked & (~zeroMask) + int32x2_t res = vbic_s32(masked, zeroMask); + + return vreinterpret_m64_s32(res); +} + +// Negate packed 8-bit integers in a when the corresponding signed 8-bit integer +// in b is negative, and store the results in dst. Element in dst are zeroed out +// when the corresponding element in b is zero. +// +// FOR j := 0 to 7 +// i := j*8 +// IF b[i+7:i] < 0 +// dst[i+7:i] := -(a[i+7:i]) +// ELSE IF b[i+7:i] == 0 +// dst[i+7:i] := 0 +// ELSE +// dst[i+7:i] := a[i+7:i] +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sign_pi8 +FORCE_INLINE __m64 _mm_sign_pi8(__m64 _a, __m64 _b) +{ + int8x8_t a = vreinterpret_s8_m64(_a); + int8x8_t b = vreinterpret_s8_m64(_b); + + // signed shift right: faster than vclt + // (b < 0) ? 0xFF : 0 + uint8x8_t ltMask = vreinterpret_u8_s8(vshr_n_s8(b, 7)); + + // (b == 0) ? 0xFF : 0 +#if defined(__aarch64__) + int8x8_t zeroMask = vreinterpret_s8_u8(vceqz_s8(b)); +#else + int8x8_t zeroMask = vreinterpret_s8_u8(vceq_s8(b, vdup_n_s8(0))); +#endif + + // bitwise select either a or nagative 'a' (vneg_s8(a) return nagative 'a') + // based on ltMask + int8x8_t masked = vbsl_s8(ltMask, vneg_s8(a), a); + // res = masked & (~zeroMask) + int8x8_t res = vbic_s8(masked, zeroMask); + + return vreinterpret_m64_s8(res); +} + +// Average packed unsigned 16-bit integers in a and b, and store the results in +// dst. +// +// FOR j := 0 to 3 +// i := j*16 +// dst[i+15:i] := (a[i+15:i] + b[i+15:i] + 1) >> 1 +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_avg_pu16 +FORCE_INLINE __m64 _mm_avg_pu16(__m64 a, __m64 b) +{ + return vreinterpret_m64_u16( + vrhadd_u16(vreinterpret_u16_m64(a), vreinterpret_u16_m64(b))); +} + +// Average packed unsigned 8-bit integers in a and b, and store the results in +// dst. +// +// FOR j := 0 to 7 +// i := j*8 +// dst[i+7:i] := (a[i+7:i] + b[i+7:i] + 1) >> 1 +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_avg_pu8 +FORCE_INLINE __m64 _mm_avg_pu8(__m64 a, __m64 b) +{ + return vreinterpret_m64_u8( + vrhadd_u8(vreinterpret_u8_m64(a), vreinterpret_u8_m64(b))); +} + +// Average packed unsigned 8-bit integers in a and b, and store the results in +// dst. +// +// FOR j := 0 to 7 +// i := j*8 +// dst[i+7:i] := (a[i+7:i] + b[i+7:i] + 1) >> 1 +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_m_pavgb +#define _m_pavgb(a, b) _mm_avg_pu8(a, b) + +// Average packed unsigned 16-bit integers in a and b, and store the results in +// dst. +// +// FOR j := 0 to 3 +// i := j*16 +// dst[i+15:i] := (a[i+15:i] + b[i+15:i] + 1) >> 1 +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_m_pavgw +#define _m_pavgw(a, b) _mm_avg_pu16(a, b) + +// Computes the average of the 16 unsigned 8-bit integers in a and the 16 +// unsigned 8-bit integers in b and rounds. +// +// r0 := (a0 + b0) / 2 +// r1 := (a1 + b1) / 2 +// ... +// r15 := (a15 + b15) / 2 +// +// https://msdn.microsoft.com/en-us/library/vstudio/8zwh554a(v%3dvs.90).aspx +FORCE_INLINE __m128i _mm_avg_epu8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vrhaddq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b))); +} + +// Computes the average of the 8 unsigned 16-bit integers in a and the 8 +// unsigned 16-bit integers in b and rounds. +// +// r0 := (a0 + b0) / 2 +// r1 := (a1 + b1) / 2 +// ... +// r7 := (a7 + b7) / 2 +// +// https://msdn.microsoft.com/en-us/library/vstudio/y13ca3c8(v=vs.90).aspx +FORCE_INLINE __m128i _mm_avg_epu16(__m128i a, __m128i b) +{ + return (__m128i) vrhaddq_u16(vreinterpretq_u16_m128i(a), + vreinterpretq_u16_m128i(b)); +} + +// Adds the four single-precision, floating-point values of a and b. +// +// r0 := a0 + b0 +// r1 := a1 + b1 +// r2 := a2 + b2 +// r3 := a3 + b3 +// +// https://msdn.microsoft.com/en-us/library/vstudio/c9848chc(v=vs.100).aspx +FORCE_INLINE __m128 _mm_add_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_f32( + vaddq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Add packed double-precision (64-bit) floating-point elements in a and b, and +// store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_add_pd +FORCE_INLINE __m128d _mm_add_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64( + vaddq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + double *da = (double *) &a; + double *db = (double *) &b; + double c[2]; + c[0] = da[0] + db[0]; + c[1] = da[1] + db[1]; + return vld1q_f32((float32_t *) c); +#endif +} + +// Add 64-bit integers a and b, and store the result in dst. +// +// dst[63:0] := a[63:0] + b[63:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_add_si64 +FORCE_INLINE __m64 _mm_add_si64(__m64 a, __m64 b) +{ + return vreinterpret_m64_s64( + vadd_s64(vreinterpret_s64_m64(a), vreinterpret_s64_m64(b))); +} + +// adds the scalar single-precision floating point values of a and b. +// https://msdn.microsoft.com/en-us/library/be94x2y6(v=vs.100).aspx +FORCE_INLINE __m128 _mm_add_ss(__m128 a, __m128 b) +{ + float32_t b0 = vgetq_lane_f32(vreinterpretq_f32_m128(b), 0); + float32x4_t value = vsetq_lane_f32(b0, vdupq_n_f32(0), 0); + // the upper values in the result must be the remnants of . + return vreinterpretq_m128_f32(vaddq_f32(a, value)); +} + +// Adds the 4 signed or unsigned 64-bit integers in a to the 4 signed or +// unsigned 32-bit integers in b. +// https://msdn.microsoft.com/en-us/library/vstudio/09xs4fkk(v=vs.100).aspx +FORCE_INLINE __m128i _mm_add_epi64(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s64( + vaddq_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(b))); +} + +// Adds the 4 signed or unsigned 32-bit integers in a to the 4 signed or +// unsigned 32-bit integers in b. +// +// r0 := a0 + b0 +// r1 := a1 + b1 +// r2 := a2 + b2 +// r3 := a3 + b3 +// +// https://msdn.microsoft.com/en-us/library/vstudio/09xs4fkk(v=vs.100).aspx +FORCE_INLINE __m128i _mm_add_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vaddq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Adds the 8 signed or unsigned 16-bit integers in a to the 8 signed or +// unsigned 16-bit integers in b. +// https://msdn.microsoft.com/en-us/library/fceha5k4(v=vs.100).aspx +FORCE_INLINE __m128i _mm_add_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vaddq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Adds the 16 signed or unsigned 8-bit integers in a to the 16 signed or +// unsigned 8-bit integers in b. +// https://technet.microsoft.com/en-us/subscriptions/yc7tcyzs(v=vs.90) +FORCE_INLINE __m128i _mm_add_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s8( + vaddq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Adds the 8 signed 16-bit integers in a to the 8 signed 16-bit integers in b +// and saturates. +// +// r0 := SignedSaturate(a0 + b0) +// r1 := SignedSaturate(a1 + b1) +// ... +// r7 := SignedSaturate(a7 + b7) +// +// https://msdn.microsoft.com/en-us/library/1a306ef8(v=vs.100).aspx +FORCE_INLINE __m128i _mm_adds_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vqaddq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Add packed signed 8-bit integers in a and b using saturation, and store the +// results in dst. +// +// FOR j := 0 to 15 +// i := j*8 +// dst[i+7:i] := Saturate8( a[i+7:i] + b[i+7:i] ) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_adds_epi8 +FORCE_INLINE __m128i _mm_adds_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s8( + vqaddq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Adds the 16 unsigned 8-bit integers in a to the 16 unsigned 8-bit integers in +// b and saturates.. +// https://msdn.microsoft.com/en-us/library/9hahyddy(v=vs.100).aspx +FORCE_INLINE __m128i _mm_adds_epu8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vqaddq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b))); +} + +// Multiplies the 8 signed or unsigned 16-bit integers from a by the 8 signed or +// unsigned 16-bit integers from b. +// +// r0 := (a0 * b0)[15:0] +// r1 := (a1 * b1)[15:0] +// ... +// r7 := (a7 * b7)[15:0] +// +// https://msdn.microsoft.com/en-us/library/vstudio/9ks1472s(v=vs.100).aspx +FORCE_INLINE __m128i _mm_mullo_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vmulq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Multiplies the 4 signed or unsigned 32-bit integers from a by the 4 signed or +// unsigned 32-bit integers from b. +// https://msdn.microsoft.com/en-us/library/vstudio/bb531409(v=vs.100).aspx +FORCE_INLINE __m128i _mm_mullo_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vmulq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Multiply the packed unsigned 16-bit integers in a and b, producing +// intermediate 32-bit integers, and store the high 16 bits of the intermediate +// integers in dst. +// +// FOR j := 0 to 3 +// i := j*16 +// tmp[31:0] := a[i+15:i] * b[i+15:i] +// dst[i+15:i] := tmp[31:16] +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_m_pmulhuw +#define _m_pmulhuw(a, b) _mm_mulhi_pu16(a, b) + +// Multiplies the four single-precision, floating-point values of a and b. +// +// r0 := a0 * b0 +// r1 := a1 * b1 +// r2 := a2 * b2 +// r3 := a3 * b3 +// +// https://msdn.microsoft.com/en-us/library/vstudio/22kbk6t9(v=vs.100).aspx +FORCE_INLINE __m128 _mm_mul_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_f32( + vmulq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Multiply packed double-precision (64-bit) floating-point elements in a and b, +// and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mul_pd +FORCE_INLINE __m128d _mm_mul_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64( + vmulq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + double *da = (double *) &a; + double *db = (double *) &b; + double c[2]; + c[0] = da[0] * db[0]; + c[1] = da[1] * db[1]; + return vld1q_f32((float32_t *) c); +#endif +} + +// Multiply the lower single-precision (32-bit) floating-point element in a and +// b, store the result in the lower element of dst, and copy the upper 3 packed +// elements from a to the upper elements of dst. +// +// dst[31:0] := a[31:0] * b[31:0] +// dst[127:32] := a[127:32] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mul_ss +FORCE_INLINE __m128 _mm_mul_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_mul_ps(a, b)); +} + +// Multiply the low unsigned 32-bit integers from each packed 64-bit element in +// a and b, and store the unsigned 64-bit results in dst. +// +// r0 := (a0 & 0xFFFFFFFF) * (b0 & 0xFFFFFFFF) +// r1 := (a2 & 0xFFFFFFFF) * (b2 & 0xFFFFFFFF) +FORCE_INLINE __m128i _mm_mul_epu32(__m128i a, __m128i b) +{ + // vmull_u32 upcasts instead of masking, so we downcast. + uint32x2_t a_lo = vmovn_u64(vreinterpretq_u64_m128i(a)); + uint32x2_t b_lo = vmovn_u64(vreinterpretq_u64_m128i(b)); + return vreinterpretq_m128i_u64(vmull_u32(a_lo, b_lo)); +} + +// Multiply the low unsigned 32-bit integers from a and b, and store the +// unsigned 64-bit result in dst. +// +// dst[63:0] := a[31:0] * b[31:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mul_su32 +FORCE_INLINE __m64 _mm_mul_su32(__m64 a, __m64 b) +{ + return vreinterpret_m64_u64(vget_low_u64( + vmull_u32(vreinterpret_u32_m64(a), vreinterpret_u32_m64(b)))); +} + +// Multiply the low signed 32-bit integers from each packed 64-bit element in +// a and b, and store the signed 64-bit results in dst. +// +// r0 := (int64_t)(int32_t)a0 * (int64_t)(int32_t)b0 +// r1 := (int64_t)(int32_t)a2 * (int64_t)(int32_t)b2 +FORCE_INLINE __m128i _mm_mul_epi32(__m128i a, __m128i b) +{ + // vmull_s32 upcasts instead of masking, so we downcast. + int32x2_t a_lo = vmovn_s64(vreinterpretq_s64_m128i(a)); + int32x2_t b_lo = vmovn_s64(vreinterpretq_s64_m128i(b)); + return vreinterpretq_m128i_s64(vmull_s32(a_lo, b_lo)); +} + +// Multiplies the 8 signed 16-bit integers from a by the 8 signed 16-bit +// integers from b. +// +// r0 := (a0 * b0) + (a1 * b1) +// r1 := (a2 * b2) + (a3 * b3) +// r2 := (a4 * b4) + (a5 * b5) +// r3 := (a6 * b6) + (a7 * b7) +// https://msdn.microsoft.com/en-us/library/yht36sa6(v=vs.90).aspx +FORCE_INLINE __m128i _mm_madd_epi16(__m128i a, __m128i b) +{ + int32x4_t low = vmull_s16(vget_low_s16(vreinterpretq_s16_m128i(a)), + vget_low_s16(vreinterpretq_s16_m128i(b))); + int32x4_t high = vmull_s16(vget_high_s16(vreinterpretq_s16_m128i(a)), + vget_high_s16(vreinterpretq_s16_m128i(b))); + + int32x2_t low_sum = vpadd_s32(vget_low_s32(low), vget_high_s32(low)); + int32x2_t high_sum = vpadd_s32(vget_low_s32(high), vget_high_s32(high)); + + return vreinterpretq_m128i_s32(vcombine_s32(low_sum, high_sum)); +} + +// Multiply packed signed 16-bit integers in a and b, producing intermediate +// signed 32-bit integers. Shift right by 15 bits while rounding up, and store +// the packed 16-bit integers in dst. +// +// r0 := Round(((int32_t)a0 * (int32_t)b0) >> 15) +// r1 := Round(((int32_t)a1 * (int32_t)b1) >> 15) +// r2 := Round(((int32_t)a2 * (int32_t)b2) >> 15) +// ... +// r7 := Round(((int32_t)a7 * (int32_t)b7) >> 15) +FORCE_INLINE __m128i _mm_mulhrs_epi16(__m128i a, __m128i b) +{ + // Has issues due to saturation + // return vreinterpretq_m128i_s16(vqrdmulhq_s16(a, b)); + + // Multiply + int32x4_t mul_lo = vmull_s16(vget_low_s16(vreinterpretq_s16_m128i(a)), + vget_low_s16(vreinterpretq_s16_m128i(b))); + int32x4_t mul_hi = vmull_s16(vget_high_s16(vreinterpretq_s16_m128i(a)), + vget_high_s16(vreinterpretq_s16_m128i(b))); + + // Rounding narrowing shift right + // narrow = (int16_t)((mul + 16384) >> 15); + int16x4_t narrow_lo = vrshrn_n_s32(mul_lo, 15); + int16x4_t narrow_hi = vrshrn_n_s32(mul_hi, 15); + + // Join together + return vreinterpretq_m128i_s16(vcombine_s16(narrow_lo, narrow_hi)); +} + +// Vertically multiply each unsigned 8-bit integer from a with the corresponding +// signed 8-bit integer from b, producing intermediate signed 16-bit integers. +// Horizontally add adjacent pairs of intermediate signed 16-bit integers, +// and pack the saturated results in dst. +// +// FOR j := 0 to 7 +// i := j*16 +// dst[i+15:i] := Saturate_To_Int16( a[i+15:i+8]*b[i+15:i+8] + +// a[i+7:i]*b[i+7:i] ) +// ENDFOR +FORCE_INLINE __m128i _mm_maddubs_epi16(__m128i _a, __m128i _b) +{ +#if defined(__aarch64__) + uint8x16_t a = vreinterpretq_u8_m128i(_a); + int8x16_t b = vreinterpretq_s8_m128i(_b); + int16x8_t tl = vmulq_s16(vreinterpretq_s16_u16(vmovl_u8(vget_low_u8(a))), + vmovl_s8(vget_low_s8(b))); + int16x8_t th = vmulq_s16(vreinterpretq_s16_u16(vmovl_u8(vget_high_u8(a))), + vmovl_s8(vget_high_s8(b))); + return vreinterpretq_m128i_s16( + vqaddq_s16(vuzp1q_s16(tl, th), vuzp2q_s16(tl, th))); +#else + // This would be much simpler if x86 would choose to zero extend OR sign + // extend, not both. This could probably be optimized better. + uint16x8_t a = vreinterpretq_u16_m128i(_a); + int16x8_t b = vreinterpretq_s16_m128i(_b); + + // Zero extend a + int16x8_t a_odd = vreinterpretq_s16_u16(vshrq_n_u16(a, 8)); + int16x8_t a_even = vreinterpretq_s16_u16(vbicq_u16(a, vdupq_n_u16(0xff00))); + + // Sign extend by shifting left then shifting right. + int16x8_t b_even = vshrq_n_s16(vshlq_n_s16(b, 8), 8); + int16x8_t b_odd = vshrq_n_s16(b, 8); + + // multiply + int16x8_t prod1 = vmulq_s16(a_even, b_even); + int16x8_t prod2 = vmulq_s16(a_odd, b_odd); + + // saturated add + return vreinterpretq_m128i_s16(vqaddq_s16(prod1, prod2)); +#endif +} + +// Computes the fused multiple add product of 32-bit floating point numbers. +// +// Return Value +// Multiplies A and B, and adds C to the temporary result before returning it. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_fmadd +FORCE_INLINE __m128 _mm_fmadd_ps(__m128 a, __m128 b, __m128 c) +{ +#if defined(__aarch64__) + return vreinterpretq_m128_f32(vfmaq_f32(vreinterpretq_f32_m128(c), + vreinterpretq_f32_m128(b), + vreinterpretq_f32_m128(a))); +#else + return _mm_add_ps(_mm_mul_ps(a, b), c); +#endif +} + +// Alternatively add and subtract packed single-precision (32-bit) +// floating-point elements in a to/from packed elements in b, and store the +// results in dst. +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=addsub_ps +FORCE_INLINE __m128 _mm_addsub_ps(__m128 a, __m128 b) +{ + __m128 mask = {-1.0f, 1.0f, -1.0f, 1.0f}; + return _mm_fmadd_ps(b, mask, a); +} + +// Compute the absolute differences of packed unsigned 8-bit integers in a and +// b, then horizontally sum each consecutive 8 differences to produce two +// unsigned 16-bit integers, and pack these unsigned 16-bit integers in the low +// 16 bits of 64-bit elements in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sad_epu8 +FORCE_INLINE __m128i _mm_sad_epu8(__m128i a, __m128i b) +{ + uint16x8_t t = vpaddlq_u8(vabdq_u8((uint8x16_t) a, (uint8x16_t) b)); + uint16_t r0 = t[0] + t[1] + t[2] + t[3]; + uint16_t r4 = t[4] + t[5] + t[6] + t[7]; + uint16x8_t r = vsetq_lane_u16(r0, vdupq_n_u16(0), 0); + return (__m128i) vsetq_lane_u16(r4, r, 4); +} + +// Compute the absolute differences of packed unsigned 8-bit integers in a and +// b, then horizontally sum each consecutive 8 differences to produce four +// unsigned 16-bit integers, and pack these unsigned 16-bit integers in the low +// 16 bits of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sad_pu8 +FORCE_INLINE __m64 _mm_sad_pu8(__m64 a, __m64 b) +{ + uint16x4_t t = + vpaddl_u8(vabd_u8(vreinterpret_u8_m64(a), vreinterpret_u8_m64(b))); + uint16_t r0 = t[0] + t[1] + t[2] + t[3]; + return vreinterpret_m64_u16(vset_lane_u16(r0, vdup_n_u16(0), 0)); +} + +// Compute the absolute differences of packed unsigned 8-bit integers in a and +// b, then horizontally sum each consecutive 8 differences to produce four +// unsigned 16-bit integers, and pack these unsigned 16-bit integers in the low +// 16 bits of dst. +// +// FOR j := 0 to 7 +// i := j*8 +// tmp[i+7:i] := ABS(a[i+7:i] - b[i+7:i]) +// ENDFOR +// dst[15:0] := tmp[7:0] + tmp[15:8] + tmp[23:16] + tmp[31:24] + tmp[39:32] + +// tmp[47:40] + tmp[55:48] + tmp[63:56] dst[63:16] := 0 +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_m_psadbw +#define _m_psadbw(a, b) _mm_sad_pu8(a, b) + +// Divides the four single-precision, floating-point values of a and b. +// +// r0 := a0 / b0 +// r1 := a1 / b1 +// r2 := a2 / b2 +// r3 := a3 / b3 +// +// https://msdn.microsoft.com/en-us/library/edaw8147(v=vs.100).aspx +FORCE_INLINE __m128 _mm_div_ps(__m128 a, __m128 b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128_f32( + vdivq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +#else + float32x4_t recip0 = vrecpeq_f32(vreinterpretq_f32_m128(b)); + float32x4_t recip1 = + vmulq_f32(recip0, vrecpsq_f32(recip0, vreinterpretq_f32_m128(b))); + return vreinterpretq_m128_f32(vmulq_f32(vreinterpretq_f32_m128(a), recip1)); +#endif +} + +// Divides the scalar single-precision floating point value of a by b. +// https://msdn.microsoft.com/en-us/library/4y73xa49(v=vs.100).aspx +FORCE_INLINE __m128 _mm_div_ss(__m128 a, __m128 b) +{ + float32_t value = + vgetq_lane_f32(vreinterpretq_f32_m128(_mm_div_ps(a, b)), 0); + return vreinterpretq_m128_f32( + vsetq_lane_f32(value, vreinterpretq_f32_m128(a), 0)); +} + +// Compute the approximate reciprocal of packed single-precision (32-bit) +// floating-point elements in a, and store the results in dst. The maximum +// relative error for this approximation is less than 1.5*2^-12. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_rcp_ps +FORCE_INLINE __m128 _mm_rcp_ps(__m128 in) +{ +#if defined(__aarch64__) + return vreinterpretq_m128_f32( + vdivq_f32(vdupq_n_f32(1.0f), vreinterpretq_f32_m128(in))); +#else + float32x4_t recip = vrecpeq_f32(vreinterpretq_f32_m128(in)); + recip = vmulq_f32(recip, vrecpsq_f32(recip, vreinterpretq_f32_m128(in))); + return vreinterpretq_m128_f32(recip); +#endif +} + +// Compute the approximate reciprocal of the lower single-precision (32-bit) +// floating-point element in a, store the result in the lower element of dst, +// and copy the upper 3 packed elements from a to the upper elements of dst. The +// maximum relative error for this approximation is less than 1.5*2^-12. +// +// dst[31:0] := (1.0 / a[31:0]) +// dst[127:32] := a[127:32] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_rcp_ss +FORCE_INLINE __m128 _mm_rcp_ss(__m128 a) +{ + return _mm_move_ss(a, _mm_rcp_ps(a)); +} + +// Computes the approximations of square roots of the four single-precision, +// floating-point values of a. First computes reciprocal square roots and then +// reciprocals of the four values. +// +// r0 := sqrt(a0) +// r1 := sqrt(a1) +// r2 := sqrt(a2) +// r3 := sqrt(a3) +// +// https://msdn.microsoft.com/en-us/library/vstudio/8z67bwwk(v=vs.100).aspx +FORCE_INLINE __m128 _mm_sqrt_ps(__m128 in) +{ +#if defined(__aarch64__) + return vreinterpretq_m128_f32(vsqrtq_f32(vreinterpretq_f32_m128(in))); +#else + float32x4_t recipsq = vrsqrteq_f32(vreinterpretq_f32_m128(in)); + float32x4_t sq = vrecpeq_f32(recipsq); + // ??? use step versions of both sqrt and recip for better accuracy? + return vreinterpretq_m128_f32(sq); +#endif +} + +// Computes the approximation of the square root of the scalar single-precision +// floating point value of in. +// https://msdn.microsoft.com/en-us/library/ahfsc22d(v=vs.100).aspx +FORCE_INLINE __m128 _mm_sqrt_ss(__m128 in) +{ + float32_t value = + vgetq_lane_f32(vreinterpretq_f32_m128(_mm_sqrt_ps(in)), 0); + return vreinterpretq_m128_f32( + vsetq_lane_f32(value, vreinterpretq_f32_m128(in), 0)); +} + +// Computes the approximations of the reciprocal square roots of the four +// single-precision floating point values of in. +// https://msdn.microsoft.com/en-us/library/22hfsh53(v=vs.100).aspx +FORCE_INLINE __m128 _mm_rsqrt_ps(__m128 in) +{ + return vreinterpretq_m128_f32(vrsqrteq_f32(vreinterpretq_f32_m128(in))); +} + +// Compute the approximate reciprocal square root of the lower single-precision +// (32-bit) floating-point element in a, store the result in the lower element +// of dst, and copy the upper 3 packed elements from a to the upper elements of +// dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_rsqrt_ss +FORCE_INLINE __m128 _mm_rsqrt_ss(__m128 in) +{ + return vsetq_lane_f32(vgetq_lane_f32(_mm_rsqrt_ps(in), 0), in, 0); +} + +// Compare packed signed 16-bit integers in a and b, and store packed maximum +// values in dst. +// +// FOR j := 0 to 3 +// i := j*16 +// dst[i+15:i] := MAX(a[i+15:i], b[i+15:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_max_pi16 +FORCE_INLINE __m64 _mm_max_pi16(__m64 a, __m64 b) +{ + return vreinterpret_m64_s16( + vmax_s16(vreinterpret_s16_m64(a), vreinterpret_s16_m64(b))); +} + +// Compare packed signed 16-bit integers in a and b, and store packed maximum +// values in dst. +// +// FOR j := 0 to 3 +// i := j*16 +// dst[i+15:i] := MAX(a[i+15:i], b[i+15:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_max_pi16 +#define _m_pmaxsw(a, b) _mm_max_pi16(a, b) + +// Computes the maximums of the four single-precision, floating-point values of +// a and b. +// https://msdn.microsoft.com/en-us/library/vstudio/ff5d607a(v=vs.100).aspx +FORCE_INLINE __m128 _mm_max_ps(__m128 a, __m128 b) +{ +#if SSE2NEON_PRECISE_MINMAX + float32x4_t _a = vreinterpretq_f32_m128(a); + float32x4_t _b = vreinterpretq_f32_m128(b); + return vbslq_f32(vcltq_f32(_b, _a), _a, _b); +#else + return vreinterpretq_m128_f32( + vmaxq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +#endif +} + +// Compare packed unsigned 8-bit integers in a and b, and store packed maximum +// values in dst. +// +// FOR j := 0 to 7 +// i := j*8 +// dst[i+7:i] := MAX(a[i+7:i], b[i+7:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_max_pu8 +FORCE_INLINE __m64 _mm_max_pu8(__m64 a, __m64 b) +{ + return vreinterpret_m64_u8( + vmax_u8(vreinterpret_u8_m64(a), vreinterpret_u8_m64(b))); +} + +// Compare packed unsigned 8-bit integers in a and b, and store packed maximum +// values in dst. +// +// FOR j := 0 to 7 +// i := j*8 +// dst[i+7:i] := MAX(a[i+7:i], b[i+7:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_max_pu8 +#define _m_pmaxub(a, b) _mm_max_pu8(a, b) + +// Compare packed signed 16-bit integers in a and b, and store packed minimum +// values in dst. +// +// FOR j := 0 to 3 +// i := j*16 +// dst[i+15:i] := MIN(a[i+15:i], b[i+15:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_min_pi16 +FORCE_INLINE __m64 _mm_min_pi16(__m64 a, __m64 b) +{ + return vreinterpret_m64_s16( + vmin_s16(vreinterpret_s16_m64(a), vreinterpret_s16_m64(b))); +} + +// Compare packed signed 16-bit integers in a and b, and store packed minimum +// values in dst. +// +// FOR j := 0 to 3 +// i := j*16 +// dst[i+15:i] := MIN(a[i+15:i], b[i+15:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_min_pi16 +#define _m_pminsw(a, b) _mm_min_pi16(a, b) + +// Computes the minima of the four single-precision, floating-point values of a +// and b. +// https://msdn.microsoft.com/en-us/library/vstudio/wh13kadz(v=vs.100).aspx +FORCE_INLINE __m128 _mm_min_ps(__m128 a, __m128 b) +{ +#if SSE2NEON_PRECISE_MINMAX + float32x4_t _a = vreinterpretq_f32_m128(a); + float32x4_t _b = vreinterpretq_f32_m128(b); + return vbslq_f32(vcltq_f32(_a, _b), _a, _b); +#else + return vreinterpretq_m128_f32( + vminq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +#endif +} + +// Compare packed unsigned 8-bit integers in a and b, and store packed minimum +// values in dst. +// +// FOR j := 0 to 7 +// i := j*8 +// dst[i+7:i] := MIN(a[i+7:i], b[i+7:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_min_pu8 +FORCE_INLINE __m64 _mm_min_pu8(__m64 a, __m64 b) +{ + return vreinterpret_m64_u8( + vmin_u8(vreinterpret_u8_m64(a), vreinterpret_u8_m64(b))); +} + +// Compare packed unsigned 8-bit integers in a and b, and store packed minimum +// values in dst. +// +// FOR j := 0 to 7 +// i := j*8 +// dst[i+7:i] := MIN(a[i+7:i], b[i+7:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_min_pu8 +#define _m_pminub(a, b) _mm_min_pu8(a, b) + +// Computes the maximum of the two lower scalar single-precision floating point +// values of a and b. +// https://msdn.microsoft.com/en-us/library/s6db5esz(v=vs.100).aspx +FORCE_INLINE __m128 _mm_max_ss(__m128 a, __m128 b) +{ + float32_t value = vgetq_lane_f32(_mm_max_ps(a, b), 0); + return vreinterpretq_m128_f32( + vsetq_lane_f32(value, vreinterpretq_f32_m128(a), 0)); +} + +// Computes the minimum of the two lower scalar single-precision floating point +// values of a and b. +// https://msdn.microsoft.com/en-us/library/0a9y7xaa(v=vs.100).aspx +FORCE_INLINE __m128 _mm_min_ss(__m128 a, __m128 b) +{ + float32_t value = vgetq_lane_f32(_mm_min_ps(a, b), 0); + return vreinterpretq_m128_f32( + vsetq_lane_f32(value, vreinterpretq_f32_m128(a), 0)); +} + +// Computes the pairwise maxima of the 16 unsigned 8-bit integers from a and the +// 16 unsigned 8-bit integers from b. +// https://msdn.microsoft.com/en-us/library/st6634za(v=vs.100).aspx +FORCE_INLINE __m128i _mm_max_epu8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vmaxq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b))); +} + +// Computes the pairwise minima of the 16 unsigned 8-bit integers from a and the +// 16 unsigned 8-bit integers from b. +// https://msdn.microsoft.com/ko-kr/library/17k8cf58(v=vs.100).aspxx +FORCE_INLINE __m128i _mm_min_epu8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vminq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b))); +} + +// Computes the pairwise minima of the 8 signed 16-bit integers from a and the 8 +// signed 16-bit integers from b. +// https://msdn.microsoft.com/en-us/library/vstudio/6te997ew(v=vs.100).aspx +FORCE_INLINE __m128i _mm_min_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vminq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Compare packed signed 8-bit integers in a and b, and store packed maximum +// values in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_max_epi8 +FORCE_INLINE __m128i _mm_max_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s8( + vmaxq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Compare packed unsigned 16-bit integers in a and b, and store packed maximum +// values in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_max_epu16 +FORCE_INLINE __m128i _mm_max_epu16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vmaxq_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b))); +} + +// Compare packed signed 8-bit integers in a and b, and store packed minimum +// values in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_min_epi8 +FORCE_INLINE __m128i _mm_min_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s8( + vminq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Compare packed unsigned 16-bit integers in a and b, and store packed minimum +// values in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_min_epu16 +FORCE_INLINE __m128i _mm_min_epu16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vminq_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b))); +} + +// Computes the pairwise maxima of the 8 signed 16-bit integers from a and the 8 +// signed 16-bit integers from b. +// https://msdn.microsoft.com/en-us/LIBRary/3x060h7c(v=vs.100).aspx +FORCE_INLINE __m128i _mm_max_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vmaxq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// epi versions of min/max +// Computes the pariwise maximums of the four signed 32-bit integer values of a +// and b. +// +// A 128-bit parameter that can be defined with the following equations: +// r0 := (a0 > b0) ? a0 : b0 +// r1 := (a1 > b1) ? a1 : b1 +// r2 := (a2 > b2) ? a2 : b2 +// r3 := (a3 > b3) ? a3 : b3 +// +// https://msdn.microsoft.com/en-us/library/vstudio/bb514055(v=vs.100).aspx +FORCE_INLINE __m128i _mm_max_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vmaxq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Computes the pariwise minima of the four signed 32-bit integer values of a +// and b. +// +// A 128-bit parameter that can be defined with the following equations: +// r0 := (a0 < b0) ? a0 : b0 +// r1 := (a1 < b1) ? a1 : b1 +// r2 := (a2 < b2) ? a2 : b2 +// r3 := (a3 < b3) ? a3 : b3 +// +// https://msdn.microsoft.com/en-us/library/vstudio/bb531476(v=vs.100).aspx +FORCE_INLINE __m128i _mm_min_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vminq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Compare packed unsigned 32-bit integers in a and b, and store packed maximum +// values in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_max_epu32 +FORCE_INLINE __m128i _mm_max_epu32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u32( + vmaxq_u32(vreinterpretq_u32_m128i(a), vreinterpretq_u32_m128i(b))); +} + +// Compare packed unsigned 32-bit integers in a and b, and store packed minimum +// values in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_max_epu32 +FORCE_INLINE __m128i _mm_min_epu32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u32( + vminq_u32(vreinterpretq_u32_m128i(a), vreinterpretq_u32_m128i(b))); +} + +// Multiply the packed unsigned 16-bit integers in a and b, producing +// intermediate 32-bit integers, and store the high 16 bits of the intermediate +// integers in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mulhi_pu16 +FORCE_INLINE __m64 _mm_mulhi_pu16(__m64 a, __m64 b) +{ + return vreinterpret_m64_u16(vshrn_n_u32( + vmull_u16(vreinterpret_u16_m64(a), vreinterpret_u16_m64(b)), 16)); +} + +// Multiplies the 8 signed 16-bit integers from a by the 8 signed 16-bit +// integers from b. +// +// r0 := (a0 * b0)[31:16] +// r1 := (a1 * b1)[31:16] +// ... +// r7 := (a7 * b7)[31:16] +// +// https://msdn.microsoft.com/en-us/library/vstudio/59hddw1d(v=vs.100).aspx +FORCE_INLINE __m128i _mm_mulhi_epi16(__m128i a, __m128i b) +{ + /* FIXME: issue with large values because of result saturation */ + // int16x8_t ret = vqdmulhq_s16(vreinterpretq_s16_m128i(a), + // vreinterpretq_s16_m128i(b)); /* =2*a*b */ return + // vreinterpretq_m128i_s16(vshrq_n_s16(ret, 1)); + int16x4_t a3210 = vget_low_s16(vreinterpretq_s16_m128i(a)); + int16x4_t b3210 = vget_low_s16(vreinterpretq_s16_m128i(b)); + int32x4_t ab3210 = vmull_s16(a3210, b3210); /* 3333222211110000 */ + int16x4_t a7654 = vget_high_s16(vreinterpretq_s16_m128i(a)); + int16x4_t b7654 = vget_high_s16(vreinterpretq_s16_m128i(b)); + int32x4_t ab7654 = vmull_s16(a7654, b7654); /* 7777666655554444 */ + uint16x8x2_t r = + vuzpq_u16(vreinterpretq_u16_s32(ab3210), vreinterpretq_u16_s32(ab7654)); + return vreinterpretq_m128i_u16(r.val[1]); +} + +// Multiply the packed unsigned 16-bit integers in a and b, producing +// intermediate 32-bit integers, and store the high 16 bits of the intermediate +// integers in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mulhi_epu16 +FORCE_INLINE __m128i _mm_mulhi_epu16(__m128i a, __m128i b) +{ + uint16x4_t a3210 = vget_low_u16(vreinterpretq_u16_m128i(a)); + uint16x4_t b3210 = vget_low_u16(vreinterpretq_u16_m128i(b)); + uint32x4_t ab3210 = vmull_u16(a3210, b3210); +#if defined(__aarch64__) + uint32x4_t ab7654 = + vmull_high_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b)); + uint16x8_t r = vuzp2q_u16(vreinterpretq_u16_u32(ab3210), + vreinterpretq_u16_u32(ab7654)); + return vreinterpretq_m128i_u16(r); +#else + uint16x4_t a7654 = vget_high_u16(vreinterpretq_u16_m128i(a)); + uint16x4_t b7654 = vget_high_u16(vreinterpretq_u16_m128i(b)); + uint32x4_t ab7654 = vmull_u16(a7654, b7654); + uint16x8x2_t r = + vuzpq_u16(vreinterpretq_u16_u32(ab3210), vreinterpretq_u16_u32(ab7654)); + return vreinterpretq_m128i_u16(r.val[1]); +#endif +} + +// Computes pairwise add of each argument as single-precision, floating-point +// values a and b. +// https://msdn.microsoft.com/en-us/library/yd9wecaa.aspx +FORCE_INLINE __m128 _mm_hadd_ps(__m128 a, __m128 b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128_f32( + vpaddq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +#else + float32x2_t a10 = vget_low_f32(vreinterpretq_f32_m128(a)); + float32x2_t a32 = vget_high_f32(vreinterpretq_f32_m128(a)); + float32x2_t b10 = vget_low_f32(vreinterpretq_f32_m128(b)); + float32x2_t b32 = vget_high_f32(vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_f32( + vcombine_f32(vpadd_f32(a10, a32), vpadd_f32(b10, b32))); +#endif +} + +// Computes pairwise add of each argument as a 16-bit signed or unsigned integer +// values a and b. +FORCE_INLINE __m128i _mm_hadd_epi16(__m128i _a, __m128i _b) +{ + int16x8_t a = vreinterpretq_s16_m128i(_a); + int16x8_t b = vreinterpretq_s16_m128i(_b); +#if defined(__aarch64__) + return vreinterpretq_m128i_s16(vpaddq_s16(a, b)); +#else + return vreinterpretq_m128i_s16( + vcombine_s16(vpadd_s16(vget_low_s16(a), vget_high_s16(a)), + vpadd_s16(vget_low_s16(b), vget_high_s16(b)))); +#endif +} + +// Horizontally substract adjacent pairs of single-precision (32-bit) +// floating-point elements in a and b, and pack the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_hsub_ps +FORCE_INLINE __m128 _mm_hsub_ps(__m128 _a, __m128 _b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128_f32(vsubq_f32( + vuzp1q_f32(vreinterpretq_f32_m128(_a), vreinterpretq_f32_m128(_b)), + vuzp2q_f32(vreinterpretq_f32_m128(_a), vreinterpretq_f32_m128(_b)))); +#else + float32x4x2_t c = + vuzpq_f32(vreinterpretq_f32_m128(_a), vreinterpretq_f32_m128(_b)); + return vreinterpretq_m128_f32(vsubq_f32(c.val[0], c.val[1])); +#endif +} + +// Horizontally add adjacent pairs of 16-bit integers in a and b, and pack the +// signed 16-bit results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_hadd_pi16 +FORCE_INLINE __m64 _mm_hadd_pi16(__m64 a, __m64 b) +{ + return vreinterpret_m64_s16( + vpadd_s16(vreinterpret_s16_m64(a), vreinterpret_s16_m64(b))); +} + +// Horizontally add adjacent pairs of 32-bit integers in a and b, and pack the +// signed 32-bit results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_hadd_pi32 +FORCE_INLINE __m64 _mm_hadd_pi32(__m64 a, __m64 b) +{ + return vreinterpret_m64_s32( + vpadd_s32(vreinterpret_s32_m64(a), vreinterpret_s32_m64(b))); +} + +// Computes pairwise difference of each argument as a 16-bit signed or unsigned +// integer values a and b. +FORCE_INLINE __m128i _mm_hsub_epi16(__m128i _a, __m128i _b) +{ + int32x4_t a = vreinterpretq_s32_m128i(_a); + int32x4_t b = vreinterpretq_s32_m128i(_b); + // Interleave using vshrn/vmovn + // [a0|a2|a4|a6|b0|b2|b4|b6] + // [a1|a3|a5|a7|b1|b3|b5|b7] + int16x8_t ab0246 = vcombine_s16(vmovn_s32(a), vmovn_s32(b)); + int16x8_t ab1357 = vcombine_s16(vshrn_n_s32(a, 16), vshrn_n_s32(b, 16)); + // Subtract + return vreinterpretq_m128i_s16(vsubq_s16(ab0246, ab1357)); +} + +// Computes saturated pairwise sub of each argument as a 16-bit signed +// integer values a and b. +FORCE_INLINE __m128i _mm_hadds_epi16(__m128i _a, __m128i _b) +{ +#if defined(__aarch64__) + int16x8_t a = vreinterpretq_s16_m128i(_a); + int16x8_t b = vreinterpretq_s16_m128i(_b); + return vreinterpretq_s64_s16( + vqaddq_s16(vuzp1q_s16(a, b), vuzp2q_s16(a, b))); +#else + int32x4_t a = vreinterpretq_s32_m128i(_a); + int32x4_t b = vreinterpretq_s32_m128i(_b); + // Interleave using vshrn/vmovn + // [a0|a2|a4|a6|b0|b2|b4|b6] + // [a1|a3|a5|a7|b1|b3|b5|b7] + int16x8_t ab0246 = vcombine_s16(vmovn_s32(a), vmovn_s32(b)); + int16x8_t ab1357 = vcombine_s16(vshrn_n_s32(a, 16), vshrn_n_s32(b, 16)); + // Saturated add + return vreinterpretq_m128i_s16(vqaddq_s16(ab0246, ab1357)); +#endif +} + +// Computes saturated pairwise difference of each argument as a 16-bit signed +// integer values a and b. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_hsubs_epi16 +FORCE_INLINE __m128i _mm_hsubs_epi16(__m128i _a, __m128i _b) +{ +#if defined(__aarch64__) + int16x8_t a = vreinterpretq_s16_m128i(_a); + int16x8_t b = vreinterpretq_s16_m128i(_b); + return vreinterpretq_s64_s16( + vqsubq_s16(vuzp1q_s16(a, b), vuzp2q_s16(a, b))); +#else + int32x4_t a = vreinterpretq_s32_m128i(_a); + int32x4_t b = vreinterpretq_s32_m128i(_b); + // Interleave using vshrn/vmovn + // [a0|a2|a4|a6|b0|b2|b4|b6] + // [a1|a3|a5|a7|b1|b3|b5|b7] + int16x8_t ab0246 = vcombine_s16(vmovn_s32(a), vmovn_s32(b)); + int16x8_t ab1357 = vcombine_s16(vshrn_n_s32(a, 16), vshrn_n_s32(b, 16)); + // Saturated subtract + return vreinterpretq_m128i_s16(vqsubq_s16(ab0246, ab1357)); +#endif +} + +// Computes pairwise add of each argument as a 32-bit signed or unsigned integer +// values a and b. +FORCE_INLINE __m128i _mm_hadd_epi32(__m128i _a, __m128i _b) +{ + int32x4_t a = vreinterpretq_s32_m128i(_a); + int32x4_t b = vreinterpretq_s32_m128i(_b); + return vreinterpretq_m128i_s32( + vcombine_s32(vpadd_s32(vget_low_s32(a), vget_high_s32(a)), + vpadd_s32(vget_low_s32(b), vget_high_s32(b)))); +} + +// Computes pairwise difference of each argument as a 32-bit signed or unsigned +// integer values a and b. +FORCE_INLINE __m128i _mm_hsub_epi32(__m128i _a, __m128i _b) +{ + int64x2_t a = vreinterpretq_s64_m128i(_a); + int64x2_t b = vreinterpretq_s64_m128i(_b); + // Interleave using vshrn/vmovn + // [a0|a2|b0|b2] + // [a1|a2|b1|b3] + int32x4_t ab02 = vcombine_s32(vmovn_s64(a), vmovn_s64(b)); + int32x4_t ab13 = vcombine_s32(vshrn_n_s64(a, 32), vshrn_n_s64(b, 32)); + // Subtract + return vreinterpretq_m128i_s32(vsubq_s32(ab02, ab13)); +} + +// Kahan summation for accurate summation of floating-point numbers. +// http://blog.zachbjornson.com/2019/08/11/fast-float-summation.html +FORCE_INLINE void sse2neon_kadd_f32(float *sum, float *c, float y) +{ + y -= *c; + float t = *sum + y; + *c = (t - *sum) - y; + *sum = t; +} + +// Conditionally multiply the packed single-precision (32-bit) floating-point +// elements in a and b using the high 4 bits in imm8, sum the four products, +// and conditionally store the sum in dst using the low 4 bits of imm. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_dp_ps +FORCE_INLINE __m128 _mm_dp_ps(__m128 a, __m128 b, const int imm) +{ +#if defined(__aarch64__) + /* shortcuts */ + if (imm == 0xFF) { + return _mm_set1_ps(vaddvq_f32(_mm_mul_ps(a, b))); + } + if (imm == 0x7F) { + float32x4_t m = _mm_mul_ps(a, b); + m[3] = 0; + return _mm_set1_ps(vaddvq_f32(m)); + } +#endif + + float s = 0, c = 0; + float32x4_t f32a = vreinterpretq_f32_m128(a); + float32x4_t f32b = vreinterpretq_f32_m128(b); + + /* To improve the accuracy of floating-point summation, Kahan algorithm + * is used for each operation. + */ + if (imm & (1 << 4)) + sse2neon_kadd_f32(&s, &c, f32a[0] * f32b[0]); + if (imm & (1 << 5)) + sse2neon_kadd_f32(&s, &c, f32a[1] * f32b[1]); + if (imm & (1 << 6)) + sse2neon_kadd_f32(&s, &c, f32a[2] * f32b[2]); + if (imm & (1 << 7)) + sse2neon_kadd_f32(&s, &c, f32a[3] * f32b[3]); + s += c; + + float32x4_t res = { + (imm & 0x1) ? s : 0, + (imm & 0x2) ? s : 0, + (imm & 0x4) ? s : 0, + (imm & 0x8) ? s : 0, + }; + return vreinterpretq_m128_f32(res); +} + +/* Compare operations */ + +// Compares for less than +// https://msdn.microsoft.com/en-us/library/vstudio/f330yhc8(v=vs.100).aspx +FORCE_INLINE __m128 _mm_cmplt_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32( + vcltq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Compares for less than +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/fy94wye7(v=vs.100) +FORCE_INLINE __m128 _mm_cmplt_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmplt_ps(a, b)); +} + +// Compares for greater than. +// +// r0 := (a0 > b0) ? 0xffffffff : 0x0 +// r1 := (a1 > b1) ? 0xffffffff : 0x0 +// r2 := (a2 > b2) ? 0xffffffff : 0x0 +// r3 := (a3 > b3) ? 0xffffffff : 0x0 +// +// https://msdn.microsoft.com/en-us/library/vstudio/11dy102s(v=vs.100).aspx +FORCE_INLINE __m128 _mm_cmpgt_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32( + vcgtq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Compares for greater than. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/1xyyyy9e(v=vs.100) +FORCE_INLINE __m128 _mm_cmpgt_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpgt_ps(a, b)); +} + +// Compares for greater than or equal. +// https://msdn.microsoft.com/en-us/library/vstudio/fs813y2t(v=vs.100).aspx +FORCE_INLINE __m128 _mm_cmpge_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32( + vcgeq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Compares for greater than or equal. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/kesh3ddc(v=vs.100) +FORCE_INLINE __m128 _mm_cmpge_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpge_ps(a, b)); +} + +// Compares for less than or equal. +// +// r0 := (a0 <= b0) ? 0xffffffff : 0x0 +// r1 := (a1 <= b1) ? 0xffffffff : 0x0 +// r2 := (a2 <= b2) ? 0xffffffff : 0x0 +// r3 := (a3 <= b3) ? 0xffffffff : 0x0 +// +// https://msdn.microsoft.com/en-us/library/vstudio/1s75w83z(v=vs.100).aspx +FORCE_INLINE __m128 _mm_cmple_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32( + vcleq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Compares for less than or equal. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/a7x0hbhw(v=vs.100) +FORCE_INLINE __m128 _mm_cmple_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmple_ps(a, b)); +} + +// Compares for equality. +// https://msdn.microsoft.com/en-us/library/vstudio/36aectz5(v=vs.100).aspx +FORCE_INLINE __m128 _mm_cmpeq_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32( + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Compares for equality. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/k423z28e(v=vs.100) +FORCE_INLINE __m128 _mm_cmpeq_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpeq_ps(a, b)); +} + +// Compares for inequality. +// https://msdn.microsoft.com/en-us/library/sf44thbx(v=vs.100).aspx +FORCE_INLINE __m128 _mm_cmpneq_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32(vmvnq_u32( + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)))); +} + +// Compares for inequality. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/ekya8fh4(v=vs.100) +FORCE_INLINE __m128 _mm_cmpneq_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpneq_ps(a, b)); +} + +// Compares for not greater than or equal. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/wsexys62(v=vs.100) +FORCE_INLINE __m128 _mm_cmpnge_ps(__m128 a, __m128 b) +{ + return _mm_cmplt_ps(a, b); +} + +// Compares for not greater than or equal. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/fk2y80s8(v=vs.100) +FORCE_INLINE __m128 _mm_cmpnge_ss(__m128 a, __m128 b) +{ + return _mm_cmplt_ss(a, b); +} + +// Compares for not greater than. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/d0xh7w0s(v=vs.100) +FORCE_INLINE __m128 _mm_cmpngt_ps(__m128 a, __m128 b) +{ + return _mm_cmple_ps(a, b); +} + +// Compares for not greater than. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/z7x9ydwh(v=vs.100) +FORCE_INLINE __m128 _mm_cmpngt_ss(__m128 a, __m128 b) +{ + return _mm_cmple_ss(a, b); +} + +// Compares for not less than or equal. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/6a330kxw(v=vs.100) +FORCE_INLINE __m128 _mm_cmpnle_ps(__m128 a, __m128 b) +{ + return _mm_cmpgt_ps(a, b); +} + +// Compares for not less than or equal. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/z7x9ydwh(v=vs.100) +FORCE_INLINE __m128 _mm_cmpnle_ss(__m128 a, __m128 b) +{ + return _mm_cmpgt_ss(a, b); +} + +// Compares for not less than. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/4686bbdw(v=vs.100) +FORCE_INLINE __m128 _mm_cmpnlt_ps(__m128 a, __m128 b) +{ + return _mm_cmpge_ps(a, b); +} + +// Compares for not less than. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/56b9z2wf(v=vs.100) +FORCE_INLINE __m128 _mm_cmpnlt_ss(__m128 a, __m128 b) +{ + return _mm_cmpge_ss(a, b); +} + +// Compares the 16 signed or unsigned 8-bit integers in a and the 16 signed or +// unsigned 8-bit integers in b for equality. +// https://msdn.microsoft.com/en-us/library/windows/desktop/bz5xk21a(v=vs.90).aspx +FORCE_INLINE __m128i _mm_cmpeq_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vceqq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Compares the 8 signed or unsigned 16-bit integers in a and the 8 signed or +// unsigned 16-bit integers in b for equality. +// https://msdn.microsoft.com/en-us/library/2ay060te(v=vs.100).aspx +FORCE_INLINE __m128i _mm_cmpeq_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vceqq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Compare packed 32-bit integers in a and b for equality, and store the results +// in dst +FORCE_INLINE __m128i _mm_cmpeq_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u32( + vceqq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Compare packed 64-bit integers in a and b for equality, and store the results +// in dst +FORCE_INLINE __m128i _mm_cmpeq_epi64(__m128i a, __m128i b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128i_u64( + vceqq_u64(vreinterpretq_u64_m128i(a), vreinterpretq_u64_m128i(b))); +#else + // ARMv7 lacks vceqq_u64 + // (a == b) -> (a_lo == b_lo) && (a_hi == b_hi) + uint32x4_t cmp = + vceqq_u32(vreinterpretq_u32_m128i(a), vreinterpretq_u32_m128i(b)); + uint32x4_t swapped = vrev64q_u32(cmp); + return vreinterpretq_m128i_u32(vandq_u32(cmp, swapped)); +#endif +} + +// Compares the 16 signed 8-bit integers in a and the 16 signed 8-bit integers +// in b for lesser than. +// https://msdn.microsoft.com/en-us/library/windows/desktop/9s46csht(v=vs.90).aspx +FORCE_INLINE __m128i _mm_cmplt_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vcltq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Compares the 16 signed 8-bit integers in a and the 16 signed 8-bit integers +// in b for greater than. +// +// r0 := (a0 > b0) ? 0xff : 0x0 +// r1 := (a1 > b1) ? 0xff : 0x0 +// ... +// r15 := (a15 > b15) ? 0xff : 0x0 +// +// https://msdn.microsoft.com/zh-tw/library/wf45zt2b(v=vs.100).aspx +FORCE_INLINE __m128i _mm_cmpgt_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vcgtq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Compares the 8 signed 16-bit integers in a and the 8 signed 16-bit integers +// in b for less than. +// +// r0 := (a0 < b0) ? 0xffff : 0x0 +// r1 := (a1 < b1) ? 0xffff : 0x0 +// ... +// r7 := (a7 < b7) ? 0xffff : 0x0 +// +// https://technet.microsoft.com/en-us/library/t863edb2(v=vs.100).aspx +FORCE_INLINE __m128i _mm_cmplt_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vcltq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Compares the 8 signed 16-bit integers in a and the 8 signed 16-bit integers +// in b for greater than. +// +// r0 := (a0 > b0) ? 0xffff : 0x0 +// r1 := (a1 > b1) ? 0xffff : 0x0 +// ... +// r7 := (a7 > b7) ? 0xffff : 0x0 +// +// https://technet.microsoft.com/en-us/library/xd43yfsa(v=vs.100).aspx +FORCE_INLINE __m128i _mm_cmpgt_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vcgtq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + + +// Compares the 4 signed 32-bit integers in a and the 4 signed 32-bit integers +// in b for less than. +// https://msdn.microsoft.com/en-us/library/vstudio/4ak0bf5d(v=vs.100).aspx +FORCE_INLINE __m128i _mm_cmplt_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u32( + vcltq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Compares the 4 signed 32-bit integers in a and the 4 signed 32-bit integers +// in b for greater than. +// https://msdn.microsoft.com/en-us/library/vstudio/1s9f2z0y(v=vs.100).aspx +FORCE_INLINE __m128i _mm_cmpgt_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u32( + vcgtq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Compares the 2 signed 64-bit integers in a and the 2 signed 64-bit integers +// in b for greater than. +FORCE_INLINE __m128i _mm_cmpgt_epi64(__m128i a, __m128i b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128i_u64( + vcgtq_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(b))); +#else + // ARMv7 lacks vcgtq_s64. + // This is based off of Clang's SSE2 polyfill: + // (a > b) -> ((a_hi > b_hi) || (a_lo > b_lo && a_hi == b_hi)) + + // Mask the sign bit out since we need a signed AND an unsigned comparison + // and it is ugly to try and split them. + int32x4_t mask = vreinterpretq_s32_s64(vdupq_n_s64(0x80000000ull)); + int32x4_t a_mask = veorq_s32(vreinterpretq_s32_m128i(a), mask); + int32x4_t b_mask = veorq_s32(vreinterpretq_s32_m128i(b), mask); + // Check if a > b + int64x2_t greater = vreinterpretq_s64_u32(vcgtq_s32(a_mask, b_mask)); + // Copy upper mask to lower mask + // a_hi > b_hi + int64x2_t gt_hi = vshrq_n_s64(greater, 63); + // Copy lower mask to upper mask + // a_lo > b_lo + int64x2_t gt_lo = vsliq_n_s64(greater, greater, 32); + // Compare for equality + int64x2_t equal = vreinterpretq_s64_u32(vceqq_s32(a_mask, b_mask)); + // Copy upper mask to lower mask + // a_hi == b_hi + int64x2_t eq_hi = vshrq_n_s64(equal, 63); + // a_hi > b_hi || (a_lo > b_lo && a_hi == b_hi) + int64x2_t ret = vorrq_s64(gt_hi, vandq_s64(gt_lo, eq_hi)); + return vreinterpretq_m128i_s64(ret); +#endif +} + +// Compares the four 32-bit floats in a and b to check if any values are NaN. +// Ordered compare between each value returns true for "orderable" and false for +// "not orderable" (NaN). +// https://msdn.microsoft.com/en-us/library/vstudio/0h9w00fx(v=vs.100).aspx see +// also: +// http://stackoverflow.com/questions/8627331/what-does-ordered-unordered-comparison-mean +// http://stackoverflow.com/questions/29349621/neon-isnanval-intrinsics +FORCE_INLINE __m128 _mm_cmpord_ps(__m128 a, __m128 b) +{ + // Note: NEON does not have ordered compare builtin + // Need to compare a eq a and b eq b to check for NaN + // Do AND of results to get final + uint32x4_t ceqaa = + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a)); + uint32x4_t ceqbb = + vceqq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_u32(vandq_u32(ceqaa, ceqbb)); +} + +// Compares for ordered. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/343t62da(v=vs.100) +FORCE_INLINE __m128 _mm_cmpord_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpord_ps(a, b)); +} + +// Compares for unordered. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/khy6fk1t(v=vs.100) +FORCE_INLINE __m128 _mm_cmpunord_ps(__m128 a, __m128 b) +{ + uint32x4_t f32a = + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a)); + uint32x4_t f32b = + vceqq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_u32(vmvnq_u32(vandq_u32(f32a, f32b))); +} + +// Compares for unordered. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/2as2387b(v=vs.100) +FORCE_INLINE __m128 _mm_cmpunord_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpunord_ps(a, b)); +} + +// Compares the lower single-precision floating point scalar values of a and b +// using a less than operation. : +// https://msdn.microsoft.com/en-us/library/2kwe606b(v=vs.90).aspx Important +// note!! The documentation on MSDN is incorrect! If either of the values is a +// NAN the docs say you will get a one, but in fact, it will return a zero!! +FORCE_INLINE int _mm_comilt_ss(__m128 a, __m128 b) +{ + uint32x4_t a_not_nan = + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a)); + uint32x4_t b_not_nan = + vceqq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b)); + uint32x4_t a_and_b_not_nan = vandq_u32(a_not_nan, b_not_nan); + uint32x4_t a_lt_b = + vcltq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)); + return (vgetq_lane_u32(vandq_u32(a_and_b_not_nan, a_lt_b), 0) != 0) ? 1 : 0; +} + +// Compares the lower single-precision floating point scalar values of a and b +// using a greater than operation. : +// https://msdn.microsoft.com/en-us/library/b0738e0t(v=vs.100).aspx +FORCE_INLINE int _mm_comigt_ss(__m128 a, __m128 b) +{ + // return vgetq_lane_u32(vcgtq_f32(vreinterpretq_f32_m128(a), + // vreinterpretq_f32_m128(b)), 0); + uint32x4_t a_not_nan = + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a)); + uint32x4_t b_not_nan = + vceqq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b)); + uint32x4_t a_and_b_not_nan = vandq_u32(a_not_nan, b_not_nan); + uint32x4_t a_gt_b = + vcgtq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)); + return (vgetq_lane_u32(vandq_u32(a_and_b_not_nan, a_gt_b), 0) != 0) ? 1 : 0; +} + +// Compares the lower single-precision floating point scalar values of a and b +// using a less than or equal operation. : +// https://msdn.microsoft.com/en-us/library/1w4t7c57(v=vs.90).aspx +FORCE_INLINE int _mm_comile_ss(__m128 a, __m128 b) +{ + // return vgetq_lane_u32(vcleq_f32(vreinterpretq_f32_m128(a), + // vreinterpretq_f32_m128(b)), 0); + uint32x4_t a_not_nan = + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a)); + uint32x4_t b_not_nan = + vceqq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b)); + uint32x4_t a_and_b_not_nan = vandq_u32(a_not_nan, b_not_nan); + uint32x4_t a_le_b = + vcleq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)); + return (vgetq_lane_u32(vandq_u32(a_and_b_not_nan, a_le_b), 0) != 0) ? 1 : 0; +} + +// Compares the lower single-precision floating point scalar values of a and b +// using a greater than or equal operation. : +// https://msdn.microsoft.com/en-us/library/8t80des6(v=vs.100).aspx +FORCE_INLINE int _mm_comige_ss(__m128 a, __m128 b) +{ + // return vgetq_lane_u32(vcgeq_f32(vreinterpretq_f32_m128(a), + // vreinterpretq_f32_m128(b)), 0); + uint32x4_t a_not_nan = + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a)); + uint32x4_t b_not_nan = + vceqq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b)); + uint32x4_t a_and_b_not_nan = vandq_u32(a_not_nan, b_not_nan); + uint32x4_t a_ge_b = + vcgeq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)); + return (vgetq_lane_u32(vandq_u32(a_and_b_not_nan, a_ge_b), 0) != 0) ? 1 : 0; +} + +// Compares the lower single-precision floating point scalar values of a and b +// using an equality operation. : +// https://msdn.microsoft.com/en-us/library/93yx2h2b(v=vs.100).aspx +FORCE_INLINE int _mm_comieq_ss(__m128 a, __m128 b) +{ + // return vgetq_lane_u32(vceqq_f32(vreinterpretq_f32_m128(a), + // vreinterpretq_f32_m128(b)), 0); + uint32x4_t a_not_nan = + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a)); + uint32x4_t b_not_nan = + vceqq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b)); + uint32x4_t a_and_b_not_nan = vandq_u32(a_not_nan, b_not_nan); + uint32x4_t a_eq_b = + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)); + return (vgetq_lane_u32(vandq_u32(a_and_b_not_nan, a_eq_b), 0) != 0) ? 1 : 0; +} + +// Compares the lower single-precision floating point scalar values of a and b +// using an inequality operation. : +// https://msdn.microsoft.com/en-us/library/bafh5e0a(v=vs.90).aspx +FORCE_INLINE int _mm_comineq_ss(__m128 a, __m128 b) +{ + // return !vgetq_lane_u32(vceqq_f32(vreinterpretq_f32_m128(a), + // vreinterpretq_f32_m128(b)), 0); + uint32x4_t a_not_nan = + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a)); + uint32x4_t b_not_nan = + vceqq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b)); + uint32x4_t a_or_b_nan = vmvnq_u32(vandq_u32(a_not_nan, b_not_nan)); + uint32x4_t a_neq_b = vmvnq_u32( + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); + return (vgetq_lane_u32(vorrq_u32(a_or_b_nan, a_neq_b), 0) != 0) ? 1 : 0; +} + +// according to the documentation, these intrinsics behave the same as the +// non-'u' versions. We'll just alias them here. +#define _mm_ucomilt_ss _mm_comilt_ss +#define _mm_ucomile_ss _mm_comile_ss +#define _mm_ucomigt_ss _mm_comigt_ss +#define _mm_ucomige_ss _mm_comige_ss +#define _mm_ucomieq_ss _mm_comieq_ss +#define _mm_ucomineq_ss _mm_comineq_ss + +/* Conversions */ + +// Convert packed signed 32-bit integers in b to packed single-precision +// (32-bit) floating-point elements, store the results in the lower 2 elements +// of dst, and copy the upper 2 packed elements from a to the upper elements of +// dst. +// +// dst[31:0] := Convert_Int32_To_FP32(b[31:0]) +// dst[63:32] := Convert_Int32_To_FP32(b[63:32]) +// dst[95:64] := a[95:64] +// dst[127:96] := a[127:96] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvt_pi2ps +FORCE_INLINE __m128 _mm_cvt_pi2ps(__m128 a, __m64 b) +{ + return vreinterpretq_m128_f32( + vcombine_f32(vcvt_f32_s32(vreinterpret_s32_m64(b)), + vget_high_f32(vreinterpretq_f32_m128(a)))); +} + +// Convert the signed 32-bit integer b to a single-precision (32-bit) +// floating-point element, store the result in the lower element of dst, and +// copy the upper 3 packed elements from a to the upper elements of dst. +// +// dst[31:0] := Convert_Int32_To_FP32(b[31:0]) +// dst[127:32] := a[127:32] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvt_si2ss +FORCE_INLINE __m128 _mm_cvt_si2ss(__m128 a, int b) +{ + return vreinterpretq_m128_f32( + vsetq_lane_f32((float) b, vreinterpretq_f32_m128(a), 0)); +} + +// Convert the signed 32-bit integer b to a single-precision (32-bit) +// floating-point element, store the result in the lower element of dst, and +// copy the upper 3 packed elements from a to the upper elements of dst. +// +// dst[31:0] := Convert_Int32_To_FP32(b[31:0]) +// dst[127:32] := a[127:32] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsi32_ss +#define _mm_cvtsi32_ss(a, b) _mm_cvt_si2ss(a, b) + +// Convert the signed 64-bit integer b to a single-precision (32-bit) +// floating-point element, store the result in the lower element of dst, and +// copy the upper 3 packed elements from a to the upper elements of dst. +// +// dst[31:0] := Convert_Int64_To_FP32(b[63:0]) +// dst[127:32] := a[127:32] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsi64_ss +FORCE_INLINE __m128 _mm_cvtsi64_ss(__m128 a, int64_t b) +{ + return vreinterpretq_m128_f32( + vsetq_lane_f32((float) b, vreinterpretq_f32_m128(a), 0)); +} + +// Convert the lower single-precision (32-bit) floating-point element in a to a +// 32-bit integer, and store the result in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvt_ss2si +FORCE_INLINE int _mm_cvt_ss2si(__m128 a) +{ +#if defined(__aarch64__) + return vgetq_lane_s32(vcvtnq_s32_f32(vreinterpretq_f32_m128(a)), 0); +#else + float32_t data = vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); + float32_t diff = data - floor(data); + if (diff > 0.5) + return (int32_t) ceil(data); + if (diff == 0.5) { + int32_t f = (int32_t) floor(data); + int32_t c = (int32_t) ceil(data); + return c & 1 ? f : c; + } + return (int32_t) floor(data); +#endif +} + +// Convert packed 16-bit integers in a to packed single-precision (32-bit) +// floating-point elements, and store the results in dst. +// +// FOR j := 0 to 3 +// i := j*16 +// m := j*32 +// dst[m+31:m] := Convert_Int16_To_FP32(a[i+15:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtpi16_ps +FORCE_INLINE __m128 _mm_cvtpi16_ps(__m64 a) +{ + return vreinterpretq_m128_f32( + vcvtq_f32_s32(vmovl_s16(vreinterpret_s16_m64(a)))); +} + +// Convert packed 32-bit integers in b to packed single-precision (32-bit) +// floating-point elements, store the results in the lower 2 elements of dst, +// and copy the upper 2 packed elements from a to the upper elements of dst. +// +// dst[31:0] := Convert_Int32_To_FP32(b[31:0]) +// dst[63:32] := Convert_Int32_To_FP32(b[63:32]) +// dst[95:64] := a[95:64] +// dst[127:96] := a[127:96] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtpi32_ps +FORCE_INLINE __m128 _mm_cvtpi32_ps(__m128 a, __m64 b) +{ + return vreinterpretq_m128_f32( + vcombine_f32(vcvt_f32_s32(vreinterpret_s32_m64(b)), + vget_high_f32(vreinterpretq_f32_m128(a)))); +} + +// Convert packed signed 32-bit integers in a to packed single-precision +// (32-bit) floating-point elements, store the results in the lower 2 elements +// of dst, then covert the packed signed 32-bit integers in b to +// single-precision (32-bit) floating-point element, and store the results in +// the upper 2 elements of dst. +// +// dst[31:0] := Convert_Int32_To_FP32(a[31:0]) +// dst[63:32] := Convert_Int32_To_FP32(a[63:32]) +// dst[95:64] := Convert_Int32_To_FP32(b[31:0]) +// dst[127:96] := Convert_Int32_To_FP32(b[63:32]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtpi32x2_ps +FORCE_INLINE __m128 _mm_cvtpi32x2_ps(__m64 a, __m64 b) +{ + return vreinterpretq_m128_f32(vcvtq_f32_s32( + vcombine_s32(vreinterpret_s32_m64(a), vreinterpret_s32_m64(b)))); +} + +// Convert the lower packed 8-bit integers in a to packed single-precision +// (32-bit) floating-point elements, and store the results in dst. +// +// FOR j := 0 to 3 +// i := j*8 +// m := j*32 +// dst[m+31:m] := Convert_Int8_To_FP32(a[i+7:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtpi8_ps +FORCE_INLINE __m128 _mm_cvtpi8_ps(__m64 a) +{ + return vreinterpretq_m128_f32(vcvtq_f32_s32( + vmovl_s16(vget_low_s16(vmovl_s8(vreinterpret_s8_m64(a)))))); +} + +// Convert packed unsigned 16-bit integers in a to packed single-precision +// (32-bit) floating-point elements, and store the results in dst. +// +// FOR j := 0 to 3 +// i := j*16 +// m := j*32 +// dst[m+31:m] := Convert_UInt16_To_FP32(a[i+15:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtpu16_ps +FORCE_INLINE __m128 _mm_cvtpu16_ps(__m64 a) +{ + return vreinterpretq_m128_f32( + vcvtq_f32_u32(vmovl_u16(vreinterpret_u16_m64(a)))); +} + +// Convert the lower packed unsigned 8-bit integers in a to packed +// single-precision (32-bit) floating-point elements, and store the results in +// dst. +// +// FOR j := 0 to 3 +// i := j*8 +// m := j*32 +// dst[m+31:m] := Convert_UInt8_To_FP32(a[i+7:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtpu8_ps +FORCE_INLINE __m128 _mm_cvtpu8_ps(__m64 a) +{ + return vreinterpretq_m128_f32(vcvtq_f32_u32( + vmovl_u16(vget_low_u16(vmovl_u8(vreinterpret_u8_m64(a)))))); +} + +// Converts the four single-precision, floating-point values of a to signed +// 32-bit integer values using truncate. +// https://msdn.microsoft.com/en-us/library/vstudio/1h005y6x(v=vs.100).aspx +FORCE_INLINE __m128i _mm_cvttps_epi32(__m128 a) +{ + return vreinterpretq_m128i_s32(vcvtq_s32_f32(vreinterpretq_f32_m128(a))); +} + +// Convert the lower double-precision (64-bit) floating-point element in a to a +// 64-bit integer with truncation, and store the result in dst. +// +// dst[63:0] := Convert_FP64_To_Int64_Truncate(a[63:0]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvttsd_si64 +FORCE_INLINE int64_t _mm_cvttsd_si64(__m128d a) +{ +#if defined(__aarch64__) + return vgetq_lane_s64(vcvtq_s64_f64(vreinterpretq_f64_m128d(a)), 0); +#else + double ret = *((double *) &a); + return (int64_t) ret; +#endif +} + +// Convert the lower double-precision (64-bit) floating-point element in a to a +// 64-bit integer with truncation, and store the result in dst. +// +// dst[63:0] := Convert_FP64_To_Int64_Truncate(a[63:0]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvttsd_si64x +#define _mm_cvttsd_si64x(a) _mm_cvttsd_si64(a) + +// Converts the four signed 32-bit integer values of a to single-precision, +// floating-point values +// https://msdn.microsoft.com/en-us/library/vstudio/36bwxcx5(v=vs.100).aspx +FORCE_INLINE __m128 _mm_cvtepi32_ps(__m128i a) +{ + return vreinterpretq_m128_f32(vcvtq_f32_s32(vreinterpretq_s32_m128i(a))); +} + +// Converts the four unsigned 8-bit integers in the lower 16 bits to four +// unsigned 32-bit integers. +FORCE_INLINE __m128i _mm_cvtepu8_epi16(__m128i a) +{ + uint8x16_t u8x16 = vreinterpretq_u8_m128i(a); /* xxxx xxxx xxxx DCBA */ + uint16x8_t u16x8 = vmovl_u8(vget_low_u8(u8x16)); /* 0x0x 0x0x 0D0C 0B0A */ + return vreinterpretq_m128i_u16(u16x8); +} + +// Converts the four unsigned 8-bit integers in the lower 32 bits to four +// unsigned 32-bit integers. +// https://msdn.microsoft.com/en-us/library/bb531467%28v=vs.100%29.aspx +FORCE_INLINE __m128i _mm_cvtepu8_epi32(__m128i a) +{ + uint8x16_t u8x16 = vreinterpretq_u8_m128i(a); /* xxxx xxxx xxxx DCBA */ + uint16x8_t u16x8 = vmovl_u8(vget_low_u8(u8x16)); /* 0x0x 0x0x 0D0C 0B0A */ + uint32x4_t u32x4 = vmovl_u16(vget_low_u16(u16x8)); /* 000D 000C 000B 000A */ + return vreinterpretq_m128i_u32(u32x4); +} + +// Converts the two unsigned 8-bit integers in the lower 16 bits to two +// unsigned 64-bit integers. +FORCE_INLINE __m128i _mm_cvtepu8_epi64(__m128i a) +{ + uint8x16_t u8x16 = vreinterpretq_u8_m128i(a); /* xxxx xxxx xxxx xxBA */ + uint16x8_t u16x8 = vmovl_u8(vget_low_u8(u8x16)); /* 0x0x 0x0x 0x0x 0B0A */ + uint32x4_t u32x4 = vmovl_u16(vget_low_u16(u16x8)); /* 000x 000x 000B 000A */ + uint64x2_t u64x2 = vmovl_u32(vget_low_u32(u32x4)); /* 0000 000B 0000 000A */ + return vreinterpretq_m128i_u64(u64x2); +} + +// Converts the four unsigned 8-bit integers in the lower 16 bits to four +// unsigned 32-bit integers. +FORCE_INLINE __m128i _mm_cvtepi8_epi16(__m128i a) +{ + int8x16_t s8x16 = vreinterpretq_s8_m128i(a); /* xxxx xxxx xxxx DCBA */ + int16x8_t s16x8 = vmovl_s8(vget_low_s8(s8x16)); /* 0x0x 0x0x 0D0C 0B0A */ + return vreinterpretq_m128i_s16(s16x8); +} + +// Converts the four unsigned 8-bit integers in the lower 32 bits to four +// unsigned 32-bit integers. +FORCE_INLINE __m128i _mm_cvtepi8_epi32(__m128i a) +{ + int8x16_t s8x16 = vreinterpretq_s8_m128i(a); /* xxxx xxxx xxxx DCBA */ + int16x8_t s16x8 = vmovl_s8(vget_low_s8(s8x16)); /* 0x0x 0x0x 0D0C 0B0A */ + int32x4_t s32x4 = vmovl_s16(vget_low_s16(s16x8)); /* 000D 000C 000B 000A */ + return vreinterpretq_m128i_s32(s32x4); +} + +// Converts the two signed 8-bit integers in the lower 32 bits to four +// signed 64-bit integers. +FORCE_INLINE __m128i _mm_cvtepi8_epi64(__m128i a) +{ + int8x16_t s8x16 = vreinterpretq_s8_m128i(a); /* xxxx xxxx xxxx xxBA */ + int16x8_t s16x8 = vmovl_s8(vget_low_s8(s8x16)); /* 0x0x 0x0x 0x0x 0B0A */ + int32x4_t s32x4 = vmovl_s16(vget_low_s16(s16x8)); /* 000x 000x 000B 000A */ + int64x2_t s64x2 = vmovl_s32(vget_low_s32(s32x4)); /* 0000 000B 0000 000A */ + return vreinterpretq_m128i_s64(s64x2); +} + +// Converts the four signed 16-bit integers in the lower 64 bits to four signed +// 32-bit integers. +FORCE_INLINE __m128i _mm_cvtepi16_epi32(__m128i a) +{ + return vreinterpretq_m128i_s32( + vmovl_s16(vget_low_s16(vreinterpretq_s16_m128i(a)))); +} + +// Converts the two signed 16-bit integers in the lower 32 bits two signed +// 32-bit integers. +FORCE_INLINE __m128i _mm_cvtepi16_epi64(__m128i a) +{ + int16x8_t s16x8 = vreinterpretq_s16_m128i(a); /* xxxx xxxx xxxx 0B0A */ + int32x4_t s32x4 = vmovl_s16(vget_low_s16(s16x8)); /* 000x 000x 000B 000A */ + int64x2_t s64x2 = vmovl_s32(vget_low_s32(s32x4)); /* 0000 000B 0000 000A */ + return vreinterpretq_m128i_s64(s64x2); +} + +// Converts the four unsigned 16-bit integers in the lower 64 bits to four +// unsigned 32-bit integers. +FORCE_INLINE __m128i _mm_cvtepu16_epi32(__m128i a) +{ + return vreinterpretq_m128i_u32( + vmovl_u16(vget_low_u16(vreinterpretq_u16_m128i(a)))); +} + +// Converts the two unsigned 16-bit integers in the lower 32 bits to two +// unsigned 64-bit integers. +FORCE_INLINE __m128i _mm_cvtepu16_epi64(__m128i a) +{ + uint16x8_t u16x8 = vreinterpretq_u16_m128i(a); /* xxxx xxxx xxxx 0B0A */ + uint32x4_t u32x4 = vmovl_u16(vget_low_u16(u16x8)); /* 000x 000x 000B 000A */ + uint64x2_t u64x2 = vmovl_u32(vget_low_u32(u32x4)); /* 0000 000B 0000 000A */ + return vreinterpretq_m128i_u64(u64x2); +} + +// Converts the two unsigned 32-bit integers in the lower 64 bits to two +// unsigned 64-bit integers. +FORCE_INLINE __m128i _mm_cvtepu32_epi64(__m128i a) +{ + return vreinterpretq_m128i_u64( + vmovl_u32(vget_low_u32(vreinterpretq_u32_m128i(a)))); +} + +// Converts the two signed 32-bit integers in the lower 64 bits to two signed +// 64-bit integers. +FORCE_INLINE __m128i _mm_cvtepi32_epi64(__m128i a) +{ + return vreinterpretq_m128i_s64( + vmovl_s32(vget_low_s32(vreinterpretq_s32_m128i(a)))); +} + +// Converts the four single-precision, floating-point values of a to signed +// 32-bit integer values. +// +// r0 := (int) a0 +// r1 := (int) a1 +// r2 := (int) a2 +// r3 := (int) a3 +// +// https://msdn.microsoft.com/en-us/library/vstudio/xdc42k5e(v=vs.100).aspx +// *NOTE*. The default rounding mode on SSE is 'round to even', which ARMv7-A +// does not support! It is supported on ARMv8-A however. +FORCE_INLINE __m128i _mm_cvtps_epi32(__m128 a) +{ +#if defined(__aarch64__) + return vreinterpretq_m128i_s32(vcvtnq_s32_f32(a)); +#else + uint32x4_t signmask = vdupq_n_u32(0x80000000); + float32x4_t half = vbslq_f32(signmask, vreinterpretq_f32_m128(a), + vdupq_n_f32(0.5f)); /* +/- 0.5 */ + int32x4_t r_normal = vcvtq_s32_f32(vaddq_f32( + vreinterpretq_f32_m128(a), half)); /* round to integer: [a + 0.5]*/ + int32x4_t r_trunc = + vcvtq_s32_f32(vreinterpretq_f32_m128(a)); /* truncate to integer: [a] */ + int32x4_t plusone = vreinterpretq_s32_u32(vshrq_n_u32( + vreinterpretq_u32_s32(vnegq_s32(r_trunc)), 31)); /* 1 or 0 */ + int32x4_t r_even = vbicq_s32(vaddq_s32(r_trunc, plusone), + vdupq_n_s32(1)); /* ([a] + {0,1}) & ~1 */ + float32x4_t delta = vsubq_f32( + vreinterpretq_f32_m128(a), + vcvtq_f32_s32(r_trunc)); /* compute delta: delta = (a - [a]) */ + uint32x4_t is_delta_half = vceqq_f32(delta, half); /* delta == +/- 0.5 */ + return vreinterpretq_m128i_s32(vbslq_s32(is_delta_half, r_even, r_normal)); +#endif +} + +// Copy the lower 32-bit integer in a to dst. +// +// dst[31:0] := a[31:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsi128_si32 +FORCE_INLINE int _mm_cvtsi128_si32(__m128i a) +{ + return vgetq_lane_s32(vreinterpretq_s32_m128i(a), 0); +} + +// Copy the lower 64-bit integer in a to dst. +// +// dst[63:0] := a[63:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsi128_si64 +FORCE_INLINE int64_t _mm_cvtsi128_si64(__m128i a) +{ + return vgetq_lane_s64(vreinterpretq_s64_m128i(a), 0); +} + +// Copy the lower 64-bit integer in a to dst. +// +// dst[63:0] := a[63:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsi128_si64x +#define _mm_cvtsi128_si64x(a) _mm_cvtsi128_si64(a) + +// Moves 32-bit integer a to the least significant 32 bits of an __m128 object, +// zero extending the upper bits. +// +// r0 := a +// r1 := 0x0 +// r2 := 0x0 +// r3 := 0x0 +// +// https://msdn.microsoft.com/en-us/library/ct3539ha%28v=vs.90%29.aspx +FORCE_INLINE __m128i _mm_cvtsi32_si128(int a) +{ + return vreinterpretq_m128i_s32(vsetq_lane_s32(a, vdupq_n_s32(0), 0)); +} + +// Moves 64-bit integer a to the least significant 64 bits of an __m128 object, +// zero extending the upper bits. +// +// r0 := a +// r1 := 0x0 +FORCE_INLINE __m128i _mm_cvtsi64_si128(int64_t a) +{ + return vreinterpretq_m128i_s64(vsetq_lane_s64(a, vdupq_n_s64(0), 0)); +} + +// Cast vector of type __m128 to type __m128d. This intrinsic is only used for +// compilation and does not generate any instructions, thus it has zero latency. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_castps_pd +FORCE_INLINE __m128d _mm_castps_pd(__m128 a) +{ + return vreinterpretq_m128d_s32(vreinterpretq_s32_m128(a)); +} + +// Applies a type cast to reinterpret four 32-bit floating point values passed +// in as a 128-bit parameter as packed 32-bit integers. +// https://msdn.microsoft.com/en-us/library/bb514099.aspx +FORCE_INLINE __m128i _mm_castps_si128(__m128 a) +{ + return vreinterpretq_m128i_s32(vreinterpretq_s32_m128(a)); +} + +// Applies a type cast to reinterpret four 32-bit integers passed in as a +// 128-bit parameter as packed 32-bit floating point values. +// https://msdn.microsoft.com/en-us/library/bb514029.aspx +FORCE_INLINE __m128 _mm_castsi128_ps(__m128i a) +{ + return vreinterpretq_m128_s32(vreinterpretq_s32_m128i(a)); +} + +// Loads 128-bit value. : +// https://msdn.microsoft.com/en-us/library/atzzad1h(v=vs.80).aspx +FORCE_INLINE __m128i _mm_load_si128(const __m128i *p) +{ + return vreinterpretq_m128i_s32(vld1q_s32((const int32_t *) p)); +} + +// Load a double-precision (64-bit) floating-point element from memory into both +// elements of dst. +// +// dst[63:0] := MEM[mem_addr+63:mem_addr] +// dst[127:64] := MEM[mem_addr+63:mem_addr] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_load1_pd +FORCE_INLINE __m128d _mm_load1_pd(const double *p) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64(vld1q_dup_f64(p)); +#else + return vreinterpretq_m128d_s64(vdupq_n_s64(*(const int64_t *) p)); +#endif +} + +// Load a double-precision (64-bit) floating-point element from memory into the +// upper element of dst, and copy the lower element from a to dst. mem_addr does +// not need to be aligned on any particular boundary. +// +// dst[63:0] := a[63:0] +// dst[127:64] := MEM[mem_addr+63:mem_addr] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_loadh_pd +FORCE_INLINE __m128d _mm_loadh_pd(__m128d a, const double *p) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64( + vcombine_f64(vget_low_f64(vreinterpretq_f64_m128d(a)), vld1_f64(p))); +#else + return vreinterpretq_m128d_f32(vcombine_f32( + vget_low_f32(vreinterpretq_f32_m128d(a)), vld1_f32((const float *) p))); +#endif +} + +// Load a double-precision (64-bit) floating-point element from memory into both +// elements of dst. +// +// dst[63:0] := MEM[mem_addr+63:mem_addr] +// dst[127:64] := MEM[mem_addr+63:mem_addr] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_load_pd1 +#define _mm_load_pd1 _mm_load1_pd + +// Load a double-precision (64-bit) floating-point element from memory into both +// elements of dst. +// +// dst[63:0] := MEM[mem_addr+63:mem_addr] +// dst[127:64] := MEM[mem_addr+63:mem_addr] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_loaddup_pd +#define _mm_loaddup_pd _mm_load1_pd + +// Loads 128-bit value. : +// https://msdn.microsoft.com/zh-cn/library/f4k12ae8(v=vs.90).aspx +FORCE_INLINE __m128i _mm_loadu_si128(const __m128i *p) +{ + return vreinterpretq_m128i_s32(vld1q_s32((const int32_t *) p)); +} + +// Load unaligned 32-bit integer from memory into the first element of dst. +// +// dst[31:0] := MEM[mem_addr+31:mem_addr] +// dst[MAX:32] := 0 +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_loadu_si32 +FORCE_INLINE __m128i _mm_loadu_si32(const void *p) +{ + return vreinterpretq_m128i_s32( + vsetq_lane_s32(*(const int32_t *) p, vdupq_n_s32(0), 0)); +} + +// Convert packed double-precision (64-bit) floating-point elements in a to +// packed single-precision (32-bit) floating-point elements, and store the +// results in dst. +// +// FOR j := 0 to 1 +// i := 32*j +// k := 64*j +// dst[i+31:i] := Convert_FP64_To_FP32(a[k+64:k]) +// ENDFOR +// dst[127:64] := 0 +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtpd_ps +FORCE_INLINE __m128 _mm_cvtpd_ps(__m128d a) +{ +#if defined(__aarch64__) + float32x2_t tmp = vcvt_f32_f64(vreinterpretq_f64_m128d(a)); + return vreinterpretq_m128_f32(vcombine_f32(tmp, vdup_n_f32(0))); +#else + float a0 = (float) ((double *) &a)[0]; + float a1 = (float) ((double *) &a)[1]; + return _mm_set_ps(0, 0, a1, a0); +#endif +} + +// Copy the lower double-precision (64-bit) floating-point element of a to dst. +// +// dst[63:0] := a[63:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsd_f64 +FORCE_INLINE double _mm_cvtsd_f64(__m128d a) +{ +#if defined(__aarch64__) + return (double) vgetq_lane_f64(vreinterpretq_f64_m128d(a), 0); +#else + return ((double *) &a)[0]; +#endif +} + +// Convert packed single-precision (32-bit) floating-point elements in a to +// packed double-precision (64-bit) floating-point elements, and store the +// results in dst. +// +// FOR j := 0 to 1 +// i := 64*j +// k := 32*j +// dst[i+63:i] := Convert_FP32_To_FP64(a[k+31:k]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtps_pd +FORCE_INLINE __m128d _mm_cvtps_pd(__m128 a) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64( + vcvt_f64_f32(vget_low_f32(vreinterpretq_f32_m128(a)))); +#else + double a0 = (double) vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); + double a1 = (double) vgetq_lane_f32(vreinterpretq_f32_m128(a), 1); + return _mm_set_pd(a1, a0); +#endif +} + +// Cast vector of type __m128d to type __m128i. This intrinsic is only used for +// compilation and does not generate any instructions, thus it has zero latency. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_castpd_si128 +FORCE_INLINE __m128i _mm_castpd_si128(__m128d a) +{ + return vreinterpretq_m128i_s64(vreinterpretq_s64_m128d(a)); +} + +// Blend packed single-precision (32-bit) floating-point elements from a and b +// using mask, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_blendv_ps +FORCE_INLINE __m128 _mm_blendv_ps(__m128 a, __m128 b, __m128 mask) +{ + return vreinterpretq_m128_f32(vbslq_f32(vreinterpretq_u32_m128(mask), + vreinterpretq_f32_m128(b), + vreinterpretq_f32_m128(a))); +} + +// Round the packed single-precision (32-bit) floating-point elements in a using +// the rounding parameter, and store the results as packed single-precision +// floating-point elements in dst. +// software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_round_ps +FORCE_INLINE __m128 _mm_round_ps(__m128 a, int rounding) +{ +#if defined(__aarch64__) + switch (rounding) { + case (_MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC): + return vreinterpretq_m128_f32(vrndnq_f32(vreinterpretq_f32_m128(a))); + case (_MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC): + return vreinterpretq_m128_f32(vrndmq_f32(vreinterpretq_f32_m128(a))); + case (_MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC): + return vreinterpretq_m128_f32(vrndpq_f32(vreinterpretq_f32_m128(a))); + case (_MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC): + return vreinterpretq_m128_f32(vrndq_f32(vreinterpretq_f32_m128(a))); + default: //_MM_FROUND_CUR_DIRECTION + return vreinterpretq_m128_f32(vrndiq_f32(vreinterpretq_f32_m128(a))); + } +#else + float *v_float = (float *) &a; + __m128 zero, neg_inf, pos_inf; + + switch (rounding) { + case (_MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC): + return _mm_cvtepi32_ps(_mm_cvtps_epi32(a)); + case (_MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC): + return (__m128){floorf(v_float[0]), floorf(v_float[1]), + floorf(v_float[2]), floorf(v_float[3])}; + case (_MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC): + return (__m128){ceilf(v_float[0]), ceilf(v_float[1]), ceilf(v_float[2]), + ceilf(v_float[3])}; + case (_MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC): + zero = _mm_set_ps(0.0f, 0.0f, 0.0f, 0.0f); + neg_inf = _mm_set_ps(floorf(v_float[0]), floorf(v_float[1]), + floorf(v_float[2]), floorf(v_float[3])); + pos_inf = _mm_set_ps(ceilf(v_float[0]), ceilf(v_float[1]), + ceilf(v_float[2]), ceilf(v_float[3])); + return _mm_blendv_ps(pos_inf, neg_inf, _mm_cmple_ps(a, zero)); + default: //_MM_FROUND_CUR_DIRECTION + return (__m128){roundf(v_float[0]), roundf(v_float[1]), + roundf(v_float[2]), roundf(v_float[3])}; + } +#endif +} + +// Convert packed single-precision (32-bit) floating-point elements in a to +// packed 32-bit integers, and store the results in dst. +// +// FOR j := 0 to 1 +// i := 32*j +// dst[i+31:i] := Convert_FP32_To_Int32(a[i+31:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvt_ps2pi +FORCE_INLINE __m64 _mm_cvt_ps2pi(__m128 a) +{ +#if defined(__aarch64__) + return vreinterpret_m64_s32( + vget_low_s32(vcvtnq_s32_f32(vreinterpretq_f32_m128(a)))); +#else + return vreinterpret_m64_s32( + vcvt_s32_f32(vget_low_f32(vreinterpretq_f32_m128( + _mm_round_ps(a, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC))))); +#endif +} + +// Round the packed single-precision (32-bit) floating-point elements in a up to +// an integer value, and store the results as packed single-precision +// floating-point elements in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_ceil_ps +FORCE_INLINE __m128 _mm_ceil_ps(__m128 a) +{ + return _mm_round_ps(a, _MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC); +} + +// Round the packed single-precision (32-bit) floating-point elements in a down +// to an integer value, and store the results as packed single-precision +// floating-point elements in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_floor_ps +FORCE_INLINE __m128 _mm_floor_ps(__m128 a) +{ + return _mm_round_ps(a, _MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC); +} + + +// Load 128-bits of integer data from unaligned memory into dst. This intrinsic +// may perform better than _mm_loadu_si128 when the data crosses a cache line +// boundary. +// +// dst[127:0] := MEM[mem_addr+127:mem_addr] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_lddqu_si128 +#define _mm_lddqu_si128 _mm_loadu_si128 + +/* Miscellaneous Operations */ + +// Shifts the 8 signed 16-bit integers in a right by count bits while shifting +// in the sign bit. +// +// r0 := a0 >> count +// r1 := a1 >> count +// ... +// r7 := a7 >> count +// +// https://msdn.microsoft.com/en-us/library/3c9997dk(v%3dvs.90).aspx +FORCE_INLINE __m128i _mm_sra_epi16(__m128i a, __m128i count) +{ + int64_t c = (int64_t) vget_low_s64((int64x2_t) count); + if (c > 15) + return _mm_cmplt_epi16(a, _mm_setzero_si128()); + return vreinterpretq_m128i_s16(vshlq_s16((int16x8_t) a, vdupq_n_s16(-c))); +} + +// Shifts the 4 signed 32-bit integers in a right by count bits while shifting +// in the sign bit. +// +// r0 := a0 >> count +// r1 := a1 >> count +// r2 := a2 >> count +// r3 := a3 >> count +// +// https://msdn.microsoft.com/en-us/library/ce40009e(v%3dvs.100).aspx +FORCE_INLINE __m128i _mm_sra_epi32(__m128i a, __m128i count) +{ + int64_t c = (int64_t) vget_low_s64((int64x2_t) count); + if (c > 31) + return _mm_cmplt_epi32(a, _mm_setzero_si128()); + return vreinterpretq_m128i_s32(vshlq_s32((int32x4_t) a, vdupq_n_s32(-c))); +} + +// Packs the 16 signed 16-bit integers from a and b into 8-bit integers and +// saturates. +// https://msdn.microsoft.com/en-us/library/k4y4f7w5%28v=vs.90%29.aspx +FORCE_INLINE __m128i _mm_packs_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s8( + vcombine_s8(vqmovn_s16(vreinterpretq_s16_m128i(a)), + vqmovn_s16(vreinterpretq_s16_m128i(b)))); +} + +// Packs the 16 signed 16 - bit integers from a and b into 8 - bit unsigned +// integers and saturates. +// +// r0 := UnsignedSaturate(a0) +// r1 := UnsignedSaturate(a1) +// ... +// r7 := UnsignedSaturate(a7) +// r8 := UnsignedSaturate(b0) +// r9 := UnsignedSaturate(b1) +// ... +// r15 := UnsignedSaturate(b7) +// +// https://msdn.microsoft.com/en-us/library/07ad1wx4(v=vs.100).aspx +FORCE_INLINE __m128i _mm_packus_epi16(const __m128i a, const __m128i b) +{ + return vreinterpretq_m128i_u8( + vcombine_u8(vqmovun_s16(vreinterpretq_s16_m128i(a)), + vqmovun_s16(vreinterpretq_s16_m128i(b)))); +} + +// Packs the 8 signed 32-bit integers from a and b into signed 16-bit integers +// and saturates. +// +// r0 := SignedSaturate(a0) +// r1 := SignedSaturate(a1) +// r2 := SignedSaturate(a2) +// r3 := SignedSaturate(a3) +// r4 := SignedSaturate(b0) +// r5 := SignedSaturate(b1) +// r6 := SignedSaturate(b2) +// r7 := SignedSaturate(b3) +// +// https://msdn.microsoft.com/en-us/library/393t56f9%28v=vs.90%29.aspx +FORCE_INLINE __m128i _mm_packs_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vcombine_s16(vqmovn_s32(vreinterpretq_s32_m128i(a)), + vqmovn_s32(vreinterpretq_s32_m128i(b)))); +} + +// Packs the 8 unsigned 32-bit integers from a and b into unsigned 16-bit +// integers and saturates. +// +// r0 := UnsignedSaturate(a0) +// r1 := UnsignedSaturate(a1) +// r2 := UnsignedSaturate(a2) +// r3 := UnsignedSaturate(a3) +// r4 := UnsignedSaturate(b0) +// r5 := UnsignedSaturate(b1) +// r6 := UnsignedSaturate(b2) +// r7 := UnsignedSaturate(b3) +FORCE_INLINE __m128i _mm_packus_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vcombine_u16(vqmovun_s32(vreinterpretq_s32_m128i(a)), + vqmovun_s32(vreinterpretq_s32_m128i(b)))); +} + +// Interleaves the lower 8 signed or unsigned 8-bit integers in a with the lower +// 8 signed or unsigned 8-bit integers in b. +// +// r0 := a0 +// r1 := b0 +// r2 := a1 +// r3 := b1 +// ... +// r14 := a7 +// r15 := b7 +// +// https://msdn.microsoft.com/en-us/library/xf7k860c%28v=vs.90%29.aspx +FORCE_INLINE __m128i _mm_unpacklo_epi8(__m128i a, __m128i b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128i_s8( + vzip1q_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +#else + int8x8_t a1 = vreinterpret_s8_s16(vget_low_s16(vreinterpretq_s16_m128i(a))); + int8x8_t b1 = vreinterpret_s8_s16(vget_low_s16(vreinterpretq_s16_m128i(b))); + int8x8x2_t result = vzip_s8(a1, b1); + return vreinterpretq_m128i_s8(vcombine_s8(result.val[0], result.val[1])); +#endif +} + +// Interleaves the lower 4 signed or unsigned 16-bit integers in a with the +// lower 4 signed or unsigned 16-bit integers in b. +// +// r0 := a0 +// r1 := b0 +// r2 := a1 +// r3 := b1 +// r4 := a2 +// r5 := b2 +// r6 := a3 +// r7 := b3 +// +// https://msdn.microsoft.com/en-us/library/btxb17bw%28v=vs.90%29.aspx +FORCE_INLINE __m128i _mm_unpacklo_epi16(__m128i a, __m128i b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128i_s16( + vzip1q_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +#else + int16x4_t a1 = vget_low_s16(vreinterpretq_s16_m128i(a)); + int16x4_t b1 = vget_low_s16(vreinterpretq_s16_m128i(b)); + int16x4x2_t result = vzip_s16(a1, b1); + return vreinterpretq_m128i_s16(vcombine_s16(result.val[0], result.val[1])); +#endif +} + +// Interleaves the lower 2 signed or unsigned 32 - bit integers in a with the +// lower 2 signed or unsigned 32 - bit integers in b. +// +// r0 := a0 +// r1 := b0 +// r2 := a1 +// r3 := b1 +// +// https://msdn.microsoft.com/en-us/library/x8atst9d(v=vs.100).aspx +FORCE_INLINE __m128i _mm_unpacklo_epi32(__m128i a, __m128i b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128i_s32( + vzip1q_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +#else + int32x2_t a1 = vget_low_s32(vreinterpretq_s32_m128i(a)); + int32x2_t b1 = vget_low_s32(vreinterpretq_s32_m128i(b)); + int32x2x2_t result = vzip_s32(a1, b1); + return vreinterpretq_m128i_s32(vcombine_s32(result.val[0], result.val[1])); +#endif +} + +FORCE_INLINE __m128i _mm_unpacklo_epi64(__m128i a, __m128i b) +{ + int64x1_t a_l = vget_low_s64(vreinterpretq_s64_m128i(a)); + int64x1_t b_l = vget_low_s64(vreinterpretq_s64_m128i(b)); + return vreinterpretq_m128i_s64(vcombine_s64(a_l, b_l)); +} + +// Selects and interleaves the lower two single-precision, floating-point values +// from a and b. +// +// r0 := a0 +// r1 := b0 +// r2 := a1 +// r3 := b1 +// +// https://msdn.microsoft.com/en-us/library/25st103b%28v=vs.90%29.aspx +FORCE_INLINE __m128 _mm_unpacklo_ps(__m128 a, __m128 b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128_f32( + vzip1q_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +#else + float32x2_t a1 = vget_low_f32(vreinterpretq_f32_m128(a)); + float32x2_t b1 = vget_low_f32(vreinterpretq_f32_m128(b)); + float32x2x2_t result = vzip_f32(a1, b1); + return vreinterpretq_m128_f32(vcombine_f32(result.val[0], result.val[1])); +#endif +} + +// Selects and interleaves the upper two single-precision, floating-point values +// from a and b. +// +// r0 := a2 +// r1 := b2 +// r2 := a3 +// r3 := b3 +// +// https://msdn.microsoft.com/en-us/library/skccxx7d%28v=vs.90%29.aspx +FORCE_INLINE __m128 _mm_unpackhi_ps(__m128 a, __m128 b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128_f32( + vzip2q_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +#else + float32x2_t a1 = vget_high_f32(vreinterpretq_f32_m128(a)); + float32x2_t b1 = vget_high_f32(vreinterpretq_f32_m128(b)); + float32x2x2_t result = vzip_f32(a1, b1); + return vreinterpretq_m128_f32(vcombine_f32(result.val[0], result.val[1])); +#endif +} + +// Interleaves the upper 8 signed or unsigned 8-bit integers in a with the upper +// 8 signed or unsigned 8-bit integers in b. +// +// r0 := a8 +// r1 := b8 +// r2 := a9 +// r3 := b9 +// ... +// r14 := a15 +// r15 := b15 +// +// https://msdn.microsoft.com/en-us/library/t5h7783k(v=vs.100).aspx +FORCE_INLINE __m128i _mm_unpackhi_epi8(__m128i a, __m128i b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128i_s8( + vzip2q_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +#else + int8x8_t a1 = + vreinterpret_s8_s16(vget_high_s16(vreinterpretq_s16_m128i(a))); + int8x8_t b1 = + vreinterpret_s8_s16(vget_high_s16(vreinterpretq_s16_m128i(b))); + int8x8x2_t result = vzip_s8(a1, b1); + return vreinterpretq_m128i_s8(vcombine_s8(result.val[0], result.val[1])); +#endif +} + +// Interleaves the upper 4 signed or unsigned 16-bit integers in a with the +// upper 4 signed or unsigned 16-bit integers in b. +// +// r0 := a4 +// r1 := b4 +// r2 := a5 +// r3 := b5 +// r4 := a6 +// r5 := b6 +// r6 := a7 +// r7 := b7 +// +// https://msdn.microsoft.com/en-us/library/03196cz7(v=vs.100).aspx +FORCE_INLINE __m128i _mm_unpackhi_epi16(__m128i a, __m128i b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128i_s16( + vzip2q_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +#else + int16x4_t a1 = vget_high_s16(vreinterpretq_s16_m128i(a)); + int16x4_t b1 = vget_high_s16(vreinterpretq_s16_m128i(b)); + int16x4x2_t result = vzip_s16(a1, b1); + return vreinterpretq_m128i_s16(vcombine_s16(result.val[0], result.val[1])); +#endif +} + +// Interleaves the upper 2 signed or unsigned 32-bit integers in a with the +// upper 2 signed or unsigned 32-bit integers in b. +// https://msdn.microsoft.com/en-us/library/65sa7cbs(v=vs.100).aspx +FORCE_INLINE __m128i _mm_unpackhi_epi32(__m128i a, __m128i b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128i_s32( + vzip2q_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +#else + int32x2_t a1 = vget_high_s32(vreinterpretq_s32_m128i(a)); + int32x2_t b1 = vget_high_s32(vreinterpretq_s32_m128i(b)); + int32x2x2_t result = vzip_s32(a1, b1); + return vreinterpretq_m128i_s32(vcombine_s32(result.val[0], result.val[1])); +#endif +} + +// Interleaves the upper signed or unsigned 64-bit integer in a with the +// upper signed or unsigned 64-bit integer in b. +// +// r0 := a1 +// r1 := b1 +FORCE_INLINE __m128i _mm_unpackhi_epi64(__m128i a, __m128i b) +{ + int64x1_t a_h = vget_high_s64(vreinterpretq_s64_m128i(a)); + int64x1_t b_h = vget_high_s64(vreinterpretq_s64_m128i(b)); + return vreinterpretq_m128i_s64(vcombine_s64(a_h, b_h)); +} + +// Horizontally compute the minimum amongst the packed unsigned 16-bit integers +// in a, store the minimum and index in dst, and zero the remaining bits in dst. +// +// index[2:0] := 0 +// min[15:0] := a[15:0] +// FOR j := 0 to 7 +// i := j*16 +// IF a[i+15:i] < min[15:0] +// index[2:0] := j +// min[15:0] := a[i+15:i] +// FI +// ENDFOR +// dst[15:0] := min[15:0] +// dst[18:16] := index[2:0] +// dst[127:19] := 0 +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_minpos_epu16 +FORCE_INLINE __m128i _mm_minpos_epu16(__m128i a) +{ + __m128i dst; + uint16_t min, idx = 0; + // Find the minimum value +#if defined(__aarch64__) + min = vminvq_u16(vreinterpretq_u16_m128i(a)); +#else + __m64 tmp; + tmp = vreinterpret_m64_u16( + vmin_u16(vget_low_u16(vreinterpretq_u16_m128i(a)), + vget_high_u16(vreinterpretq_u16_m128i(a)))); + tmp = vreinterpret_m64_u16( + vpmin_u16(vreinterpret_u16_m64(tmp), vreinterpret_u16_m64(tmp))); + tmp = vreinterpret_m64_u16( + vpmin_u16(vreinterpret_u16_m64(tmp), vreinterpret_u16_m64(tmp))); + min = vget_lane_u16(vreinterpret_u16_m64(tmp), 0); +#endif + // Get the index of the minimum value + int i; + for (i = 0; i < 8; i++) { + if (min == vgetq_lane_u16(vreinterpretq_u16_m128i(a), 0)) { + idx = (uint16_t) i; + break; + } + a = _mm_srli_si128(a, 2); + } + // Generate result + dst = _mm_setzero_si128(); + dst = vreinterpretq_m128i_u16( + vsetq_lane_u16(min, vreinterpretq_u16_m128i(dst), 0)); + dst = vreinterpretq_m128i_u16( + vsetq_lane_u16(idx, vreinterpretq_u16_m128i(dst), 1)); + return dst; +} + +// shift to right +// https://msdn.microsoft.com/en-us/library/bb514041(v=vs.120).aspx +// http://blog.csdn.net/hemmingway/article/details/44828303 +// Clang requires a macro here, as it is extremely picky about c being a +// literal. +#define _mm_alignr_epi8(a, b, c) \ + ((__m128i) vextq_s8((int8x16_t)(b), (int8x16_t)(a), (c))) + +// Compute the bitwise AND of 128 bits (representing integer data) in a and b, +// and set ZF to 1 if the result is zero, otherwise set ZF to 0. Compute the +// bitwise NOT of a and then AND with b, and set CF to 1 if the result is zero, +// otherwise set CF to 0. Return the CF value. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_testc_si128 +FORCE_INLINE int _mm_testc_si128(__m128i a, __m128i b) +{ + int64x2_t s64 = + vandq_s64(vreinterpretq_s64_s32(vmvnq_s32(vreinterpretq_s32_m128i(a))), + vreinterpretq_s64_m128i(b)); + return !(vgetq_lane_s64(s64, 0) | vgetq_lane_s64(s64, 1)); +} + +// Compute the bitwise AND of 128 bits (representing integer data) in a and b, +// and set ZF to 1 if the result is zero, otherwise set ZF to 0. Compute the +// bitwise NOT of a and then AND with b, and set CF to 1 if the result is zero, +// otherwise set CF to 0. Return the ZF value. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_testz_si128 +FORCE_INLINE int _mm_testz_si128(__m128i a, __m128i b) +{ + int64x2_t s64 = + vandq_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(b)); + return !(vgetq_lane_s64(s64, 0) | vgetq_lane_s64(s64, 1)); +} + +// Extracts the selected signed or unsigned 8-bit integer from a and zero +// extends. +// FORCE_INLINE int _mm_extract_epi8(__m128i a, __constrange(0,16) int imm) +#define _mm_extract_epi8(a, imm) vgetq_lane_u8(vreinterpretq_u8_m128i(a), (imm)) + +// Inserts the least significant 8 bits of b into the selected 8-bit integer +// of a. +// FORCE_INLINE __m128i _mm_insert_epi8(__m128i a, int b, +// __constrange(0,16) int imm) +#define _mm_insert_epi8(a, b, imm) \ + __extension__({ \ + vreinterpretq_m128i_s8( \ + vsetq_lane_s8((b), vreinterpretq_s8_m128i(a), (imm))); \ + }) + +// Extracts the selected signed or unsigned 16-bit integer from a and zero +// extends. +// https://msdn.microsoft.com/en-us/library/6dceta0c(v=vs.100).aspx +// FORCE_INLINE int _mm_extract_epi16(__m128i a, __constrange(0,8) int imm) +#define _mm_extract_epi16(a, imm) \ + vgetq_lane_u16(vreinterpretq_u16_m128i(a), (imm)) + +// Inserts the least significant 16 bits of b into the selected 16-bit integer +// of a. +// https://msdn.microsoft.com/en-us/library/kaze8hz1%28v=vs.100%29.aspx +// FORCE_INLINE __m128i _mm_insert_epi16(__m128i a, int b, +// __constrange(0,8) int imm) +#define _mm_insert_epi16(a, b, imm) \ + __extension__({ \ + vreinterpretq_m128i_s16( \ + vsetq_lane_s16((b), vreinterpretq_s16_m128i(a), (imm))); \ + }) + +// Extracts the selected signed or unsigned 32-bit integer from a and zero +// extends. +// FORCE_INLINE int _mm_extract_epi32(__m128i a, __constrange(0,4) int imm) +#define _mm_extract_epi32(a, imm) \ + vgetq_lane_s32(vreinterpretq_s32_m128i(a), (imm)) + +// Extracts the selected single-precision (32-bit) floating-point from a. +// FORCE_INLINE int _mm_extract_ps(__m128 a, __constrange(0,4) int imm) +#define _mm_extract_ps(a, imm) vgetq_lane_s32(vreinterpretq_s32_m128(a), (imm)) + +// Inserts the least significant 32 bits of b into the selected 32-bit integer +// of a. +// FORCE_INLINE __m128i _mm_insert_epi32(__m128i a, int b, +// __constrange(0,4) int imm) +#define _mm_insert_epi32(a, b, imm) \ + __extension__({ \ + vreinterpretq_m128i_s32( \ + vsetq_lane_s32((b), vreinterpretq_s32_m128i(a), (imm))); \ + }) + +// Extracts the selected signed or unsigned 64-bit integer from a and zero +// extends. +// FORCE_INLINE __int64 _mm_extract_epi64(__m128i a, __constrange(0,2) int imm) +#define _mm_extract_epi64(a, imm) \ + vgetq_lane_s64(vreinterpretq_s64_m128i(a), (imm)) + +// Inserts the least significant 64 bits of b into the selected 64-bit integer +// of a. +// FORCE_INLINE __m128i _mm_insert_epi64(__m128i a, __int64 b, +// __constrange(0,2) int imm) +#define _mm_insert_epi64(a, b, imm) \ + __extension__({ \ + vreinterpretq_m128i_s64( \ + vsetq_lane_s64((b), vreinterpretq_s64_m128i(a), (imm))); \ + }) + +// Count the number of bits set to 1 in unsigned 32-bit integer a, and +// return that count in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_popcnt_u32 +FORCE_INLINE int _mm_popcnt_u32(unsigned int a) +{ +#if defined(__aarch64__) +#if __has_builtin(__builtin_popcount) + return __builtin_popcount(a); +#else + return (int) vaddlv_u8(vcnt_u8(vcreate_u8((uint64_t) a))); +#endif +#else + uint32_t count = 0; + uint8x8_t input_val, count8x8_val; + uint16x4_t count16x4_val; + uint32x2_t count32x2_val; + + input_val = vld1_u8((uint8_t *) &a); + count8x8_val = vcnt_u8(input_val); + count16x4_val = vpaddl_u8(count8x8_val); + count32x2_val = vpaddl_u16(count16x4_val); + + vst1_u32(&count, count32x2_val); + return count; +#endif +} + +// Count the number of bits set to 1 in unsigned 64-bit integer a, and +// return that count in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_popcnt_u64 +FORCE_INLINE int64_t _mm_popcnt_u64(uint64_t a) +{ +#if defined(__aarch64__) +#if __has_builtin(__builtin_popcountll) + return __builtin_popcountll(a); +#else + return (int64_t) vaddlv_u8(vcnt_u8(vcreate_u8(a))); +#endif +#else + uint64_t count = 0; + uint8x8_t input_val, count8x8_val; + uint16x4_t count16x4_val; + uint32x2_t count32x2_val; + uint64x1_t count64x1_val; + + input_val = vld1_u8((uint8_t *) &a); + count8x8_val = vcnt_u8(input_val); + count16x4_val = vpaddl_u8(count8x8_val); + count32x2_val = vpaddl_u16(count16x4_val); + count64x1_val = vpaddl_u32(count32x2_val); + vst1_u64(&count, count64x1_val); + return count; +#endif +} + +// Macro: Transpose the 4x4 matrix formed by the 4 rows of single-precision +// (32-bit) floating-point elements in row0, row1, row2, and row3, and store the +// transposed matrix in these vectors (row0 now contains column 0, etc.). +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=MM_TRANSPOSE4_PS +#define _MM_TRANSPOSE4_PS(row0, row1, row2, row3) \ + do { \ + float32x4x2_t ROW01 = vtrnq_f32(row0, row1); \ + float32x4x2_t ROW23 = vtrnq_f32(row2, row3); \ + row0 = vcombine_f32(vget_low_f32(ROW01.val[0]), \ + vget_low_f32(ROW23.val[0])); \ + row1 = vcombine_f32(vget_low_f32(ROW01.val[1]), \ + vget_low_f32(ROW23.val[1])); \ + row2 = vcombine_f32(vget_high_f32(ROW01.val[0]), \ + vget_high_f32(ROW23.val[0])); \ + row3 = vcombine_f32(vget_high_f32(ROW01.val[1]), \ + vget_high_f32(ROW23.val[1])); \ + } while (0) + +/* Crypto Extensions */ + +#if defined(__ARM_FEATURE_CRYPTO) +// Wraps vmull_p64 +FORCE_INLINE uint64x2_t _sse2neon_vmull_p64(uint64x1_t _a, uint64x1_t _b) +{ + poly64_t a = vget_lane_p64(vreinterpret_p64_u64(_a), 0); + poly64_t b = vget_lane_p64(vreinterpret_p64_u64(_b), 0); + return vreinterpretq_u64_p128(vmull_p64(a, b)); +} +#else // ARMv7 polyfill +// ARMv7/some A64 lacks vmull_p64, but it has vmull_p8. +// +// vmull_p8 calculates 8 8-bit->16-bit polynomial multiplies, but we need a +// 64-bit->128-bit polynomial multiply. +// +// It needs some work and is somewhat slow, but it is still faster than all +// known scalar methods. +// +// Algorithm adapted to C from +// https://www.workofard.com/2017/07/ghash-for-low-end-cores/, which is adapted +// from "Fast Software Polynomial Multiplication on ARM Processors Using the +// NEON Engine" by Danilo Camara, Conrado Gouvea, Julio Lopez and Ricardo Dahab +// (https://hal.inria.fr/hal-01506572) +static uint64x2_t _sse2neon_vmull_p64(uint64x1_t _a, uint64x1_t _b) +{ + poly8x8_t a = vreinterpret_p8_u64(_a); + poly8x8_t b = vreinterpret_p8_u64(_b); + + // Masks + uint8x16_t k48_32 = vcombine_u8(vcreate_u8(0x0000ffffffffffff), + vcreate_u8(0x00000000ffffffff)); + uint8x16_t k16_00 = vcombine_u8(vcreate_u8(0x000000000000ffff), + vcreate_u8(0x0000000000000000)); + + // Do the multiplies, rotating with vext to get all combinations + uint8x16_t d = vreinterpretq_u8_p16(vmull_p8(a, b)); // D = A0 * B0 + uint8x16_t e = + vreinterpretq_u8_p16(vmull_p8(a, vext_p8(b, b, 1))); // E = A0 * B1 + uint8x16_t f = + vreinterpretq_u8_p16(vmull_p8(vext_p8(a, a, 1), b)); // F = A1 * B0 + uint8x16_t g = + vreinterpretq_u8_p16(vmull_p8(a, vext_p8(b, b, 2))); // G = A0 * B2 + uint8x16_t h = + vreinterpretq_u8_p16(vmull_p8(vext_p8(a, a, 2), b)); // H = A2 * B0 + uint8x16_t i = + vreinterpretq_u8_p16(vmull_p8(a, vext_p8(b, b, 3))); // I = A0 * B3 + uint8x16_t j = + vreinterpretq_u8_p16(vmull_p8(vext_p8(a, a, 3), b)); // J = A3 * B0 + uint8x16_t k = + vreinterpretq_u8_p16(vmull_p8(a, vext_p8(b, b, 4))); // L = A0 * B4 + + // Add cross products + uint8x16_t l = veorq_u8(e, f); // L = E + F + uint8x16_t m = veorq_u8(g, h); // M = G + H + uint8x16_t n = veorq_u8(i, j); // N = I + J + + // Interleave. Using vzip1 and vzip2 prevents Clang from emitting TBL + // instructions. +#if defined(__aarch64__) + uint8x16_t lm_p0 = vreinterpretq_u8_u64( + vzip1q_u64(vreinterpretq_u64_u8(l), vreinterpretq_u64_u8(m))); + uint8x16_t lm_p1 = vreinterpretq_u8_u64( + vzip2q_u64(vreinterpretq_u64_u8(l), vreinterpretq_u64_u8(m))); + uint8x16_t nk_p0 = vreinterpretq_u8_u64( + vzip1q_u64(vreinterpretq_u64_u8(n), vreinterpretq_u64_u8(k))); + uint8x16_t nk_p1 = vreinterpretq_u8_u64( + vzip2q_u64(vreinterpretq_u64_u8(n), vreinterpretq_u64_u8(k))); +#else + uint8x16_t lm_p0 = vcombine_u8(vget_low_u8(l), vget_low_u8(m)); + uint8x16_t lm_p1 = vcombine_u8(vget_high_u8(l), vget_high_u8(m)); + uint8x16_t nk_p0 = vcombine_u8(vget_low_u8(n), vget_low_u8(k)); + uint8x16_t nk_p1 = vcombine_u8(vget_high_u8(n), vget_high_u8(k)); +#endif + // t0 = (L) (P0 + P1) << 8 + // t1 = (M) (P2 + P3) << 16 + uint8x16_t t0t1_tmp = veorq_u8(lm_p0, lm_p1); + uint8x16_t t0t1_h = vandq_u8(lm_p1, k48_32); + uint8x16_t t0t1_l = veorq_u8(t0t1_tmp, t0t1_h); + + // t2 = (N) (P4 + P5) << 24 + // t3 = (K) (P6 + P7) << 32 + uint8x16_t t2t3_tmp = veorq_u8(nk_p0, nk_p1); + uint8x16_t t2t3_h = vandq_u8(nk_p1, k16_00); + uint8x16_t t2t3_l = veorq_u8(t2t3_tmp, t2t3_h); + + // De-interleave +#if defined(__aarch64__) + uint8x16_t t0 = vreinterpretq_u8_u64( + vuzp1q_u64(vreinterpretq_u64_u8(t0t1_l), vreinterpretq_u64_u8(t0t1_h))); + uint8x16_t t1 = vreinterpretq_u8_u64( + vuzp2q_u64(vreinterpretq_u64_u8(t0t1_l), vreinterpretq_u64_u8(t0t1_h))); + uint8x16_t t2 = vreinterpretq_u8_u64( + vuzp1q_u64(vreinterpretq_u64_u8(t2t3_l), vreinterpretq_u64_u8(t2t3_h))); + uint8x16_t t3 = vreinterpretq_u8_u64( + vuzp2q_u64(vreinterpretq_u64_u8(t2t3_l), vreinterpretq_u64_u8(t2t3_h))); +#else + uint8x16_t t1 = vcombine_u8(vget_high_u8(t0t1_l), vget_high_u8(t0t1_h)); + uint8x16_t t0 = vcombine_u8(vget_low_u8(t0t1_l), vget_low_u8(t0t1_h)); + uint8x16_t t3 = vcombine_u8(vget_high_u8(t2t3_l), vget_high_u8(t2t3_h)); + uint8x16_t t2 = vcombine_u8(vget_low_u8(t2t3_l), vget_low_u8(t2t3_h)); +#endif + // Shift the cross products + uint8x16_t t0_shift = vextq_u8(t0, t0, 15); // t0 << 8 + uint8x16_t t1_shift = vextq_u8(t1, t1, 14); // t1 << 16 + uint8x16_t t2_shift = vextq_u8(t2, t2, 13); // t2 << 24 + uint8x16_t t3_shift = vextq_u8(t3, t3, 12); // t3 << 32 + + // Accumulate the products + uint8x16_t cross1 = veorq_u8(t0_shift, t1_shift); + uint8x16_t cross2 = veorq_u8(t2_shift, t3_shift); + uint8x16_t mix = veorq_u8(d, cross1); + uint8x16_t r = veorq_u8(mix, cross2); + return vreinterpretq_u64_u8(r); +} +#endif // ARMv7 polyfill + +FORCE_INLINE __m128i _mm_clmulepi64_si128(__m128i _a, __m128i _b, const int imm) +{ + uint64x2_t a = vreinterpretq_u64_m128i(_a); + uint64x2_t b = vreinterpretq_u64_m128i(_b); + switch (imm & 0x11) { + case 0x00: + return vreinterpretq_m128i_u64( + _sse2neon_vmull_p64(vget_low_u64(a), vget_low_u64(b))); + case 0x01: + return vreinterpretq_m128i_u64( + _sse2neon_vmull_p64(vget_high_u64(a), vget_low_u64(b))); + case 0x10: + return vreinterpretq_m128i_u64( + _sse2neon_vmull_p64(vget_low_u64(a), vget_high_u64(b))); + case 0x11: + return vreinterpretq_m128i_u64( + _sse2neon_vmull_p64(vget_high_u64(a), vget_high_u64(b))); + default: + abort(); + } +} + +#if !defined(__ARM_FEATURE_CRYPTO) +/* clang-format off */ +#define SSE2NEON_AES_DATA(w) \ + { \ + w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), \ + w(0xc5), w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), \ + w(0xab), w(0x76), w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), \ + w(0x59), w(0x47), w(0xf0), w(0xad), w(0xd4), w(0xa2), w(0xaf), \ + w(0x9c), w(0xa4), w(0x72), w(0xc0), w(0xb7), w(0xfd), w(0x93), \ + w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc), w(0x34), w(0xa5), \ + w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15), w(0x04), \ + w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a), \ + w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), \ + w(0x75), w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), \ + w(0x5a), w(0xa0), w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), \ + w(0xe3), w(0x2f), w(0x84), w(0x53), w(0xd1), w(0x00), w(0xed), \ + w(0x20), w(0xfc), w(0xb1), w(0x5b), w(0x6a), w(0xcb), w(0xbe), \ + w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf), w(0xd0), w(0xef), \ + w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85), w(0x45), \ + w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8), \ + w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), \ + w(0xf5), w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), \ + w(0xf3), w(0xd2), w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), \ + w(0x97), w(0x44), w(0x17), w(0xc4), w(0xa7), w(0x7e), w(0x3d), \ + w(0x64), w(0x5d), w(0x19), w(0x73), w(0x60), w(0x81), w(0x4f), \ + w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88), w(0x46), w(0xee), \ + w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb), w(0xe0), \ + w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c), \ + w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), \ + w(0x79), w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), \ + w(0x4e), w(0xa9), w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), \ + w(0x7a), w(0xae), w(0x08), w(0xba), w(0x78), w(0x25), w(0x2e), \ + w(0x1c), w(0xa6), w(0xb4), w(0xc6), w(0xe8), w(0xdd), w(0x74), \ + w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a), w(0x70), w(0x3e), \ + w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e), w(0x61), \ + w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e), \ + w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), \ + w(0x94), w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), \ + w(0x28), w(0xdf), w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), \ + w(0xe6), w(0x42), w(0x68), w(0x41), w(0x99), w(0x2d), w(0x0f), \ + w(0xb0), w(0x54), w(0xbb), w(0x16) \ + } +/* clang-format on */ + +/* X Macro trick. See https://en.wikipedia.org/wiki/X_Macro */ +#define SSE2NEON_AES_H0(x) (x) +static const uint8_t SSE2NEON_sbox[256] = SSE2NEON_AES_DATA(SSE2NEON_AES_H0); +#undef SSE2NEON_AES_H0 + +// In the absence of crypto extensions, implement aesenc using regular neon +// intrinsics instead. See: +// https://www.workofard.com/2017/01/accelerated-aes-for-the-arm64-linux-kernel/ +// https://www.workofard.com/2017/07/ghash-for-low-end-cores/ and +// https://github.com/ColinIanKing/linux-next-mirror/blob/b5f466091e130caaf0735976648f72bd5e09aa84/crypto/aegis128-neon-inner.c#L52 +// for more information Reproduced with permission of the author. +FORCE_INLINE __m128i _mm_aesenc_si128(__m128i EncBlock, __m128i RoundKey) +{ +#if defined(__aarch64__) + static const uint8_t shift_rows[] = {0x0, 0x5, 0xa, 0xf, 0x4, 0x9, + 0xe, 0x3, 0x8, 0xd, 0x2, 0x7, + 0xc, 0x1, 0x6, 0xb}; + static const uint8_t ror32by8[] = {0x1, 0x2, 0x3, 0x0, 0x5, 0x6, 0x7, 0x4, + 0x9, 0xa, 0xb, 0x8, 0xd, 0xe, 0xf, 0xc}; + + uint8x16_t v; + uint8x16_t w = vreinterpretq_u8_m128i(EncBlock); + + // shift rows + w = vqtbl1q_u8(w, vld1q_u8(shift_rows)); + + // sub bytes + v = vqtbl4q_u8(vld1q_u8_x4(SSE2NEON_sbox), w); + v = vqtbx4q_u8(v, vld1q_u8_x4(SSE2NEON_sbox + 0x40), w - 0x40); + v = vqtbx4q_u8(v, vld1q_u8_x4(SSE2NEON_sbox + 0x80), w - 0x80); + v = vqtbx4q_u8(v, vld1q_u8_x4(SSE2NEON_sbox + 0xc0), w - 0xc0); + + // mix columns + w = (v << 1) ^ (uint8x16_t)(((int8x16_t) v >> 7) & 0x1b); + w ^= (uint8x16_t) vrev32q_u16((uint16x8_t) v); + w ^= vqtbl1q_u8(v ^ w, vld1q_u8(ror32by8)); + + // add round key + return vreinterpretq_m128i_u8(w) ^ RoundKey; + +#else /* ARMv7-A NEON implementation */ +#define SSE2NEON_AES_B2W(b0, b1, b2, b3) \ + (((uint32_t)(b3) << 24) | ((uint32_t)(b2) << 16) | ((uint32_t)(b1) << 8) | \ + (b0)) +#define SSE2NEON_AES_F2(x) ((x << 1) ^ (((x >> 7) & 1) * 0x011b /* WPOLY */)) +#define SSE2NEON_AES_F3(x) (SSE2NEON_AES_F2(x) ^ x) +#define SSE2NEON_AES_U0(p) \ + SSE2NEON_AES_B2W(SSE2NEON_AES_F2(p), p, p, SSE2NEON_AES_F3(p)) +#define SSE2NEON_AES_U1(p) \ + SSE2NEON_AES_B2W(SSE2NEON_AES_F3(p), SSE2NEON_AES_F2(p), p, p) +#define SSE2NEON_AES_U2(p) \ + SSE2NEON_AES_B2W(p, SSE2NEON_AES_F3(p), SSE2NEON_AES_F2(p), p) +#define SSE2NEON_AES_U3(p) \ + SSE2NEON_AES_B2W(p, p, SSE2NEON_AES_F3(p), SSE2NEON_AES_F2(p)) + static const uint32_t ALIGN_STRUCT(16) aes_table[4][256] = { + SSE2NEON_AES_DATA(SSE2NEON_AES_U0), + SSE2NEON_AES_DATA(SSE2NEON_AES_U1), + SSE2NEON_AES_DATA(SSE2NEON_AES_U2), + SSE2NEON_AES_DATA(SSE2NEON_AES_U3), + }; +#undef SSE2NEON_AES_B2W +#undef SSE2NEON_AES_F2 +#undef SSE2NEON_AES_F3 +#undef SSE2NEON_AES_U0 +#undef SSE2NEON_AES_U1 +#undef SSE2NEON_AES_U2 +#undef SSE2NEON_AES_U3 + + uint32_t x0 = _mm_cvtsi128_si32(EncBlock); + uint32_t x1 = _mm_cvtsi128_si32(_mm_shuffle_epi32(EncBlock, 0x55)); + uint32_t x2 = _mm_cvtsi128_si32(_mm_shuffle_epi32(EncBlock, 0xAA)); + uint32_t x3 = _mm_cvtsi128_si32(_mm_shuffle_epi32(EncBlock, 0xFF)); + + __m128i out = _mm_set_epi32( + (aes_table[0][x3 & 0xff] ^ aes_table[1][(x0 >> 8) & 0xff] ^ + aes_table[2][(x1 >> 16) & 0xff] ^ aes_table[3][x2 >> 24]), + (aes_table[0][x2 & 0xff] ^ aes_table[1][(x3 >> 8) & 0xff] ^ + aes_table[2][(x0 >> 16) & 0xff] ^ aes_table[3][x1 >> 24]), + (aes_table[0][x1 & 0xff] ^ aes_table[1][(x2 >> 8) & 0xff] ^ + aes_table[2][(x3 >> 16) & 0xff] ^ aes_table[3][x0 >> 24]), + (aes_table[0][x0 & 0xff] ^ aes_table[1][(x1 >> 8) & 0xff] ^ + aes_table[2][(x2 >> 16) & 0xff] ^ aes_table[3][x3 >> 24])); + + return _mm_xor_si128(out, RoundKey); +#endif +} + +FORCE_INLINE __m128i _mm_aesenclast_si128(__m128i a, __m128i RoundKey) +{ + /* FIXME: optimized for NEON */ + uint8_t v[4][4] = { + [0] = {SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 0)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 5)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 10)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 15)]}, + [1] = {SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 4)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 9)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 14)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 3)]}, + [2] = {SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 8)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 13)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 2)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 7)]}, + [3] = {SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 12)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 1)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 6)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 11)]}, + }; + for (int i = 0; i < 16; i++) + vreinterpretq_nth_u8_m128i(a, i) = + v[i / 4][i % 4] ^ vreinterpretq_nth_u8_m128i(RoundKey, i); + return a; +} + +// Emits the Advanced Encryption Standard (AES) instruction aeskeygenassist. +// This instruction generates a round key for AES encryption. See +// https://kazakov.life/2017/11/01/cryptocurrency-mining-on-ios-devices/ +// for details. +// +// https://msdn.microsoft.com/en-us/library/cc714138(v=vs.120).aspx +FORCE_INLINE __m128i _mm_aeskeygenassist_si128(__m128i key, const int rcon) +{ + uint32_t X1 = _mm_cvtsi128_si32(_mm_shuffle_epi32(key, 0x55)); + uint32_t X3 = _mm_cvtsi128_si32(_mm_shuffle_epi32(key, 0xFF)); + for (int i = 0; i < 4; ++i) { + ((uint8_t *) &X1)[i] = SSE2NEON_sbox[((uint8_t *) &X1)[i]]; + ((uint8_t *) &X3)[i] = SSE2NEON_sbox[((uint8_t *) &X3)[i]]; + } + return _mm_set_epi32(((X3 >> 8) | (X3 << 24)) ^ rcon, X3, + ((X1 >> 8) | (X1 << 24)) ^ rcon, X1); +} +#undef SSE2NEON_AES_DATA + +#else /* __ARM_FEATURE_CRYPTO */ +// Implements equivalent of 'aesenc' by combining AESE (with an empty key) and +// AESMC and then manually applying the real key as an xor operation. This +// unfortunately means an additional xor op; the compiler should be able to +// optimize this away for repeated calls however. See +// https://blog.michaelbrase.com/2018/05/08/emulating-x86-aes-intrinsics-on-armv8-a +// for more details. +FORCE_INLINE __m128i _mm_aesenc_si128(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vaesmcq_u8(vaeseq_u8(vreinterpretq_u8_m128i(a), vdupq_n_u8(0))) ^ + vreinterpretq_u8_m128i(b)); +} + +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_aesenclast_si128 +FORCE_INLINE __m128i _mm_aesenclast_si128(__m128i a, __m128i RoundKey) +{ + return _mm_xor_si128(vreinterpretq_m128i_u8(vaeseq_u8( + vreinterpretq_u8_m128i(a), vdupq_n_u8(0))), + RoundKey); +} + +FORCE_INLINE __m128i _mm_aeskeygenassist_si128(__m128i a, const int rcon) +{ + // AESE does ShiftRows and SubBytes on A + uint8x16_t u8 = vaeseq_u8(vreinterpretq_u8_m128i(a), vdupq_n_u8(0)); + + uint8x16_t dest = { + // Undo ShiftRows step from AESE and extract X1 and X3 + u8[0x4], u8[0x1], u8[0xE], u8[0xB], // SubBytes(X1) + u8[0x1], u8[0xE], u8[0xB], u8[0x4], // ROT(SubBytes(X1)) + u8[0xC], u8[0x9], u8[0x6], u8[0x3], // SubBytes(X3) + u8[0x9], u8[0x6], u8[0x3], u8[0xC], // ROT(SubBytes(X3)) + }; + uint32x4_t r = {0, (unsigned) rcon, 0, (unsigned) rcon}; + return vreinterpretq_m128i_u8(dest) ^ vreinterpretq_m128i_u32(r); +} +#endif + +/* Streaming Extensions */ + +// Guarantees that every preceding store is globally visible before any +// subsequent store. +// https://msdn.microsoft.com/en-us/library/5h2w73d1%28v=vs.90%29.aspx +FORCE_INLINE void _mm_sfence(void) +{ + __sync_synchronize(); +} + +// Store 128-bits (composed of 4 packed single-precision (32-bit) floating- +// point elements) from a into memory using a non-temporal memory hint. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_stream_ps +FORCE_INLINE void _mm_stream_ps(float *p, __m128 a) +{ +#if __has_builtin(__builtin_nontemporal_store) + __builtin_nontemporal_store(a, (float32x4_t *) p); +#else + vst1q_f32(p, vreinterpretq_f32_m128(a)); +#endif +} + +// Stores the data in a to the address p without polluting the caches. If the +// cache line containing address p is already in the cache, the cache will be +// updated. +// https://msdn.microsoft.com/en-us/library/ba08y07y%28v=vs.90%29.aspx +FORCE_INLINE void _mm_stream_si128(__m128i *p, __m128i a) +{ +#if __has_builtin(__builtin_nontemporal_store) + __builtin_nontemporal_store(a, p); +#else + vst1q_s64((int64_t *) p, vreinterpretq_s64_m128i(a)); +#endif +} + +// Load 128-bits of integer data from memory into dst using a non-temporal +// memory hint. mem_addr must be aligned on a 16-byte boundary or a +// general-protection exception may be generated. +// +// dst[127:0] := MEM[mem_addr+127:mem_addr] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_stream_load_si128 +FORCE_INLINE __m128i _mm_stream_load_si128(__m128i *p) +{ +#if __has_builtin(__builtin_nontemporal_store) + return __builtin_nontemporal_load(p); +#else + return vreinterpretq_m128i_s64(vld1q_s64((int64_t *) p)); +#endif +} + +// Cache line containing p is flushed and invalidated from all caches in the +// coherency domain. : +// https://msdn.microsoft.com/en-us/library/ba08y07y(v=vs.100).aspx +FORCE_INLINE void _mm_clflush(void const *p) +{ + (void) p; + // no corollary for Neon? +} + +// Allocate aligned blocks of memory. +// https://software.intel.com/en-us/ +// cpp-compiler-developer-guide-and-reference-allocating-and-freeing-aligned-memory-blocks +FORCE_INLINE void *_mm_malloc(size_t size, size_t align) +{ + void *ptr; + if (align == 1) + return malloc(size); + if (align == 2 || (sizeof(void *) == 8 && align == 4)) + align = sizeof(void *); + if (!posix_memalign(&ptr, align, size)) + return ptr; + return NULL; +} + +FORCE_INLINE void _mm_free(void *addr) +{ + free(addr); +} + +// Starting with the initial value in crc, accumulates a CRC32 value for +// unsigned 8-bit integer v. +// https://msdn.microsoft.com/en-us/library/bb514036(v=vs.100) +FORCE_INLINE uint32_t _mm_crc32_u8(uint32_t crc, uint8_t v) +{ +#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) + __asm__ __volatile__("crc32cb %w[c], %w[c], %w[v]\n\t" + : [c] "+r"(crc) + : [v] "r"(v)); +#else + crc ^= v; + for (int bit = 0; bit < 8; bit++) { + if (crc & 1) + crc = (crc >> 1) ^ UINT32_C(0x82f63b78); + else + crc = (crc >> 1); + } +#endif + return crc; +} + +// Starting with the initial value in crc, accumulates a CRC32 value for +// unsigned 16-bit integer v. +// https://msdn.microsoft.com/en-us/library/bb531411(v=vs.100) +FORCE_INLINE uint32_t _mm_crc32_u16(uint32_t crc, uint16_t v) +{ +#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) + __asm__ __volatile__("crc32ch %w[c], %w[c], %w[v]\n\t" + : [c] "+r"(crc) + : [v] "r"(v)); +#else + crc = _mm_crc32_u8(crc, v & 0xff); + crc = _mm_crc32_u8(crc, (v >> 8) & 0xff); +#endif + return crc; +} + +// Starting with the initial value in crc, accumulates a CRC32 value for +// unsigned 32-bit integer v. +// https://msdn.microsoft.com/en-us/library/bb531394(v=vs.100) +FORCE_INLINE uint32_t _mm_crc32_u32(uint32_t crc, uint32_t v) +{ +#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) + __asm__ __volatile__("crc32cw %w[c], %w[c], %w[v]\n\t" + : [c] "+r"(crc) + : [v] "r"(v)); +#else + crc = _mm_crc32_u16(crc, v & 0xffff); + crc = _mm_crc32_u16(crc, (v >> 16) & 0xffff); +#endif + return crc; +} + +// Starting with the initial value in crc, accumulates a CRC32 value for +// unsigned 64-bit integer v. +// https://msdn.microsoft.com/en-us/library/bb514033(v=vs.100) +FORCE_INLINE uint64_t _mm_crc32_u64(uint64_t crc, uint64_t v) +{ +#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) + __asm__ __volatile__("crc32cx %w[c], %w[c], %x[v]\n\t" + : [c] "+r"(crc) + : [v] "r"(v)); +#else + crc = _mm_crc32_u32((uint32_t)(crc), v & 0xffffffff); + crc = _mm_crc32_u32((uint32_t)(crc), (v >> 32) & 0xffffffff); +#endif + return crc; +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma pop_macro("ALIGN_STRUCT") +#pragma pop_macro("FORCE_INLINE") +#endif + +#if defined(__GNUC__) +#pragma GCC pop_options +#endif + +#endif diff --git a/.venv/include/site/python3.11/pygame/mask.h b/.venv/include/site/python3.11/pygame/mask.h new file mode 100644 index 00000000..45ad8c51 --- /dev/null +++ b/.venv/include/site/python3.11/pygame/mask.h @@ -0,0 +1,7 @@ +#ifndef PGMASK_INTERNAL_H +#define PGMASK_INTERNAL_H + +#include "include/pygame_mask.h" +#define PYGAMEAPI_MASK_NUMSLOTS 1 + +#endif /* ~PGMASK_INTERNAL_H */ diff --git a/.venv/include/site/python3.11/pygame/mixer.h b/.venv/include/site/python3.11/pygame/mixer.h new file mode 100644 index 00000000..97f5a0f1 --- /dev/null +++ b/.venv/include/site/python3.11/pygame/mixer.h @@ -0,0 +1,14 @@ +#ifndef MIXER_INTERNAL_H +#define MIXER_INTERNAL_H + +#include + +/* test mixer initializations */ +#define MIXER_INIT_CHECK() \ + if (!SDL_WasInit(SDL_INIT_AUDIO)) \ + return RAISE(pgExc_SDLError, "mixer not initialized") + +#define PYGAMEAPI_MIXER_NUMSLOTS 5 +#include "include/pygame_mixer.h" + +#endif /* ~MIXER_INTERNAL_H */ diff --git a/.venv/include/site/python3.11/pygame/palette.h b/.venv/include/site/python3.11/pygame/palette.h new file mode 100644 index 00000000..1ae4cf6d --- /dev/null +++ b/.venv/include/site/python3.11/pygame/palette.h @@ -0,0 +1,123 @@ +/* + pygame - Python Game Library + Copyright (C) 2000-2001 Pete Shinners + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Pete Shinners + pete@shinners.org +*/ + +#ifndef PALETTE_H +#define PALETTE_H + +#include + +/* SDL 2 does not assign a default palette color scheme to a new 8 bit + * surface. Instead, the palette is set all white. This defines the SDL 1.2 + * default palette. + */ +static const SDL_Color default_palette_colors[] = { + {0, 0, 0, 255}, {0, 0, 85, 255}, {0, 0, 170, 255}, + {0, 0, 255, 255}, {0, 36, 0, 255}, {0, 36, 85, 255}, + {0, 36, 170, 255}, {0, 36, 255, 255}, {0, 73, 0, 255}, + {0, 73, 85, 255}, {0, 73, 170, 255}, {0, 73, 255, 255}, + {0, 109, 0, 255}, {0, 109, 85, 255}, {0, 109, 170, 255}, + {0, 109, 255, 255}, {0, 146, 0, 255}, {0, 146, 85, 255}, + {0, 146, 170, 255}, {0, 146, 255, 255}, {0, 182, 0, 255}, + {0, 182, 85, 255}, {0, 182, 170, 255}, {0, 182, 255, 255}, + {0, 219, 0, 255}, {0, 219, 85, 255}, {0, 219, 170, 255}, + {0, 219, 255, 255}, {0, 255, 0, 255}, {0, 255, 85, 255}, + {0, 255, 170, 255}, {0, 255, 255, 255}, {85, 0, 0, 255}, + {85, 0, 85, 255}, {85, 0, 170, 255}, {85, 0, 255, 255}, + {85, 36, 0, 255}, {85, 36, 85, 255}, {85, 36, 170, 255}, + {85, 36, 255, 255}, {85, 73, 0, 255}, {85, 73, 85, 255}, + {85, 73, 170, 255}, {85, 73, 255, 255}, {85, 109, 0, 255}, + {85, 109, 85, 255}, {85, 109, 170, 255}, {85, 109, 255, 255}, + {85, 146, 0, 255}, {85, 146, 85, 255}, {85, 146, 170, 255}, + {85, 146, 255, 255}, {85, 182, 0, 255}, {85, 182, 85, 255}, + {85, 182, 170, 255}, {85, 182, 255, 255}, {85, 219, 0, 255}, + {85, 219, 85, 255}, {85, 219, 170, 255}, {85, 219, 255, 255}, + {85, 255, 0, 255}, {85, 255, 85, 255}, {85, 255, 170, 255}, + {85, 255, 255, 255}, {170, 0, 0, 255}, {170, 0, 85, 255}, + {170, 0, 170, 255}, {170, 0, 255, 255}, {170, 36, 0, 255}, + {170, 36, 85, 255}, {170, 36, 170, 255}, {170, 36, 255, 255}, + {170, 73, 0, 255}, {170, 73, 85, 255}, {170, 73, 170, 255}, + {170, 73, 255, 255}, {170, 109, 0, 255}, {170, 109, 85, 255}, + {170, 109, 170, 255}, {170, 109, 255, 255}, {170, 146, 0, 255}, + {170, 146, 85, 255}, {170, 146, 170, 255}, {170, 146, 255, 255}, + {170, 182, 0, 255}, {170, 182, 85, 255}, {170, 182, 170, 255}, + {170, 182, 255, 255}, {170, 219, 0, 255}, {170, 219, 85, 255}, + {170, 219, 170, 255}, {170, 219, 255, 255}, {170, 255, 0, 255}, + {170, 255, 85, 255}, {170, 255, 170, 255}, {170, 255, 255, 255}, + {255, 0, 0, 255}, {255, 0, 85, 255}, {255, 0, 170, 255}, + {255, 0, 255, 255}, {255, 36, 0, 255}, {255, 36, 85, 255}, + {255, 36, 170, 255}, {255, 36, 255, 255}, {255, 73, 0, 255}, + {255, 73, 85, 255}, {255, 73, 170, 255}, {255, 73, 255, 255}, + {255, 109, 0, 255}, {255, 109, 85, 255}, {255, 109, 170, 255}, + {255, 109, 255, 255}, {255, 146, 0, 255}, {255, 146, 85, 255}, + {255, 146, 170, 255}, {255, 146, 255, 255}, {255, 182, 0, 255}, + {255, 182, 85, 255}, {255, 182, 170, 255}, {255, 182, 255, 255}, + {255, 219, 0, 255}, {255, 219, 85, 255}, {255, 219, 170, 255}, + {255, 219, 255, 255}, {255, 255, 0, 255}, {255, 255, 85, 255}, + {255, 255, 170, 255}, {255, 255, 255, 255}, {0, 0, 0, 255}, + {0, 0, 85, 255}, {0, 0, 170, 255}, {0, 0, 255, 255}, + {0, 36, 0, 255}, {0, 36, 85, 255}, {0, 36, 170, 255}, + {0, 36, 255, 255}, {0, 73, 0, 255}, {0, 73, 85, 255}, + {0, 73, 170, 255}, {0, 73, 255, 255}, {0, 109, 0, 255}, + {0, 109, 85, 255}, {0, 109, 170, 255}, {0, 109, 255, 255}, + {0, 146, 0, 255}, {0, 146, 85, 255}, {0, 146, 170, 255}, + {0, 146, 255, 255}, {0, 182, 0, 255}, {0, 182, 85, 255}, + {0, 182, 170, 255}, {0, 182, 255, 255}, {0, 219, 0, 255}, + {0, 219, 85, 255}, {0, 219, 170, 255}, {0, 219, 255, 255}, + {0, 255, 0, 255}, {0, 255, 85, 255}, {0, 255, 170, 255}, + {0, 255, 255, 255}, {85, 0, 0, 255}, {85, 0, 85, 255}, + {85, 0, 170, 255}, {85, 0, 255, 255}, {85, 36, 0, 255}, + {85, 36, 85, 255}, {85, 36, 170, 255}, {85, 36, 255, 255}, + {85, 73, 0, 255}, {85, 73, 85, 255}, {85, 73, 170, 255}, + {85, 73, 255, 255}, {85, 109, 0, 255}, {85, 109, 85, 255}, + {85, 109, 170, 255}, {85, 109, 255, 255}, {85, 146, 0, 255}, + {85, 146, 85, 255}, {85, 146, 170, 255}, {85, 146, 255, 255}, + {85, 182, 0, 255}, {85, 182, 85, 255}, {85, 182, 170, 255}, + {85, 182, 255, 255}, {85, 219, 0, 255}, {85, 219, 85, 255}, + {85, 219, 170, 255}, {85, 219, 255, 255}, {85, 255, 0, 255}, + {85, 255, 85, 255}, {85, 255, 170, 255}, {85, 255, 255, 255}, + {170, 0, 0, 255}, {170, 0, 85, 255}, {170, 0, 170, 255}, + {170, 0, 255, 255}, {170, 36, 0, 255}, {170, 36, 85, 255}, + {170, 36, 170, 255}, {170, 36, 255, 255}, {170, 73, 0, 255}, + {170, 73, 85, 255}, {170, 73, 170, 255}, {170, 73, 255, 255}, + {170, 109, 0, 255}, {170, 109, 85, 255}, {170, 109, 170, 255}, + {170, 109, 255, 255}, {170, 146, 0, 255}, {170, 146, 85, 255}, + {170, 146, 170, 255}, {170, 146, 255, 255}, {170, 182, 0, 255}, + {170, 182, 85, 255}, {170, 182, 170, 255}, {170, 182, 255, 255}, + {170, 219, 0, 255}, {170, 219, 85, 255}, {170, 219, 170, 255}, + {170, 219, 255, 255}, {170, 255, 0, 255}, {170, 255, 85, 255}, + {170, 255, 170, 255}, {170, 255, 255, 255}, {255, 0, 0, 255}, + {255, 0, 85, 255}, {255, 0, 170, 255}, {255, 0, 255, 255}, + {255, 36, 0, 255}, {255, 36, 85, 255}, {255, 36, 170, 255}, + {255, 36, 255, 255}, {255, 73, 0, 255}, {255, 73, 85, 255}, + {255, 73, 170, 255}, {255, 73, 255, 255}, {255, 109, 0, 255}, + {255, 109, 85, 255}, {255, 109, 170, 255}, {255, 109, 255, 255}, + {255, 146, 0, 255}, {255, 146, 85, 255}, {255, 146, 170, 255}, + {255, 146, 255, 255}, {255, 182, 0, 255}, {255, 182, 85, 255}, + {255, 182, 170, 255}, {255, 182, 255, 255}, {255, 219, 0, 255}, + {255, 219, 85, 255}, {255, 219, 170, 255}, {255, 219, 255, 255}, + {255, 255, 0, 255}, {255, 255, 85, 255}, {255, 255, 170, 255}, + {255, 255, 255, 255}}; + +static const int default_palette_size = + (int)(sizeof(default_palette_colors) / sizeof(SDL_Color)); + +#endif diff --git a/.venv/include/site/python3.11/pygame/pgarrinter.h b/.venv/include/site/python3.11/pygame/pgarrinter.h new file mode 100644 index 00000000..5ba096be --- /dev/null +++ b/.venv/include/site/python3.11/pygame/pgarrinter.h @@ -0,0 +1,26 @@ +/* array structure interface version 3 declarations */ + +#if !defined(PG_ARRAYINTER_HEADER) +#define PG_ARRAYINTER_HEADER + +static const int PAI_CONTIGUOUS = 0x01; +static const int PAI_FORTRAN = 0x02; +static const int PAI_ALIGNED = 0x100; +static const int PAI_NOTSWAPPED = 0x200; +static const int PAI_WRITEABLE = 0x400; +static const int PAI_ARR_HAS_DESCR = 0x800; + +typedef struct { + int two; /* contains the integer 2 -- simple sanity check */ + int nd; /* number of dimensions */ + char typekind; /* kind in array -- character code of typestr */ + int itemsize; /* size of each element */ + int flags; /* flags indicating how the data should be */ + /* interpreted */ + Py_intptr_t *shape; /* A length-nd array of shape information */ + Py_intptr_t *strides; /* A length-nd array of stride information */ + void *data; /* A pointer to the first element of the array */ + PyObject *descr; /* NULL or a data-description */ +} PyArrayInterface; + +#endif diff --git a/.venv/include/site/python3.11/pygame/pgbufferproxy.h b/.venv/include/site/python3.11/pygame/pgbufferproxy.h new file mode 100644 index 00000000..15076086 --- /dev/null +++ b/.venv/include/site/python3.11/pygame/pgbufferproxy.h @@ -0,0 +1,7 @@ +#ifndef PG_BUFPROXY_INTERNAL_H +#define PG_BUFPROXY_INTERNAL_H + +#include "include/pygame_bufferproxy.h" +#define PYGAMEAPI_BUFPROXY_NUMSLOTS 4 + +#endif /* ~PG_BUFPROXY_INTERNAL_H */ diff --git a/.venv/include/site/python3.11/pygame/pgcompat.h b/.venv/include/site/python3.11/pygame/pgcompat.h new file mode 100644 index 00000000..1bc0d247 --- /dev/null +++ b/.venv/include/site/python3.11/pygame/pgcompat.h @@ -0,0 +1,27 @@ +/* Python 2.x/3.x compatibility tools (internal) + */ +#ifndef PGCOMPAT_INTERNAL_H +#define PGCOMPAT_INTERNAL_H + +#include "include/pgcompat.h" + +/* Module init function returns new module instance. */ +#define MODINIT_DEFINE(mod_name) PyMODINIT_FUNC PyInit_##mod_name(void) + +/* Defaults for unicode file path encoding */ +#if defined(MS_WIN32) +#define UNICODE_DEF_FS_ERROR "replace" +#else +#define UNICODE_DEF_FS_ERROR "surrogateescape" +#endif + +#define RELATIVE_MODULE(m) ("." m) + +#ifndef Py_TPFLAGS_HAVE_NEWBUFFER +#define Py_TPFLAGS_HAVE_NEWBUFFER 0 +#endif + +#define Slice_GET_INDICES_EX(slice, length, start, stop, step, slicelength) \ + PySlice_GetIndicesEx(slice, length, start, stop, step, slicelength) + +#endif /* ~PGCOMPAT_INTERNAL_H */ diff --git a/.venv/include/site/python3.11/pygame/pgopengl.h b/.venv/include/site/python3.11/pygame/pgopengl.h new file mode 100644 index 00000000..a845cbf2 --- /dev/null +++ b/.venv/include/site/python3.11/pygame/pgopengl.h @@ -0,0 +1,20 @@ +#if !defined(PGOPENGL_H) +#define PGOPENGL_H + +/** This header includes definitions of Opengl functions as pointer types for + ** use with the SDL function SDL_GL_GetProcAddress. + **/ + +#if defined(_WIN32) +#define GL_APIENTRY __stdcall +#else +#define GL_APIENTRY +#endif + +typedef void(GL_APIENTRY *GL_glReadPixels_Func)(int, int, int, int, + unsigned int, unsigned int, + void *); + +typedef void(GL_APIENTRY *GL_glViewport_Func)(int, int, unsigned int, + unsigned int); +#endif diff --git a/.venv/include/site/python3.11/pygame/pgplatform.h b/.venv/include/site/python3.11/pygame/pgplatform.h new file mode 100644 index 00000000..54310eb6 --- /dev/null +++ b/.venv/include/site/python3.11/pygame/pgplatform.h @@ -0,0 +1,23 @@ +/* platform/compiler adjustments (internal) */ +#ifndef PG_PLATFORM_INTERNAL_H +#define PG_PLATFORM_INTERNAL_H + +#include "include/pgplatform.h" + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef ABS +#define ABS(a) (((a) < 0) ? -(a) : (a)) +#endif + +/* warnings */ +#define PG_STRINGIZE_HELPER(x) #x +#define PG_STRINGIZE(x) PG_STRINGIZE_HELPER(x) +#define PG_WARN(desc) \ + message(__FILE__ "(" PG_STRINGIZE(__LINE__) "): WARNING: " #desc) + +#endif /* ~PG_PLATFORM_INTERNAL_H */ diff --git a/.venv/include/site/python3.11/pygame/pygame.h b/.venv/include/site/python3.11/pygame/pygame.h new file mode 100644 index 00000000..d7eaf739 --- /dev/null +++ b/.venv/include/site/python3.11/pygame/pygame.h @@ -0,0 +1,32 @@ +/* + pygame - Python Game Library + Copyright (C) 2000-2001 Pete Shinners + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Pete Shinners + pete@shinners.org +*/ + +/* This will use PYGAMEAPI_DEFINE_SLOTS instead + * of PYGAMEAPI_EXTERN_SLOTS for base modules. + */ +#ifndef PYGAME_INTERNAL_H +#define PYGAME_INTERNAL_H + +#define PYGAME_H +#include "_pygame.h" + +#endif /* ~PYGAME_INTERNAL_H */ diff --git a/.venv/include/site/python3.11/pygame/scrap.h b/.venv/include/site/python3.11/pygame/scrap.h new file mode 100644 index 00000000..5866b568 --- /dev/null +++ b/.venv/include/site/python3.11/pygame/scrap.h @@ -0,0 +1,147 @@ +/* + pygame - Python Game Library + Copyright (C) 2006, 2007 Rene Dudfield, Marcus von Appen + + Originally put in the public domain by Sam Lantinga. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef SCRAP_H +#define SCRAP_H + +/* This is unconditionally defined in Python.h */ +#if defined(_POSIX_C_SOURCE) +#undef _POSIX_C_SOURCE +#endif + +#include + +/* Handle clipboard text and data in arbitrary formats */ + +/** + * Predefined supported pygame scrap types. + */ +#define PYGAME_SCRAP_TEXT "text/plain" +#define PYGAME_SCRAP_BMP "image/bmp" +#define PYGAME_SCRAP_PPM "image/ppm" +#define PYGAME_SCRAP_PBM "image/pbm" + +/** + * The supported scrap clipboard types. + * + * This is only relevant in a X11 environment, which supports mouse + * selections as well. For Win32 and MacOS environments the default + * clipboard is used, no matter what value is passed. + */ +typedef enum { + SCRAP_CLIPBOARD, + SCRAP_SELECTION /* only supported in X11 environments. */ +} ScrapClipType; + +/** + * Macro for initialization checks. + */ +#define PYGAME_SCRAP_INIT_CHECK() \ + if (!pygame_scrap_initialized()) \ + return (PyErr_SetString(pgExc_SDLError, "scrap system not initialized."), \ + NULL) + +/** + * \brief Checks, whether the pygame scrap module was initialized. + * + * \return 1 if the modules was initialized, 0 otherwise. + */ +extern int +pygame_scrap_initialized(void); + +/** + * \brief Initializes the pygame scrap module internals. Call this before any + * other method. + * + * \return 1 on successful initialization, 0 otherwise. + */ +extern int +pygame_scrap_init(void); + +/** + * \brief Checks, whether the pygame window lost the clipboard focus or not. + * + * \return 1 if the window lost the focus, 0 otherwise. + */ +extern int +pygame_scrap_lost(void); + +/** + * \brief Places content of a specific type into the clipboard. + * + * \note For X11 the following notes are important: The following types + * are reserved for internal usage and thus will throw an error on + * setting them: "TIMESTAMP", "TARGETS", "SDL_SELECTION". + * Setting PYGAME_SCRAP_TEXT ("text/plain") will also automatically + * set the X11 types "STRING" (XA_STRING), "TEXT" and "UTF8_STRING". + * + * For Win32 the following notes are important: Setting + * PYGAME_SCRAP_TEXT ("text/plain") will also automatically set + * the Win32 type "TEXT" (CF_TEXT). + * + * For QNX the following notes are important: Setting + * PYGAME_SCRAP_TEXT ("text/plain") will also automatically set + * the QNX type "TEXT" (Ph_CL_TEXT). + * + * \param type The type of the content. + * \param srclen The length of the content. + * \param src The NULL terminated content. + * \return 1, if the content could be successfully pasted into the clipboard, + * 0 otherwise. + */ +extern int +pygame_scrap_put(char *type, Py_ssize_t srclen, char *src); + +/** + * \brief Gets the current content from the clipboard. + * + * \note The received content does not need to be the content previously + * placed in the clipboard using pygame_put_scrap(). See the + * pygame_put_scrap() notes for more details. + * + * \param type The type of the content to receive. + * \param count The size of the returned content. + * \return The content or NULL in case of an error or if no content of the + * specified type was available. + */ +extern char * +pygame_scrap_get(char *type, size_t *count); + +/** + * \brief Gets the currently available content types from the clipboard. + * + * \return The different available content types or NULL in case of an + * error or if no content type is available. + */ +extern char ** +pygame_scrap_get_types(void); + +/** + * \brief Checks whether content for the specified scrap type is currently + * available in the clipboard. + * + * \param type The type to check for. + * \return 1, if there is content and 0 otherwise. + */ +extern int +pygame_scrap_contains(char *type); + +#endif /* SCRAP_H */ diff --git a/.venv/include/site/python3.11/pygame/simd_blitters.h b/.venv/include/site/python3.11/pygame/simd_blitters.h new file mode 100644 index 00000000..da0ecbb2 --- /dev/null +++ b/.venv/include/site/python3.11/pygame/simd_blitters.h @@ -0,0 +1,84 @@ +#define NO_PYGAME_C_API +#include "_surface.h" +#include "_blit_info.h" + +#if !defined(PG_ENABLE_ARM_NEON) && defined(__aarch64__) +// arm64 has neon optimisations enabled by default, even when fpu=neon is not +// passed +#define PG_ENABLE_ARM_NEON 1 +#endif + +int +pg_sse2_at_runtime_but_uncompiled(); +int +pg_neon_at_runtime_but_uncompiled(); +int +pg_avx2_at_runtime_but_uncompiled(); + +#if (defined(__SSE2__) || defined(PG_ENABLE_ARM_NEON)) +void +alphablit_alpha_sse2_argb_surf_alpha(SDL_BlitInfo *info); +void +alphablit_alpha_sse2_argb_no_surf_alpha(SDL_BlitInfo *info); +void +alphablit_alpha_sse2_argb_no_surf_alpha_opaque_dst(SDL_BlitInfo *info); +void +blit_blend_rgba_mul_sse2(SDL_BlitInfo *info); +void +blit_blend_rgb_mul_sse2(SDL_BlitInfo *info); +void +blit_blend_rgba_add_sse2(SDL_BlitInfo *info); +void +blit_blend_rgb_add_sse2(SDL_BlitInfo *info); +void +blit_blend_rgba_sub_sse2(SDL_BlitInfo *info); +void +blit_blend_rgb_sub_sse2(SDL_BlitInfo *info); +void +blit_blend_rgba_max_sse2(SDL_BlitInfo *info); +void +blit_blend_rgb_max_sse2(SDL_BlitInfo *info); +void +blit_blend_rgba_min_sse2(SDL_BlitInfo *info); +void +blit_blend_rgb_min_sse2(SDL_BlitInfo *info); +void +blit_blend_premultiplied_sse2(SDL_BlitInfo *info); +#endif /* (defined(__SSE2__) || defined(PG_ENABLE_ARM_NEON)) */ + +/* Deliberately putting these outside of the preprocessor guards as I want to + move to a system of trusting the runtime checks to head to the right + function and having a fallback function there if pygame is not compiled + with the right stuff (this is the strategy used for AVX2 right now. + Potentially I might want to shift both these into a slightly different + file as they are not exactly blits (though v. similar) - or I could rename + the SIMD trilogy of files to replace the word blit with something more + generic like surface_ops*/ + +void +premul_surf_color_by_alpha_non_simd(SDL_Surface *src, SDL_Surface *dst); +void +premul_surf_color_by_alpha_sse2(SDL_Surface *src, SDL_Surface *dst); + +int +pg_has_avx2(); +void +blit_blend_rgba_mul_avx2(SDL_BlitInfo *info); +void +blit_blend_rgb_mul_avx2(SDL_BlitInfo *info); +void +blit_blend_rgba_add_avx2(SDL_BlitInfo *info); +void +blit_blend_rgb_add_avx2(SDL_BlitInfo *info); +void +blit_blend_rgba_sub_avx2(SDL_BlitInfo *info); +void +blit_blend_rgb_sub_avx2(SDL_BlitInfo *info); +void +blit_blend_rgba_max_avx2(SDL_BlitInfo *info); +void +blit_blend_rgb_max_avx2(SDL_BlitInfo *info); +void +blit_blend_rgba_min_avx2(SDL_BlitInfo *info); +void +blit_blend_rgb_min_avx2(SDL_BlitInfo *info); diff --git a/.venv/include/site/python3.11/pygame/surface.h b/.venv/include/site/python3.11/pygame/surface.h new file mode 100644 index 00000000..21508c63 --- /dev/null +++ b/.venv/include/site/python3.11/pygame/surface.h @@ -0,0 +1,361 @@ +/* + pygame - Python Game Library + Copyright (C) 2000-2001 Pete Shinners + Copyright (C) 2007 Marcus von Appen + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Pete Shinners + pete@shinners.org +*/ + +#ifndef SURFACE_H +#define SURFACE_H + +/* This is defined in SDL.h */ +#if defined(_POSIX_C_SOURCE) +#undef _POSIX_C_SOURCE +#endif + +#include +#include "pygame.h" + +/* Blend modes */ +#define PYGAME_BLEND_ADD 0x1 +#define PYGAME_BLEND_SUB 0x2 +#define PYGAME_BLEND_MULT 0x3 +#define PYGAME_BLEND_MIN 0x4 +#define PYGAME_BLEND_MAX 0x5 + +#define PYGAME_BLEND_RGB_ADD 0x1 +#define PYGAME_BLEND_RGB_SUB 0x2 +#define PYGAME_BLEND_RGB_MULT 0x3 +#define PYGAME_BLEND_RGB_MIN 0x4 +#define PYGAME_BLEND_RGB_MAX 0x5 + +#define PYGAME_BLEND_RGBA_ADD 0x6 +#define PYGAME_BLEND_RGBA_SUB 0x7 +#define PYGAME_BLEND_RGBA_MULT 0x8 +#define PYGAME_BLEND_RGBA_MIN 0x9 +#define PYGAME_BLEND_RGBA_MAX 0x10 +#define PYGAME_BLEND_PREMULTIPLIED 0x11 +#define PYGAME_BLEND_ALPHA_SDL2 0x12 + +#if SDL_BYTEORDER == SDL_LIL_ENDIAN +#define GET_PIXEL_24(b) (b[0] + (b[1] << 8) + (b[2] << 16)) +#else +#define GET_PIXEL_24(b) (b[2] + (b[1] << 8) + (b[0] << 16)) +#endif + +#define GET_PIXEL(pxl, bpp, source) \ + switch (bpp) { \ + case 2: \ + pxl = *((Uint16 *)(source)); \ + break; \ + case 4: \ + pxl = *((Uint32 *)(source)); \ + break; \ + default: { \ + Uint8 *b = (Uint8 *)source; \ + pxl = GET_PIXEL_24(b); \ + } break; \ + } + +#define GET_PIXELVALS(_sR, _sG, _sB, _sA, px, fmt, ppa) \ + SDL_GetRGBA(px, fmt, &(_sR), &(_sG), &(_sB), &(_sA)); \ + if (!ppa) { \ + _sA = 255; \ + } + +#define GET_PIXELVALS_1(sr, sg, sb, sa, _src, _fmt) \ + sr = _fmt->palette->colors[*((Uint8 *)(_src))].r; \ + sg = _fmt->palette->colors[*((Uint8 *)(_src))].g; \ + sb = _fmt->palette->colors[*((Uint8 *)(_src))].b; \ + sa = 255; + +/* For 1 byte palette pixels */ +#define SET_PIXELVAL(px, fmt, _dR, _dG, _dB, _dA) \ + *(px) = (Uint8)SDL_MapRGBA(fmt, _dR, _dG, _dB, _dA) + +#if SDL_BYTEORDER == SDL_LIL_ENDIAN +#define SET_OFFSETS_24(or, og, ob, fmt) \ + { \ + or = (fmt->Rshift == 0 ? 0 : fmt->Rshift == 8 ? 1 : 2); \ + og = (fmt->Gshift == 0 ? 0 : fmt->Gshift == 8 ? 1 : 2); \ + ob = (fmt->Bshift == 0 ? 0 : fmt->Bshift == 8 ? 1 : 2); \ + } + +#define SET_OFFSETS_32(or, og, ob, fmt) \ + { \ + or = (fmt->Rshift == 0 ? 0 \ + : fmt->Rshift == 8 ? 1 \ + : fmt->Rshift == 16 ? 2 \ + : 3); \ + og = (fmt->Gshift == 0 ? 0 \ + : fmt->Gshift == 8 ? 1 \ + : fmt->Gshift == 16 ? 2 \ + : 3); \ + ob = (fmt->Bshift == 0 ? 0 \ + : fmt->Bshift == 8 ? 1 \ + : fmt->Bshift == 16 ? 2 \ + : 3); \ + } +#else +#define SET_OFFSETS_24(or, og, ob, fmt) \ + { \ + or = (fmt->Rshift == 0 ? 2 : fmt->Rshift == 8 ? 1 : 0); \ + og = (fmt->Gshift == 0 ? 2 : fmt->Gshift == 8 ? 1 : 0); \ + ob = (fmt->Bshift == 0 ? 2 : fmt->Bshift == 8 ? 1 : 0); \ + } + +#define SET_OFFSETS_32(or, og, ob, fmt) \ + { \ + or = (fmt->Rshift == 0 ? 3 \ + : fmt->Rshift == 8 ? 2 \ + : fmt->Rshift == 16 ? 1 \ + : 0); \ + og = (fmt->Gshift == 0 ? 3 \ + : fmt->Gshift == 8 ? 2 \ + : fmt->Gshift == 16 ? 1 \ + : 0); \ + ob = (fmt->Bshift == 0 ? 3 \ + : fmt->Bshift == 8 ? 2 \ + : fmt->Bshift == 16 ? 1 \ + : 0); \ + } +#endif + +#define CREATE_PIXEL(buf, r, g, b, a, bp, ft) \ + switch (bp) { \ + case 2: \ + *((Uint16 *)(buf)) = ((r >> ft->Rloss) << ft->Rshift) | \ + ((g >> ft->Gloss) << ft->Gshift) | \ + ((b >> ft->Bloss) << ft->Bshift) | \ + ((a >> ft->Aloss) << ft->Ashift); \ + break; \ + case 4: \ + *((Uint32 *)(buf)) = ((r >> ft->Rloss) << ft->Rshift) | \ + ((g >> ft->Gloss) << ft->Gshift) | \ + ((b >> ft->Bloss) << ft->Bshift) | \ + ((a >> ft->Aloss) << ft->Ashift); \ + break; \ + } + +/* Pretty good idea from Tom Duff :-). */ +#define LOOP_UNROLLED4(code, n, width) \ + n = (width + 3) / 4; \ + switch (width & 3) { \ + case 0: \ + do { \ + code; \ + case 3: \ + code; \ + case 2: \ + code; \ + case 1: \ + code; \ + } while (--n > 0); \ + } + +/* Used in the srcbpp == dstbpp == 1 blend functions */ +#define REPEAT_3(code) \ + code; \ + code; \ + code; + +#define REPEAT_4(code) \ + code; \ + code; \ + code; \ + code; + +#define BLEND_ADD(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \ + tmp = dR + sR; \ + dR = (tmp <= 255 ? tmp : 255); \ + tmp = dG + sG; \ + dG = (tmp <= 255 ? tmp : 255); \ + tmp = dB + sB; \ + dB = (tmp <= 255 ? tmp : 255); + +#define BLEND_SUB(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \ + tmp = dR - sR; \ + dR = (tmp >= 0 ? tmp : 0); \ + tmp = dG - sG; \ + dG = (tmp >= 0 ? tmp : 0); \ + tmp = dB - sB; \ + dB = (tmp >= 0 ? tmp : 0); + +#define BLEND_MULT(sR, sG, sB, sA, dR, dG, dB, dA) \ + dR = (dR && sR) ? ((dR * sR) + 255) >> 8 : 0; \ + dG = (dG && sG) ? ((dG * sG) + 255) >> 8 : 0; \ + dB = (dB && sB) ? ((dB * sB) + 255) >> 8 : 0; + +#define BLEND_MIN(sR, sG, sB, sA, dR, dG, dB, dA) \ + if (sR < dR) { \ + dR = sR; \ + } \ + if (sG < dG) { \ + dG = sG; \ + } \ + if (sB < dB) { \ + dB = sB; \ + } + +#define BLEND_MAX(sR, sG, sB, sA, dR, dG, dB, dA) \ + if (sR > dR) { \ + dR = sR; \ + } \ + if (sG > dG) { \ + dG = sG; \ + } \ + if (sB > dB) { \ + dB = sB; \ + } + +#define BLEND_RGBA_ADD(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \ + tmp = dR + sR; \ + dR = (tmp <= 255 ? tmp : 255); \ + tmp = dG + sG; \ + dG = (tmp <= 255 ? tmp : 255); \ + tmp = dB + sB; \ + dB = (tmp <= 255 ? tmp : 255); \ + tmp = dA + sA; \ + dA = (tmp <= 255 ? tmp : 255); + +#define BLEND_RGBA_SUB(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \ + tmp = dR - sR; \ + dR = (tmp >= 0 ? tmp : 0); \ + tmp = dG - sG; \ + dG = (tmp >= 0 ? tmp : 0); \ + tmp = dB - sB; \ + dB = (tmp >= 0 ? tmp : 0); \ + tmp = dA - sA; \ + dA = (tmp >= 0 ? tmp : 0); + +#define BLEND_RGBA_MULT(sR, sG, sB, sA, dR, dG, dB, dA) \ + dR = (dR && sR) ? ((dR * sR) + 255) >> 8 : 0; \ + dG = (dG && sG) ? ((dG * sG) + 255) >> 8 : 0; \ + dB = (dB && sB) ? ((dB * sB) + 255) >> 8 : 0; \ + dA = (dA && sA) ? ((dA * sA) + 255) >> 8 : 0; + +#define BLEND_RGBA_MIN(sR, sG, sB, sA, dR, dG, dB, dA) \ + if (sR < dR) { \ + dR = sR; \ + } \ + if (sG < dG) { \ + dG = sG; \ + } \ + if (sB < dB) { \ + dB = sB; \ + } \ + if (sA < dA) { \ + dA = sA; \ + } + +#define BLEND_RGBA_MAX(sR, sG, sB, sA, dR, dG, dB, dA) \ + if (sR > dR) { \ + dR = sR; \ + } \ + if (sG > dG) { \ + dG = sG; \ + } \ + if (sB > dB) { \ + dB = sB; \ + } \ + if (sA > dA) { \ + dA = sA; \ + } + +#if 1 +/* Choose an alpha blend equation. If the sign is preserved on a right shift + * then use a specialized, faster, equation. Otherwise a more general form, + * where all additions are done before the shift, is needed. + */ +#if (-1 >> 1) < 0 +#define ALPHA_BLEND_COMP(sC, dC, sA) ((((sC - dC) * sA + sC) >> 8) + dC) +#else +#define ALPHA_BLEND_COMP(sC, dC, sA) (((dC << 8) + (sC - dC) * sA + sC) >> 8) +#endif + +#define ALPHA_BLEND(sR, sG, sB, sA, dR, dG, dB, dA) \ + do { \ + if (dA) { \ + dR = ALPHA_BLEND_COMP(sR, dR, sA); \ + dG = ALPHA_BLEND_COMP(sG, dG, sA); \ + dB = ALPHA_BLEND_COMP(sB, dB, sA); \ + dA = sA + dA - ((sA * dA) / 255); \ + } \ + else { \ + dR = sR; \ + dG = sG; \ + dB = sB; \ + dA = sA; \ + } \ + } while (0) + +#define ALPHA_BLEND_PREMULTIPLIED_COMP(sC, dC, sA) \ + (sC + dC - ((dC + 1) * sA >> 8)) + +#define ALPHA_BLEND_PREMULTIPLIED(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \ + do { \ + dR = ALPHA_BLEND_PREMULTIPLIED_COMP(sR, dR, sA); \ + dG = ALPHA_BLEND_PREMULTIPLIED_COMP(sG, dG, sA); \ + dB = ALPHA_BLEND_PREMULTIPLIED_COMP(sB, dB, sA); \ + dA = ALPHA_BLEND_PREMULTIPLIED_COMP(sA, dA, sA); \ + } while (0) +#elif 0 + +#define ALPHA_BLEND(sR, sG, sB, sA, dR, dG, dB, dA) \ + do { \ + if (sA) { \ + if (dA && sA < 255) { \ + int dContrib = dA * (255 - sA) / 255; \ + dA = sA + dA - ((sA * dA) / 255); \ + dR = (dR * dContrib + sR * sA) / dA; \ + dG = (dG * dContrib + sG * sA) / dA; \ + dB = (dB * dContrib + sB * sA) / dA; \ + } \ + else { \ + dR = sR; \ + dG = sG; \ + dB = sB; \ + dA = sA; \ + } \ + } \ + } while (0) +#endif + +int +surface_fill_blend(SDL_Surface *surface, SDL_Rect *rect, Uint32 color, + int blendargs); + +void +surface_respect_clip_rect(SDL_Surface *surface, SDL_Rect *rect); + +int +pygame_AlphaBlit(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, + SDL_Rect *dstrect, int the_args); + +int +pygame_Blit(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, + SDL_Rect *dstrect, int the_args); + +int +premul_surf_color_by_alpha(SDL_Surface *src, SDL_Surface *dst); + +int +pg_warn_simd_at_runtime_but_uncompiled(); + +#endif /* SURFACE_H */ diff --git a/Arabic.mp3 b/Arabic.mp3 deleted file mode 100644 index bfc73d3d..00000000 Binary files a/Arabic.mp3 and /dev/null differ diff --git a/Arabic1.mp3 b/Arabic1.mp3 deleted file mode 100644 index 6cbc8a00..00000000 Binary files a/Arabic1.mp3 and /dev/null differ diff --git a/Arabic2.mp3 b/Arabic2.mp3 deleted file mode 100644 index 3e1e36f1..00000000 Binary files a/Arabic2.mp3 and /dev/null differ diff --git a/Arabic3.mp3 b/Arabic3.mp3 deleted file mode 100644 index 98a5f81d..00000000 Binary files a/Arabic3.mp3 and /dev/null differ diff --git a/Arabic4.mp3 b/Arabic4.mp3 deleted file mode 100644 index 5e9f1921..00000000 Binary files a/Arabic4.mp3 and /dev/null differ diff --git a/Arabic5.mp3 b/Arabic5.mp3 deleted file mode 100644 index 847ce986..00000000 Binary files a/Arabic5.mp3 and /dev/null differ diff --git a/__pycache__/Combined.cpython-311.pyc b/__pycache__/Combined.cpython-311.pyc index 441c6601..61d9a0d6 100644 Binary files a/__pycache__/Combined.cpython-311.pyc and b/__pycache__/Combined.cpython-311.pyc differ diff --git a/__pycache__/open_ai_voice.cpython-311.pyc b/__pycache__/open_ai_voice.cpython-311.pyc new file mode 100644 index 00000000..14cd14e0 Binary files /dev/null and b/__pycache__/open_ai_voice.cpython-311.pyc differ diff --git a/__pycache__/xtts.cpython-311.pyc b/__pycache__/xtts.cpython-311.pyc index cdac5981..94de157b 100644 Binary files a/__pycache__/xtts.cpython-311.pyc and b/__pycache__/xtts.cpython-311.pyc differ diff --git a/combined_video.mp4 b/combined_video.mp4 index 355df94a..ff748a8e 100644 --- a/combined_video.mp4 +++ b/combined_video.mp4 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9738cde63194957609a1b7639c561b30d4c7126473740d641436629b61e9118d -size 125784374 +oid sha256:baf7e97014a436f30b41651d0b568a310c7d0a31105782d12eaaf686e5820a8c +size 97978577 diff --git a/f.mp3 b/f.mp3 new file mode 100644 index 00000000..f4f8a82d Binary files /dev/null and b/f.mp3 differ diff --git a/open_ai_voice.py b/open_ai_voice.py new file mode 100644 index 00000000..46fc297e --- /dev/null +++ b/open_ai_voice.py @@ -0,0 +1,14 @@ +import openai + +def text_to_voice_openai(text): + client = openai.OpenAI(api_key="sk-proj-8nFPKH1bvaw0KH93XKAfT3BlbkFJUW2IOhGnIf95oCK3VZXn") + + response = client.audio.speech.create( + model="tts-1", + voice='shimmer', + input=text, + + ) + + response.stream_to_file('output.wav') + diff --git a/output.wav b/output.wav index fb4dd757..6f1c5657 100644 --- a/output.wav +++ b/output.wav @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6fd5423119b8a7159dc4b6cfac2f8c7a99c2fa66d09c4e2e942ef271259ea516 -size 62028 +oid sha256:ea2c33316b3ad6085e3adcc71a4c30eff4909c4f94beee63a864f1acdd11467f +size 1760640 diff --git a/sounds/male.wav b/sounds/male.wav new file mode 100644 index 00000000..8eceb864 --- /dev/null +++ b/sounds/male.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:937c74afad004937e00d1687c68e02210e0c5d93ac072a7c8aeb9ab573517bb1 +size 762126 diff --git a/test.py b/test.py index da3d2ad2..312ef890 100644 --- a/test.py +++ b/test.py @@ -1,19 +1,13 @@ import openai +client = openai.OpenAI(api_key="sk-proj-8nFPKH1bvaw0KH93XKAfT3BlbkFJUW2IOhGnIf95oCK3VZXn") -your_openai_key = 'sk-...' -d = { - 'Arabic': 'كان ليل هادئ في تلك القرية النائية، عندما استيقظ جون فجأة على صوت خشخشة غريبة. فتح عينيه ببطء ليجد ظلاً كئيباً يقف عند طرف سريره. كان الظل طويلاً ونحيفاً بشكل مخيف، ولم يكن له ملامح واضحة.', -} - - -client = openai.OpenAI(api_key="sk-proj-Dhg6JAP1ADX0VpM3ARg6T3BlbkFJmchI4lsTbZrV0X3XitI0") -voices = ['alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer'] - -for language in d: - response = client.audio.speech.create( - model="tts-1", - voice='shimmer', - input=d[language] - ) - - response.stream_to_file(f'{language}5.mp3') \ No newline at end of file +response = client.chat.completions.create( + model="gpt-3.5-turbo", + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Who won the world series in 2020?"}, + {"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."}, + {"role": "user", "content": "Where was it played?"} + ] +) +print(response.choices[0].message.content) \ No newline at end of file diff --git a/xtts.py b/xtts.py index fd67556a..6b7b9113 100644 --- a/xtts.py +++ b/xtts.py @@ -2,32 +2,57 @@ import tkinter as tk from tkinter import filedialog import os import shutil -from TTS.api import TTS import pygame from moviepy.editor import VideoFileClip import openai -OPENAI_API_KEY = "sk-proj-8nFPKH1bvaw0KH93XKAfT3BlbkFJUW2IOhGnIf95oCK3VZXn" -openai.api_key = OPENAI_API_KEY +from open_ai_voice import text_to_voice_openai +from Combined import combine_audio_video + +tts = False +if tts: + from TTS.api import TTS +client = openai.OpenAI(api_key="sk-proj-8nFPKH1bvaw0KH93XKAfT3BlbkFJUW2IOhGnIf95oCK3VZXn") def contect_gather(subject): - response = openai.completions.create( - model="gpt-3.5-turbo-instruct", - prompt=f"Write a 2 paraghraph short story about the {subject} with a positive and adventurous tone. Avoid violence, hatred, and mature themes." - ) - return response + selected_language = language_var.get() + if selected_language == "en": + response = client.chat.completions.create( + model="gpt-3.5-turbo", + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": f"Write a About 200 words paraghraph short story about the {subject} with a positive and adventurous tone. Avoid violence, hatred, and mature themes?"}, + ] + ) + else: + response = client.chat.completions.create( + model="gpt-3.5-turbo", + messages=[ + {"role": "system", "content": "انت كاتب قصص."}, + {"role": "user", "content": f"اكتب قصة صغيره عن {subject}"}, + ] + ) + + print(response.choices[0].message.content) + return response.choices[0].message.content # Initialize TTS object -tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2", gpu=False) +if tts: + tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2", gpu=False) # Function to convert text to voice def text_to_voice(text, speaker, language): - tts.tts_to_file(text=text, - file_path="output.wav", - speaker_wav=f"sounds/{speaker}", - language=language, - split_sentences=True - ) - play_audio("output.wav") + selected_ai = model_var.get() + if selected_ai == 1: + text_to_voice_openai(text) + + else: + tts.tts_to_file(text=text, + file_path="output.wav", + speaker_wav=f"sounds/{speaker}", + language=language, + split_sentences=True + ) + play_audio("output.wav") # Function to play audio using pygame def play_audio(file_path): @@ -39,7 +64,7 @@ def play_audio(file_path): def import_voice(): file_path = filedialog.askopenfilename() if file_path: - save_path = "C:/Users/hnk24/Documents/GitHub/ai-content-maker/sounds/" + save_path = "sounds/" os.makedirs(save_path, exist_ok=True) save_path = os.path.join(save_path, os.path.basename(file_path)) shutil.copyfile(file_path, save_path) @@ -48,7 +73,7 @@ def import_voice(): # Function to refresh file list def refresh_file_list(): - sound_folder = "C:/Users/hnk24/Documents/GitHub/ai-content-maker/sounds/" + sound_folder = "sounds/" file_listbox.delete(0, tk.END) # Clear the listbox for file_name in os.listdir(sound_folder): file_listbox.insert(tk.END, file_name) @@ -94,6 +119,13 @@ en_radio = tk.Radiobutton(root, text="EN", variable=language_var, value="en") ar_radio.grid(row=2, column=0, padx=10, pady=5, sticky="w") en_radio.grid(row=2, column=1, padx=10, pady=5, sticky="w") +model_var = tk.IntVar() +model_var.set(1) # Default to OpenAI +openai_radio = tk.Radiobutton(root, text="OpenAI Model", variable=model_var, value=1) +xtts_radio = tk.Radiobutton(root, text="xtts", variable=model_var, value=2) +openai_radio.grid(row=3, column=0, padx=10, pady=5, sticky="w") +xtts_radio.grid(row=3, column=1, padx=10, pady=5, sticky="w") + # Create textboxes single_line_textbox = tk.Entry(root) multi_line_textbox = tk.Text(root, height=5, width=30) @@ -117,6 +149,7 @@ process_button.grid(row=5, column=0, columnspan=2, padx=10, pady=10, sticky="ew" video_canvas = tk.Canvas(root, bg="black", width=400, height=300) video_canvas.grid(row=6, column=0, columnspan=2, padx=10, pady=10) + # Function to play video def play_video(file_path): clip = VideoFileClip(file_path) @@ -131,14 +164,16 @@ def play_media(file_path): # Function to display output file in the canvas and play media def display_output(): - file_path = "output.wav" + file_path = "combined_video.mp4" if os.path.exists(file_path): play_media(file_path) # Display output on canvas # You need to implement this part using a suitable library like pygame for displaying images or videos on canvas. + + # Create save video button -save_button = tk.Button(root, text="Save Video", command=save_video) +save_button = tk.Button(root, text="Save Video", command=combine_audio_video("output.wav", "original_video.webm", "combined_video.mp4")) save_button.grid(row=7, column=0, columnspan=2, padx=10, pady=10, sticky="ew") root.mainloop()