{"id":51188234,"url":"https://github.com/yu314-coder/cairometal","last_synced_at":"2026-06-27T12:30:52.926Z","repository":{"id":363359479,"uuid":"1262999424","full_name":"yu314-coder/cairometal","owner":"yu314-coder","description":"pycairo-compatible 2D vector graphics rendered on the Apple GPU via Metal — 246 cm_* functions, stencil-then-cover fill, IOSurface-backed, pixel-diffed vs real cairo. macOS + iOS.","archived":false,"fork":false,"pushed_at":"2026-06-08T14:26:41.000Z","size":367,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-08T16:22:23.150Z","etag":null,"topics":["2d-graphics","apple-silicon","cairo","gpu","ios","macos","metal","pycairo","vector-graphics"],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yu314-coder.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-08T14:21:47.000Z","updated_at":"2026-06-08T14:31:35.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/yu314-coder/cairometal","commit_stats":null,"previous_names":["yu314-coder/cairometal"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/yu314-coder/cairometal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yu314-coder%2Fcairometal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yu314-coder%2Fcairometal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yu314-coder%2Fcairometal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yu314-coder%2Fcairometal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yu314-coder","download_url":"https://codeload.github.com/yu314-coder/cairometal/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yu314-coder%2Fcairometal/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34854180,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-27T02:00:06.362Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["2d-graphics","apple-silicon","cairo","gpu","ios","macos","metal","pycairo","vector-graphics"],"created_at":"2026-06-27T12:30:52.281Z","updated_at":"2026-06-27T12:30:52.918Z","avatar_url":"https://github.com/yu314-coder.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CairoMetal\n\n**A pycairo-compatible 2D vector-graphics library that renders on the Apple GPU via Metal.**\n\nCairoMetal is a broad, building re-implementation of the cairo graphics API on top of Metal. The C library exposes **246 `cm_*` functions across 24 source modules**, and the Python extension is a **drop-in for most of pycairo** — `import cairo_metal as cairo` and the usual `ImageSurface` / `Context` / gradients / patterns / text code just works, rendering paths on the GPU with a **stencil-then-cover** pipeline into an **IOSurface-backed `MTLTexture`**.\n\nEnums and matrix layout are **numerically identical to cairo's**, and output is **pixel-diffed against real cairo** (byte-identical on flat fills; anti-aliased-edge-only differences on curves).\n\n```python\nimport cairo_metal as cairo        # the GPU shim — module is \"cairo_metal\", never shadows real cairo\n\nsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 400, 300)\nctx = cairo.Context(surface)\nctx.move_to(50, 50); ctx.curve_to(150, 0, 250, 300, 350, 150)\nctx.set_source_rgba(0.2, 0.5, 0.9, 1.0)\nctx.set_line_width(8); ctx.stroke()\nsurface.write_to_png(\"out.png\")\n```\n\n\u003e **Origin / honest scope note.** CairoMetal began inside [OfflinAi / CodeBench](https://github.com/yu314-coder) (an on-device scientific-Python stack for iOS) and grew from a manim-only subset into a general GPU cairo. It is a faithful, GPU-backed cairo — but note it does **not** speed up *manim* specifically: in manim, cairo fill/stroke is ~5% of frame time and the bottleneck is single-threaded Python path interpolation, which no graphics backend can touch. CairoMetal is best thought of as \"pixel-accurate cairo, on the GPU,\" not a manim accelerator.\n\n## Prior art — how this differs\n\nUnlike PyTorch-on-Metal (which exists officially), **cairo has no Metal backend** and there is no real precedent for one. Cairo's backends are image (software), Quartz, Win32, Xlib, and PDF/SVG/PostScript; its only GPU path was OpenGL (`cairo-gl` / glitz), which was **removed in 2022–2023**, and cairo is now in **maintenance-only mode**. The 2D engine that *does* have a Metal backend is **Skia** — a different library, not cairo.\n\nCairoMetal fills that gap: a **pycairo-compatible cairo that renders on Metal**, and it **works on both iOS and macOS** — the same sources build for either (`build.sh` / `python/build.sh` on macOS, `python/build_ios.sh` for iOS). To our knowledge, a GPU/Metal cairo with a pycairo drop-in is novel.\n\n## Features\n\nA broad slice of the cairo API, with cairo-exact semantics and enum values:\n\n- **Surfaces** — `ImageSurface` in `ARGB32`, `RGB24`, `A8`, `A1`, `RGB16_565`; `RecordingSurface`; `create_similar`; **PNG read/write** (`write_to_png` / `ImageSurface.create_from_png`); raw buffer map; zero-copy `IOSurfaceRef` accessor.\n- **Context** — `save`/`restore`, `push_group`/`pop_group`(`_to_source`), status.\n- **Paths** — `move_to`, `line_to`, `curve_to`, `rel_*`, `arc`, `arc_negative`, `rectangle`, `close_path`, `new_path`/`new_sub_path`, `text_path`, plus `copy_path`/`append_path`, `path_extents`.\n- **Fill \u0026 stroke** — `fill`(`_preserve`), `stroke`(`_preserve`), `set_fill_rule` (winding **and** even-odd), line width/join/cap/miter, dashes.\n- **Sources / patterns** — `set_source_rgb(a)`; `SolidPattern`, `SurfacePattern`, `LinearGradient`, `RadialGradient`, `MeshPattern`; color stops; extend \u0026 filter modes.\n- **Compositing** — **all 28 operators** (Porter-Duff via fixed-function blend states 0–13; the separable + HSL blend modes 14–27 via programmable-blend cover fragments).\n- **Clipping \u0026 masking** — `clip`(`_preserve`), `reset_clip`, `clip_extents`, `in_clip`; `mask`, `mask_surface`; `paint`, `paint_with_alpha`.\n- **Text** — toy text API (`select_font_face`, `set_font_size`, `show_text`, `text_extents`), `FontOptions`, `ToyFontFace`, `ScaledFont`, glyph paths via FreeType (`cm_ft`).\n- **Transforms** — `translate`/`scale`/`rotate`/`transform`/`set_matrix`/`get_matrix`, device↔user conversions, full `Matrix` algebra (multiply/invert/transform point \u0026 distance).\n- **Regions** — `Region` with the cairo set operations.\n- **Queries** — `fill_extents`, `stroke_extents`, `path_extents`, `in_fill`, `in_stroke`.\n\nThe Python extension (`python/cairo_metal_ext.c`, 2246 lines) exposes **171 symbols / 18 classes / 20 enums**, with `Context` carrying **91 methods**.\n\n## Requirements\n\n- **macOS** on Apple Silicon (a Metal-capable GPU). The same sources also build for **iOS arm64**.\n- **Xcode / Command Line Tools** — `clang`, `swift`, and the Metal toolchain (`xcrun -sdk macosx -f metal` must resolve).\n- **Python 3** with dev headers (`python3-config`) for the extension. NumPy is **not** required; Pillow is optional (tests use it to decode-verify PNGs).\n\n## Quick start (macOS)\n\n```bash\ngit clone https://github.com/yu314-coder/cairometal.git\ncd cairometal\n./build.sh                      # swift build + compile shaders + render the C demo\nbash python/build.sh           # build the cairo_metal CPython extension\n```\n\nThen:\n\n```bash\nexport CM_METALLIB=\"$PWD/build/default.metallib\"\nexport PYTHONPATH=\"$PWD/python:$PYTHONPATH\"\npython3 -c \"import cairo_metal as cairo; print('cairo_metal', cairo.version())\"\n```\n\n## Usage\n\n### Python (pycairo drop-in)\n\n```python\nimport cairo_metal as cairo\n\nsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 256, 256)\nctx = cairo.Context(surface)\n\n# a linear-gradient filled rounded shape\ng = cairo.LinearGradient(0, 0, 256, 256)\ng.add_color_stop_rgba(0, 1, 0.3, 0.2, 1)\ng.add_color_stop_rgba(1, 0.2, 0.4, 1, 1)\nctx.rectangle(32, 32, 192, 192)\nctx.set_source(g)\nctx.fill_preserve()\n\n# stroke the same path\nctx.set_source_rgba(0, 0, 0, 1)\nctx.set_line_width(6)\nctx.set_line_join(cairo.LINE_JOIN_ROUND)\nctx.stroke()\n\n# text\nctx.select_font_face(\"Helvetica\")\nctx.set_font_size(28)\nctx.move_to(48, 140)\nctx.set_source_rgba(1, 1, 1, 1)\nctx.show_text(\"CairoMetal\")\n\nsurface.write_to_png(\"demo.png\")\n```\n\nBecause the module is named `cairo_metal` (not `cairo`), it never shadows a real pycairo install; you opt in explicitly with `import cairo_metal as cairo`. Enums (`FORMAT_*`, `LINE_JOIN_*`, `LINE_CAP_*`, `OPERATOR_*`, `FILL_RULE_*`, `EXTEND_*`, `FILTER_*`) carry the same integer values as cairo.\n\n### C API\n\nThe public C API is [`include/cairo_metal.h`](include/cairo_metal.h) (~1020 lines, 246 `CM_PUBLIC` functions). Every entry point mirrors a cairo call (`cm_image_surface_create`, `cm_context_create`, `cm_move_to`, `cm_curve_to`, `cm_set_source_rgba`, `cm_fill_preserve`, `cm_stroke_preserve`, `cm_surface_write_to_png`, …). `cm_matrix_t` is binary-compatible with `cairo_matrix_t`. `cm_surface_get_iosurface()` returns the `IOSurfaceRef` for zero-copy hand-off (e.g. to VideoToolbox).\n\n## Architecture\n\n```\n        your code  /  manim camera  /  pycairo-style Python\n                 │  C API  (include/cairo_metal.h)  ·  or the cairo_metal Python ext\n                 ▼\n   ┌────────────────────────────────────────────────────────────┐\n   │ cairo_metal.m   public glue + context state machine + batch  │\n   └───┬──────┬──────┬───────┬───────┬───────┬───────┬───────┬────┘\n       ▼      ▼      ▼       ▼       ▼       ▼       ▼       ▼\n   cm_path cm_fill cm_stroke cm_paint cm_clip cm_compose cm_text cm_pattern …\n       └──────┴──────┴───────┴───────┴───────┴───────┴───────┘\n                                 ▼\n                    cm_device.m + cm_surface.m\n        MTLDevice/queue · persistent pipeline \u0026 depth-stencil states ·\n        triple-buffered ring + dispatch_semaphore · IOSurface-backed\n        BGRA8 target + MSAA + stencil\n                                 │  zero-copy IOSurfaceRef\n                                 ▼\n                       e.g. h264_videotoolbox\n```\n\nThe struct layouts and inter-module function names live in [`src/cm_internal.h`](src/cm_internal.h). Pure-C modules (`cm_matrix.c`, `cm_region.c`, `cm_pattern.c`, `cm_raster.c`, …) hold the math; the Metal/IOSurface plumbing is Objective-C. See [DESIGN.md](DESIGN.md) for the full rationale.\n\n### The stencil-then-cover pipeline\n\nArbitrary self-intersecting paths with holes are filled with the classic two-pass **stencil-then-cover** technique — no CPU triangulation of concave polygons:\n\n1. **CPU flatten** — cubic Béziers flattened by adaptive de Casteljau, flatness tested in **device space** so on-screen deviation stays under tolerance at any zoom.\n2. **Stencil pass** — each contour emits a triangle fan; **winding** uses two-sided increment/decrement-wrap, **even-odd** uses invert-on-low-bit; colour writes masked off.\n3. **Cover pass** — the path's bounding quad is drawn; the depth-stencil state tests the stencil **and** resets the touched bits in the same op (no per-path clear). The fragment shader produces the paint (solid, gradient LUT, or a blend-mode cover for operators 14–27).\n\n**Anti-aliasing** is 4× MSAA on colour + stencil; `cm_frame_end` resolves into the IOSurface target. **Strokes** are CPU-expanded into a fillable outline (segment quads + joins + caps, honoring width/join/cap/miter) and run through the same fill.\n\n### Optimizations\n\nPersistent pipeline \u0026 depth-stencil states (built once); one command buffer per frame; triple-buffered dynamic buffers gated by a `dispatch_semaphore`; IOSurface-backed target for zero-copy encode; zero per-draw heap allocation (bump-allocated ring); draws grouped by pipeline state.\n\n## Build\n\n### macOS\n\n```bash\n./build.sh                 # swift build + shaders -\u003e default.metallib + render demo\n./build.sh --clean         # wipe .build/ and build/ first\n./build.sh --no-run        # build only\nbash python/build.sh       # build just the cairo_metal CPython extension\n```\n\nProduces `build/libcairometal.a`, `build/default.metallib`, `build/demo` (+ `build/demo.png`), and `python/cairo_metal.cpython-*-darwin.so`. Individual steps go through the **Makefile**; the source/shader inventory is kept in lock-step with `Package.swift`.\n\n### iOS (arm64)\n\n```bash\nbash python/build_ios.sh\n```\n\nCross-compiles the same sources + `fill.metal` + the CPython ext for the `iphoneos` target. Copy the resulting `cairo_metal.cpython-*-iphoneos.so` + `default.metallib` into your app bundle.\n\n### How it finds its Metal kernels at runtime\n\n`cm_device.m` resolves the metallib in three tiers (first wins): `$CM_METALLIB` → the app/main-bundle `default.metallib` (add `shaders/fill.metal` to the app target) → compile `shaders/fill.metal` from source at runtime.\n\n## Tests\n\n```bash\nexport CM_METALLIB=\"$PWD/build/default.metallib\"\nPYTHONPATH=\"$PWD/python\" python3 tests/test_geometry.py     # transforms, paths, matrix algebra\nPYTHONPATH=\"$PWD/python\" python3 tests/test_raster.py       # rasterized output checks\nPYTHONPATH=\"$PWD/python\" python3 tests/test_robust.py       # edge cases / robustness\nPYTHONPATH=\"$PWD/python\" python3 tests/test_reference.py    # pixel-diff vs REAL cairo (needs pycairo)\nPYTHONPATH=\"$PWD/python\" python3 python/test_full_shim.py   # full pycairo-shim smoke test\nPYTHONPATH=\"$PWD/python\" python3 python/test_gaps.py        # API-gap coverage\n```\n\n`tests/test_reference.py` renders each scene with both CairoMetal and **real cairo** (pycairo) and diffs the pixels: **byte-identical on flat fills**, anti-aliased-edge-only differences on curves (±1 LSB premultiplied rounding). The repo also includes GPU smoke scripts (`cairo_gpu_test.py`, `cairo_gpu_full_test.py`, `cairo_gpu_deep_test.py`) used to validate the path on a real Metal device.\n\n## Repository layout\n\n```\ninclude/cairo_metal.h     public C API (246 functions)\nsrc/cairo_metal.m         public glue + context state machine + batching\nsrc/cm_device.m           MTLDevice/queue, persistent states, ring + semaphore\nsrc/cm_surface*.m/.c      IOSurface target, formats, PNG, similar, MSAA resolve\nsrc/cm_path.m             record / adaptive-flatten / tessellate\nsrc/cm_fill.m             stencil-then-cover encode\nsrc/cm_stroke.m           stroke expansion -\u003e fillable polygon\nsrc/cm_paint.m            solid + gradients, 1D LUT bake\nsrc/cm_compose.m          programmable blend modes (operators 14–27)\nsrc/cm_clip.m             clip / mask\nsrc/cm_pattern.c          solid / surface / linear / radial / mesh patterns\nsrc/cm_text.m cm_font.c cm_ft.c   toy text, font options, scaled fonts, FreeType glyphs\nsrc/cm_mesh.c cm_region.c cm_matrix.c cm_raster.c cm_state.c cm_query.c  pure-C helpers\nshaders/fill.metal        vertex + stencil/cover/blend fragments\npython/cairo_metal_ext.c  CPython extension — pycairo drop-in (171 symbols)\npython/build.sh build_ios.sh   macOS / iOS extension builds\nbuild.sh Makefile Package.swift   one-shot / CLI / SwiftPM builds\nexamples/demo.m           pure-C GPU smoke test\ntests/                    geometry / raster / robust / reference (vs real cairo)\n```\n\nSee **[DESIGN.md](DESIGN.md)** for the design and **[STATUS.md](STATUS.md)** for the running verification state and known gaps.\n\n## License\n\n[MIT](LICENSE) © 2026 Yu Yao-Hsing\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyu314-coder%2Fcairometal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyu314-coder%2Fcairometal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyu314-coder%2Fcairometal/lists"}