{"id":50323159,"url":"https://github.com/diogok/zpix","last_synced_at":"2026-05-29T04:02:11.212Z","repository":{"id":349870299,"uuid":"1175919372","full_name":"diogok/zpix","owner":"diogok","description":"Image encoder/decoder in zig","archived":false,"fork":false,"pushed_at":"2026-04-26T08:51:54.000Z","size":4612,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-26T10:21:51.891Z","etag":null,"topics":["image","jpeg","png","zig"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/diogok.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-08T11:13:29.000Z","updated_at":"2026-04-26T08:51:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/diogok/zpix","commit_stats":null,"previous_names":["diogok/zpix"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/diogok/zpix","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diogok%2Fzpix","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diogok%2Fzpix/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diogok%2Fzpix/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diogok%2Fzpix/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/diogok","download_url":"https://codeload.github.com/diogok/zpix/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diogok%2Fzpix/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33635961,"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-05-29T02:00:06.066Z","response_time":107,"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":["image","jpeg","png","zig"],"created_at":"2026-05-29T04:02:10.524Z","updated_at":"2026-05-29T04:02:11.202Z","avatar_url":"https://github.com/diogok.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# zpix\n\nA pure Zig image library for generating thumbnails and tiles. Decodes JPEG and PNG, encodes PNG. No C dependencies in the core library.\n\nThis was written by AI.\n\n## Documentation\n\n- **[Usage Guide](docs/USAGE.md)** - Library and CLI usage\n- **[Architecture](docs/ARCHITECTURE.md)** - Module structure and data flow\n- **[Coding Conventions](docs/CODING_CONVENTIONS.md)** - Zig conventions for this project\n- Generate API docs: `zig build docs` (outputs to `zig-out/docs/api`)\n\n## Features\n\n- **Decode**: JPEG (baseline + progressive) and PNG\n- **Encode**: PNG\n- Crop, resize (bilinear), thumbnail generation\n- Rotate (90/180/270) and flip (horizontal/vertical)\n- JPEG encoding (baseline)\n- CLI tool for image processing\n\n## Format Support\n\n### JPEG\n\n| Feature | Decode | Encode |\n|---------|--------|--------|\n| Baseline DCT (SOF0) | Yes | Yes |\n| Extended sequential DCT (SOF1) | Yes | No |\n| Progressive DCT (SOF2) | Yes | No |\n| Grayscale | Yes | Yes |\n| YCbCr 4:4:4, 4:2:2, 4:2:0 | Yes | 4:4:4 only |\n| Restart markers (DRI) | Yes | No |\n| DC/AC refinement scans | Yes | N/A |\n| Arithmetic coding | No | No |\n\nProgressive JPEG decoding includes all scan types (DC/AC first and refinement scans) for pixel-perfect output.\n\n### PNG\n\n| Feature | Decode | Encode |\n|---------|--------|--------|\n| RGB (8-bit) | Yes | Yes |\n| RGBA (8-bit) | Yes | Yes |\n| Grayscale (8-bit) | Yes | Yes |\n| Grayscale+Alpha (8-bit) | Yes | Yes |\n| Adam7 interlacing | Yes | No |\n| All filter types (None, Sub, Up, Average, Paeth) | Yes | None only |\n\nNot supported: palette/indexed color, 16-bit depth, 1/2/4-bit depth, ancillary chunks.\n\n## Library Usage\n\n### File-based API\n\nFile-based APIs take an `io: std.Io` instance as their first argument. Get\none from \"Juicy Main\" (`init.io`) or, in tests, from `std.testing.io`.\n\n```zig\nconst zpix = @import(\"zpix\");\n\npub fn main(init: std.process.Init) !void {\n    const io = init.io;\n    const allocator = init.gpa;\n\n    // Load any image (auto-detects format by magic bytes)\n    var image = try zpix.loadFile(io, allocator, \"photo.jpg\");\n    defer image.deinit();\n\n    // Or load a specific format\n    var png_image = try zpix.loadPngFile(io, allocator, \"image.png\");\n    defer png_image.deinit();\n\n    // Crop\n    var cropped = try image.crop(x, y, width, height);\n    defer cropped.deinit();\n\n    // Resize\n    var resized = try image.resize(new_width, new_height);\n    defer resized.deinit();\n\n    // Save (auto-detects format by file extension)\n    try zpix.saveFile(io, \u0026resized, \"output.png\");\n    try zpix.saveFile(io, \u0026resized, \"output.jpg\");\n\n    // Or save a specific format\n    try zpix.savePngFile(io, \u0026resized, \"output.png\");\n    try zpix.saveJpegFile(io, \u0026resized, \"output.jpg\", 90); // quality 1-100\n}\n```\n\n### Memory API\n\n```zig\nconst zpix = @import(\"zpix\");\n\n// Load/save from memory buffers\nvar image = try zpix.loadPngMemory(allocator, png_bytes);\ndefer image.deinit();\n\nconst png_output = try zpix.savePngMemory(allocator, \u0026image);\ndefer allocator.free(png_output);\n\nconst jpeg_output = try zpix.saveJpegMemory(allocator, \u0026image, 90);\ndefer allocator.free(jpeg_output);\n```\n\n## CLI Usage\n\n```bash\n# Crop a region from an image\nzpix crop input.png output.png 100 100 200 200\n\n# Resize an image\nzpix resize input.png output.png 640 480\n\n# Create a square thumbnail (crops to center, then resizes)\nzpix thumbnail input.png thumb.png 128\n\n# Rotate image (90, 180, or 270 degrees clockwise)\nzpix rotate input.png output.png 90\n\n# Flip image (h = horizontal, v = vertical)\nzpix flip input.png output.png h\n```\n\n## Building\n\n```bash\nzig build\n```\n\n## Testing\n\n### Running Tests\n\n```bash\nzig build test              # Run unit tests (58 tests, fast, no C deps)\nzig build integration-test  # Run integration tests (11 tests, vs stb_image)\nzig build test-all          # Run all tests (69 tests)\nzig build bench             # Performance benchmarks\n```\n\n**Test organization:**\n- **Unit tests** (58 tests, ~70ms): No C dependencies, fast feedback\n  - Image operations (18 tests)\n  - PNG encoding/streaming (10 tests)\n  - JPEG behavioral tests (12 tests)\n  - Error handling tests (19 tests)\n- **Integration tests** (11 tests, ~210ms): Pixel-perfect comparison vs stb_image\n  - PNG comparison (7 tests)\n  - JPEG comparison (4 tests)\n\n### Test Fixtures\n\nTest fixtures are located in `test/fixtures/`:\n\n| File | Description | Use Case |\n|------|-------------|----------|\n| `test_rgb_4x4.png` | 4×4 RGB test pattern | Basic decode verification |\n| `test_rgba_4x4.png` | 4×4 RGBA with transparency | Alpha channel testing |\n| `test_gray_8x8.png` | 8×8 grayscale | Grayscale decode |\n| `test_gray_alpha_8x8.png` | 8×8 grayscale + alpha | Grayscale with transparency |\n| `test_interlaced_16x16.png` | 16×16 Adam7 interlaced | Interlaced PNG support |\n| `landscape_600x400.png` | 600×400 photo | Real-world testing |\n| `landscape_interlaced.png` | 600×400 interlaced | Large interlaced image |\n| `test_gray_8x8.jpg` | 8×8 JPEG grayscale | JPEG grayscale |\n| `test_rgb_4x4.jpg` | 4×4 JPEG RGB | JPEG YCbCr 4:4:4 |\n| `test_rgb_4x4_progressive.jpg` | 4×4 progressive JPEG | Progressive JPEG with refinement scans |\n| `landscape_600x400.jpg` | 600×400 JPEG photo | JPEG with subsampling |\n\n### Comparison Testing\n\nThe test suite compares zpix output against the C reference implementation (stb_image):\n\n```zig\n// Example from test/test_png.zig\ntest \"PNG decoder produces same output as stb_image for RGB\" {\n    const allocator = std.testing.allocator;\n\n    // Load with C reference (stb_image)\n    const ref = stb_load_png(\"test/fixtures/test_rgb_4x4.png\");\n    defer stb_free(ref.data);\n\n    // Load with Zig implementation\n    var zig_image = try zpix.loadPngFile(std.testing.io, allocator, \"test/fixtures/test_rgb_4x4.png\");\n    defer zig_image.deinit();\n\n    // Compare pixel-by-pixel\n    try std.testing.expectEqualSlices(u8, ref_slice, zig_image.data);\n}\n```\n\n**What's tested:**\n- Pixel-perfect output matching stb_image for all formats\n- Progressive JPEG (all scan types, refinement scans)\n- Edge cases (interlacing, different color types, subsampling)\n- Error handling (invalid files, truncated data, corrupt markers)\n- Image operations (crop, resize, rotate, flip)\n- Round-trip encoding/decoding\n- Memory leak detection\n\n**Adding new test fixtures:**\n1. Add image file to `test/fixtures/`\n2. Create comparison test in `test/test_png.zig` or `test/test_jpeg.zig`\n3. Verify zpix output matches stb_image byte-for-byte\n\n## Benchmarks\n\nCompare zpix performance against the C reference (stb_image):\n\n```bash\nzig build bench                                    # Full comparison table\nzig build bench -- png-decode-zig                  # Single benchmark (for memory profiling)\n/usr/bin/time -v zig-out/bin/bench png-decode-zig  # Measure peak memory\n```\n\nAvailable individual benchmarks: `png-decode-zig`, `png-decode-c`, `jpeg-decode-zig`, `jpeg-decode-c`, `png-encode-zig`, `png-encode-c`, `resize-zig`, `resize-c`.\n\n## Test Images\n\nThe test fixture `landscape_600x400.png` is a photo of Cinque Terre, Italy, sourced from W3Schools and used for testing purposes.\n\n## License\n\nMIT License. See [MIT.txt](MIT.txt) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdiogok%2Fzpix","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdiogok%2Fzpix","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdiogok%2Fzpix/lists"}