{"id":17238629,"url":"https://github.com/breakthrough/pygame-ctypes-tutorial","last_synced_at":"2025-10-18T20:06:44.533Z","repository":{"id":15289828,"uuid":"18019404","full_name":"Breakthrough/pygame-ctypes-tutorial","owner":"Breakthrough","description":"Accessing Pygame surfaces or OpenCV images with C/C++ via ctypes and the Numpy-based surfarray module.","archived":false,"fork":false,"pushed_at":"2014-03-28T00:50:32.000Z","size":328,"stargazers_count":3,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-30T23:42:01.914Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Breakthrough.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-03-22T20:43:53.000Z","updated_at":"2022-06-15T01:32:26.000Z","dependencies_parsed_at":"2022-07-31T03:48:51.629Z","dependency_job_id":null,"html_url":"https://github.com/Breakthrough/pygame-ctypes-tutorial","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Breakthrough%2Fpygame-ctypes-tutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Breakthrough%2Fpygame-ctypes-tutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Breakthrough%2Fpygame-ctypes-tutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Breakthrough%2Fpygame-ctypes-tutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Breakthrough","download_url":"https://codeload.github.com/Breakthrough/pygame-ctypes-tutorial/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245576529,"owners_count":20638125,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-15T05:46:13.263Z","updated_at":"2025-10-18T20:06:44.454Z","avatar_url":"https://github.com/Breakthrough.png","language":"C++","readme":"Accessing Pygame Surfaces in C++ with ctypes and Numpy\n==========================================================\nBy: [Brandon Castellano](http://www.bcastell.com)\n\n----------------------------------------------------------\n\nWith ctypes, you can mix C/C++ code with Python to gain the speed of compiled code with the awesomeness of Python.  How much speed are we talking about?  When compiled with optimizations enabled, the compiled functions provide a significant performance gain - sometimes exceeds even native Pygame methods, such as filling a surface:\n\n    ----------------------------------------------------------\n    |  Surface Fill  |        Time to Fill Surface (ms)      |\n    |     Method     |  1280x720  |  1920x1080  |  2560x1600 |\n    ----------------------------------------------------------\n    |  *Python*:     |            |             |            |\n    |  surfarray[:]  |            |             |            |\n    |  surface.fill  |            |             |            |\n    |  surface.blit  |            |             |            |\n    ----------------------------------------------------------\n    |  *C++*:        |            |             |            |\n    |  for-loop      |            |             |            |\n    |  memset        |            |             |            |\n    |  memcpy        |            |             |            |\n    ----------------------------------------------------------\n    \n    (tested w/ i7 2600k @ 4.0 GHz, test code can be found in `benchmarks/` directory)\n    \n\nWhile very useful in some cases, accessing higher-level objects can be difficult without modifying the C/C++ part of the code [into a Python module](http://docs.python.org/2/extending/extending.html#writing-extensions-in-c), or by using wrappers like [SWIG](http://www.swig.org/).  In this tutorial, we will modify a Pygame surface directly in memory using C++, without any additional dependencies/libraries.\n\nThis file (README.md) contains a brief outline of the process, which should suffice for those comfortable with ctypes and manipulating pygame/SDL surfaces directly.  A more in-depth guide can be found in TUTORIAL.md, including the creation of a demonstration application (found in the `example/` directory).\n\n\nRequirements\n------------\n - Python (should work on both 2 and 3, tested on 2)\n - pygame (\u003e 1.8) and Numpy\n - a C++ compiler (tested w/ g++ on Linux and VS2010 on Windows)\n - that's it!\n\n\nFile List\n---------\n\n - `README.md` : Quickstart covering process for accessing Pygame surface data with C++ code.\n - `TUTORIAL.md` : Step-by-step tutorial of the process, including a sample Pygame program.\n - `LICENSE` : File detailing terms all files in this repository are released under (public domain).\n - `example/` : Sample Python/C++ code for this tutorial; includes a Makefile for *NIX people that have `g++` and a Visual Studio project for Windows users.\n - `benchmarks/` : Python program and C++ shared library comparing the performance of various surface access methods/operations in both languages.\n  \n \n----------------------------------------------------------\n\nQuickstart\n----------\n\n** TODO ** : This is the complete tutorial as-is; should be moved to `TUTORIAL.md` eventually and made significantly shorter (i.e. ~1-2 pages) as a general overview.\n\nThere are four main steps we need to complete:\n\n 1. Write a C++ function to modify a Pygame surface in-place\n 2. Compile the function into a shared library\n    2.1. Compiling on Linux (gcc/g++)\n    2.2. Compiling on Windows (Visual Studio)\n 3. Import the shared library via Python's ctypes\n 4. Access the Pygame surface as a Numpy array, obtain pointer to pixel data\n\n### 1. Writing a C++ Function to Modify a Surface\n\nTo demonstrate reading/writing pixels in C++, let's create a function that draws a gradient from red to blue onto surface:\n\u003cimg style=\"max-width:100%;\" src=\"https://raw.github.com/breakthrough/pygame-ctypes-tutorial/master/images/fillrgb.png\" alt=\"Image Filled with Red/Green/Blue\" /\u003e\n\nFor simplicity, we assume the surface format is 32-bit ARGB (although it is easy to modify for additional surface types).  We have to compute the address of each pixel as an offset from the start of the surface data, since we will work with a pointer directly to the pixel data (`unsigned char*`) in memory:\n\n```C+\nextern \"C\"\nvoid fill_rgb(unsigned char* surf_data, int surf_w, int surf_h, int surf_pitch)\n{\n    if (!surf_data) return;\n\n    for (int row = 0; row \u003c surf_h; row++)\n    {\n        unsigned char* pixel_ptr = surf_data + (row * surf_pitch);\n\n        for (int col = start_red; col \u003c start_blue; col++)\n        {\n            // Smooth gradient, blue gets most intense on right edge.\n            unsigned char blue_val = (unsigned char)((0xFF * col) / (surf_w - 1));\n            // Pixel data packed in byte-by-byte as Alpha/Red/Green/Blue (ARGB).\n            pixel_ptr++;                       // Alpha (unused)\n            *(pixel_ptr++) = 0xFF - blue_val;  // Red\n            *(pixel_ptr++) = 0x00;             // Green (unused)\n            *(pixel_ptr++) = blue_val;         // Blue\n        }\n    }\n}\n```\n\nNote that we have to include `extern \"C\"` in the function declaration so that the function name is exported properly in the shared library.  **Windows users** must also add `__declspec(dllexport)` just after `extern \"C\"` for the function names to show up properly after loading the `.dll` with ctypes.\n\nSince we are accessing the pixels as they sit in memory, one must ensure that the surface type is known and being [accessed correctly](http://en.wikipedia.org/wiki/Endianness).  That being said, that the above function can easily be adapted for other surface types (the [`get_masks()`](http://www.pygame.org/docs/ref/surface.html#pygame.Surface.get_pitch) and [`get_pitch()`](http://www.pygame.org/docs/ref/surface.html#pygame.Surface.get_pitch) methods are useful for correctly addressing surfaces in-memory), the same way as would be done for a regular SDL surface in C/C++.\n\nOne could technically use the SDL library functions on pygame surfaces, although this would require linking your C++ code with SDL.\n\n\n----------------------------------------------------------\n\n### 2. Compiling the C++ Function into a Shared Library\n\nWhile this technique does work across platforms, in general compiling a shared library is platform specific. Below are instructions for building on UNIX/Linux systems with gcc/g++ (tested on 4.6) and Windows systems with Visual Studio (tested on 2010).\n\n#### 2.1 Compiling on Linux (gcc/g++)\n\nLike many things on Linux, assuming we saved our above function into `pixelmanip.cpp`, we can compile it into the shared library file `pixelmanip.so` in one command:\n\n    g++ -shared -Wl,-soname,pixelmanip.so -o pixelmanip.so -fPIC pixelmanip.cpp\n\nAnd that's it!  Now we can move on and import the library functions in our Python code via the ctypes module.  Additionally, to compile the code with optimizations enabled (yielding significant performance benefits in most cases), one can also include the `-O3` flag:\n\n    g++ -shared -O3 -Wl,-soname,pixelmanip.so -o pixelmanip.so -fPIC pixelmanip.cpp\n\nOnce the shared library is compiled, place it in the same directory as your Python/pygame code.\n\n#### 2.2 Compiling on Windows (Visual Studio)\n\nInstead of targeting an executable (`.exe`), we need to tell Visual Studio to compile a `.dll` instead in addition to `extern \"C\"` to your function!).  Create a new **empty** C++ solution/project, add the above surface manipulation function into a new file `pixelmanip.cpp` (don't forget to prepend `__declspec(dllexport)`.  Open the project's *Configuration Properties*, and under *General*, change *Configuration Type* to *Dynamic Library (.dll)*:\n\n\u003cimg style=\"max-width:100%;\" src=\"https://raw.github.com/breakthrough/pygame-ctypes-tutorial/master/images/vsdllconfig.png\" alt=\"Visual Studio 2010: Changing Build Output from Application (.exe) to Dynamic Library (.dll)\" /\u003e\n\nAlso remember to change this setting for both Debug and Release builds.  Once this is done, you can build the solution like normal (from the Build menu).  Note that by default, optimizations are disabled for Debug builds but enabled for Release builds (C/C++ -\u003e Optimization).\n\nOnce the .dll file is compiled (either in the `Debug/` or `Release/` folders), place it in the same directory as your Python/pygame code.\n\n\n----------------------------------------------------------\n\n### 3. Importing the Library Functions into Python via ctypes\n\n```Python\n\nimport ctypes\nlibsurfmanip = ctypes.CDLL('/full/path/to/shared/library/libsurfmanip.so')\n\ncpp_fill_rgb = libsurfmanip.fill_rgb\ncpp_fill_rgb.restype = None\ncpp_fill_rgb.argtypes = [ numpy.ctypeslib.ndpointer(ctypes.c_int),  # ptr to surface data\n                     ctypes.c_int,      # surface width\n                     ctypes.c_int,      # surface height\n                     ctypes.c_int ]     # surface pitch (bytes/row)\n\n```\n\n\n----------------------------------------------------------\n\n### 4. Passing a Pointer to a Surface's Pixel Data\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbreakthrough%2Fpygame-ctypes-tutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbreakthrough%2Fpygame-ctypes-tutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbreakthrough%2Fpygame-ctypes-tutorial/lists"}