{"id":13626710,"url":"https://github.com/zigimg/zigimg","last_synced_at":"2025-10-07T14:51:17.358Z","repository":{"id":38357647,"uuid":"230812549","full_name":"zigimg/zigimg","owner":"zigimg","description":"Zig library for reading and writing different image formats","archived":false,"fork":false,"pushed_at":"2025-09-22T13:11:02.000Z","size":1491,"stargazers_count":680,"open_issues_count":38,"forks_count":115,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-09-26T00:08:34.677Z","etag":null,"topics":["bitmap","bmp","image","image-processing","netpbm","pcx","png","png-decoder","zig","zig-library","zig-package"],"latest_commit_sha":null,"homepage":"","language":"Zig","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/zigimg.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":"2019-12-29T22:52:29.000Z","updated_at":"2025-09-25T03:16:01.000Z","dependencies_parsed_at":"2023-12-30T17:52:46.199Z","dependency_job_id":"46002394-927b-4288-b8f5-202eaf2e1a53","html_url":"https://github.com/zigimg/zigimg","commit_stats":{"total_commits":428,"total_committers":38,"mean_commits":"11.263157894736842","dds":"0.36448598130841126","last_synced_commit":"5b5d718159c6ec223a54c9bb960690576e5df9c2"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/zigimg/zigimg","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zigimg%2Fzigimg","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zigimg%2Fzigimg/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zigimg%2Fzigimg/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zigimg%2Fzigimg/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zigimg","download_url":"https://codeload.github.com/zigimg/zigimg/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zigimg%2Fzigimg/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278794400,"owners_count":26046968,"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","status":"online","status_checked_at":"2025-10-07T02:00:06.786Z","response_time":59,"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":["bitmap","bmp","image","image-processing","netpbm","pcx","png","png-decoder","zig","zig-library","zig-package"],"created_at":"2024-08-01T21:02:27.340Z","updated_at":"2025-10-07T14:51:17.352Z","avatar_url":"https://github.com/zigimg.png","language":"Zig","readme":"# Zig Image library\r\n\r\nThis is a work in progress library to create, process, read and write different image formats with [Zig](https://ziglang.org/) programming language.\r\n\r\n![License](https://img.shields.io/github/license/zigimg/zigimg) ![Issue](https://img.shields.io/github/issues-raw/zigimg/zigimg?style=flat) ![Commit](https://img.shields.io/github/last-commit/zigimg/zigimg) ![CI](https://github.com/zigimg/zigimg/workflows/CI/badge.svg)\r\n\r\n[![Join our Discord!](https://discordapp.com/api/guilds/1161009516771549374/widget.png?style=banner2)](https://discord.gg/TYgEEuEGnK)\r\n\r\n## Install \u0026 Build\r\n\r\nThis library currently uses zig [0.15.1](https://ziglang.org/download/), we do plan to go back to using mach nominated zig until a newer version than 0.15.1 will be nominated.\r\n\r\n### Use zigimg in your project\r\n\r\nHow to add to your project:\r\n\r\n#### As a submodule\r\n\r\n1. Clone this repository or add as a submodule\r\n1. Add to your `build.zig`\r\n```\r\npub fn build(b: *std.Build) void {\r\n    exe.root_module.addAnonymousModule(\"zigimg\", .{ .root_source_file = b.path(\"zigimg.zig\") });\r\n}\r\n```\r\n\r\n#### Through the package manager\r\n\r\n1. Run this command in your project folder to add `zigimg` to your `build.zig.zon`\r\n\r\n```sh\r\nzig fetch --save git+https://github.com/zigimg/zigimg.git\r\n```\r\n\r\n2. Get the module in your `build.zig` file\r\n\r\n```zig\r\nconst zigimg_dependency = b.dependency(\"zigimg\", .{\r\n    .target = target,\r\n    .optimize = optimize,\r\n});\r\n\r\nexe.root_module.addImport(\"zigimg\", zigimg_dependency.module(\"zigimg\"));\r\n```\r\n\r\nAfter you are done setting up, you can look at the user guide below.\r\n\r\n## Test suite\r\n\r\nTo run the test suite, checkout the [test suite](https://github.com/zigimg/test-suite) and run\r\n\r\n1. Checkout zigimg\r\n1. Go back one folder and checkout the [test suite](https://github.com/zigimg/test-suite) \r\n1. Run the tests with `zig build`\r\n```\r\nzig build test\r\n```\r\n\r\n## Supported image formats\r\n\r\n| Image Format  | Read          | Write          |\r\n| ------------- |:-------------:|:--------------:|\r\n| ANIM          | ❌            | ❌            |\r\n| BMP           | ✔️ (Partial)  | ✔️ (Partial)  |\r\n| Farbfeld      | ✔️            | ✔️            |\r\n| GIF           | ✔️            | ❌            |\r\n| ICO           | ❌            | ❌            |\r\n| IFF           | ✔️            | ❌            |\r\n| JPEG          | ✔️ (Partial)  | ❌            |\r\n| PAM           | ✔️            | ✔️            |\r\n| PBM           | ✔️            | ✔️            |\r\n| PCX           | ✔️            | ✔️            |\r\n| PGM           | ✔️ (Partial)  | ✔️ (Partial)  |\r\n| PNG           | ✔️            | ✔️ (Partial)  |\r\n| PPM           | ✔️ (Partial)  | ✔️ (Partial)  |\r\n| QOI           | ✔️            | ✔️            |\r\n| SGI           | ✔️            | ❌            |\r\n| SUN           | ✔️            | ❌            |\r\n| TGA           | ✔️            | ✔️            |\r\n| TIFF          | ✔️ (Partial)  | ❌            |\r\n| XBM           | ✔️            | ❌            |\r\n| XPM           | ❌            | ❌            |\r\n\r\n### BMP - Bitmap\r\n\r\n* version 4 BMP\r\n* version 5 BMP\r\n* 24-bit RGB read \u0026 write\r\n* 32-bit RGBA read \u0026 write\r\n* Doesn't support any compression\r\n\r\n### GIF - Graphics Interchange Format\r\n\r\n* Support GIF87a and GIF89a\r\n* Support animated GIF with Netscape application extension for looping information\r\n* Supports interlaced\r\n* Supports tiled and layered images used to achieve pseudo true color and more.\r\n* The plain text extension is not supported\r\n\r\n### IFF - InterchangeFileFormat\r\n\r\n * Supports 1-8 bit, 24 bit, HAM6/8, EHB ILBM files\r\n * Supports uncompressed, byterun 1 \u0026 2 (Atari) compressed ILBM files\r\n * Supports PBM (Deluxe Paint DOS) encoded files\r\n * Supports ACBM (Amiga Basic) files\r\n * Color cycle chunks are ignored\r\n * Mask is not supported (skipped)\r\n\r\n### JPEG - Joint Photographic Experts Group\r\n\r\n * 8-bit baseline and progressive\r\n\r\n### PAM - Portable Arbitrary Map\r\n\r\nCurrently, this only supports a subset of PAMs where:\r\n* The tuple type is official (see `man 5 pam`) or easily inferred (and by extension, depth is 4 or less)\r\n* All the images in a sequence have the same dimensions and maxval (it is technically possible to support animations with different maxvals and tuple types as each `AnimationFrame` has its own `PixelStorage`, however, this is likely not expected by users of the library)\r\n* Grayscale,\r\n* Grayscale with alpha\r\n* Rgb555\r\n* Rgb24 and Rgba32\r\n* Bgr24 and Bgra32\r\n* Rgb48 and Rgba64\r\n\r\n### PBM - Portable Bitmap format\r\n\r\n* Everything is supported\r\n\r\n### PCX - ZSoft Picture Exchange format\r\n\r\n* Support monochrome, 4 color, 16 color and 256 color indexed images\r\n* Support 24-bit RGB images\r\n\r\n### PGM - Portable Graymap format\r\n\r\n* Support 8-bit and 16-bit grayscale images\r\n* 16-bit ascii grayscale loading not tested\r\n\r\n### PNG - Portable Network Graphics\r\n\r\n* Support all pixel formats supported by PNG (grayscale, grayscale+alpha, indexed, truecolor, truecolor with alpha) in 8-bit or 16-bit.\r\n* Support the mininal chunks in order to decode the image.\r\n* Can write all supported pixel formats but writing interlaced images is not supported yet.\r\n\r\n### PPM - Portable Pixmap format\r\n\r\n* Support 24-bit RGB (8-bit per channel)\r\n* Missing 48-bit RGB (16-bit per channel)\r\n\r\n### QOI - Quite OK Image format\r\n\r\n* Imported from https://github.com/MasterQ32/zig-qoi with blessing of the author\r\n\r\n### SGI - Silicon Graphics Image\r\n\r\n* Supports 8-bit, RGB (24/48-bit), RGBA(32/64-bit) files\r\n* Supports RLE and uncompressed files\r\n\r\n### SUN - Sun Raster format\r\n\r\n* Supports 1/8/24/32-bit files\r\n* Supports uncompressed \u0026 RLE files\r\n* Supports BGR/RGB encoding\r\n* TIFF/IFF/Experimental encoding is not supported\r\n\r\n### TGA - Truevision TGA format\r\n\r\n* Supports uncompressed and compressed 8-bit grayscale, indexed with 16-bit and 24-bit colormap, truecolor with 16-bit(RGB555), 24-bit or 32-bit bit depth.\r\n* Supports reading version 1 and version 2\r\n* Supports writing version 2\r\n\r\n### TIFF - Tagged Image File Format\r\n\r\n#### What's supported:\r\n* bilevel, grayscale, palette and RGB(A) files\r\n* most _baseline_ tags\r\n* Raw, LZW, Deflate, PackBits, CCITT 1D files\r\n* big-endian (MM) and little-endian (II) files should both be decoded fine\r\n\r\n#### What's missing:\r\n* Tile-based files are not supported\r\n* YCbCr, CMJN and CIE Lab files are not supported\r\n* JPEG, CCITT Fax 3 / 4 are not supported yet\r\n\r\n#### Notes\r\n* Only the first IFD is decoded\r\n* Orientation tag is not supported yet\r\n\r\n### XBM - X BitMap format\r\n* Everything is supported\r\n\r\n## Supported Pixel formats\r\n\r\n* **Indexed**: 1bpp (bit per pixel), 2bpp, 4bpp, 8bpp, 16bpp\r\n* **Grayscale**: 1bpp, 2bpp, 4bpp, 8bpp, 16bpp, 8bpp with alpha, 16bpp with alpha\r\n* **Truecolor**: RGB332, RGB555, RGB565, RGB24 (8-bit per channel), RGBA32 (8-bit per channel), BGR555, BGR24 (8-bit per channel), BGRA32 (8-bit per channel), RGB48 (16-bit per channel), RGBA64 (16-bit per channel)\r\n* **float**: 32-bit float RGBA, this is the neutral format.\r\n\r\n# User Guide\r\n\r\n## Design philosophy\r\n\r\nzigimg offers color and image functionality. The library is designed around either using the convenient `Image` (or `Image.Managed`) struct that can read and write image formats no matter the format.\r\n\r\nOr you can also use the image format directly in case you want to extract more data from the image format. So if you find that `Image` does not give you the information that you need from a PNG or other format, you can use the PNG format albeit with a more manual API that `Image` hide from you.\r\n\r\n## `Image` vs `Image.Managed`\r\n\r\n`Image` does not bundle a memory allocator and `Image.Managed` does, similar to `std.ArrayList()` and `std.array_list.Managed` in Zig standard library. For all the examples we are going to use `Image` but the API is similar when using `Image.Managed`.\r\n\r\n## Buffer requirements\r\n\r\nStarting with Zig 0.15, all the I/O operations on file can accept a buffer for buffering. zigimg requires valid buffering buffers for reading from files and writing images to file. You can use `zigimg.io.DEFAULT_BUFFER_SIZE` as a size for the buffer you pass to the various file functions.\r\n\r\n## Read an image\r\n\r\nIt is pretty straightforward to read an image using the `Image` struct.\r\n\r\n### From a file\r\n\r\nYou can use either a file path\r\n\r\n```zig\r\nconst std = @import(\"std\");\r\nconst zigimg = @import(\"zigimg\");\r\n\r\npub fn main() !void {\r\n    const allocator = std.heap.smp_allocator;\r\n\r\n    var read_buffer: [zigimg.io.DEFAULT_BUFFER_SIZE]u8 = undefined;\r\n    var image = try zigimg.Image.fromFilePath(allocator, read_buffer[0..], \"my_image.png\");\r\n    defer image.deinit(allocator);\r\n\r\n    // Do something with your image\r\n}\r\n```\r\n\r\nor a `std.fs.File` directly\r\n\r\n```zig\r\nconst std = @import(\"std\");\r\nconst zigimg = @import(\"zigimg\");\r\n\r\npub fn main() !void {\r\n    const allocator = std.heap.smp_allocator;\r\n\r\n    var file = try std.fs.cwd().openFile(file_path, .{});\r\n    defer file.close();\r\n\r\n    var read_buffer: [zigimg.io.DEFAULT_BUFFER_SIZE]u8 = undefined;\r\n    var image = try zigimg.Image.fromFile(allocator, read_buffer[0..], \u0026file);\r\n    defer image.deinit(allocator);\r\n\r\n    // Do something with your image\r\n}\r\n```\r\n\r\n### From memory\r\n\r\n```zig\r\nconst std = @import(\"std\");\r\nconst zigimg = @import(\"zigimg\");\r\n\r\nconst image_data = @embedFile(\"test.bmp\");\r\n\r\npub fn main() !void {\r\n    const allocator = std.heap.smp_allocator;\r\n\r\n    const image = try zigimg.Image.fromMemory(allocator, image_data[0..]);\r\n    defer image.deinit(allocator);\r\n\r\n    // Do something with your image\r\n}\r\n```\r\n\r\n## Accessing pixel data\r\n\r\nFor a single image, they are two ways to get access to the pixel data.\r\n\r\n### Accessing a specific format directly\r\n\r\nYou can access the pixel data directly using `Image.pixels`. `pixels` is an union of all supported pixel formats.\r\n\r\nFor RGB pixel formats, just use the pixel format enum value and addresss the data directly.\r\n```zig\r\npub fn example() void {\r\n    // [...]\r\n    // Assuming you already have an image loaded\r\n\r\n    const first_pixel = image.pixels.rgb24[0];\r\n}\r\n```\r\n\r\nFor grayscale formats, you need to use .value to get the grayscale value. It can also contain the alpha value if you use the grayscale with alpha.\r\n\r\n```zig\r\npub fn example() void {\r\n    // [...]\r\n    // Assuming you already have an image loaded\r\n\r\n    const first_pixel = image.pixels.grayscale8Alpha[0];\r\n    const grayscale = first_pixel.value;\r\n    const alpha = grayscale.alpha;\r\n}\r\n```\r\n\r\nFor indexed formats, you need to first access the union value then either the indices or the palette. The palette color are stored in the `Rgba32` pixel format.\r\n\r\n```zig\r\npub fn example() void {\r\n    // [...]\r\n    // Assuming you already have an image loaded\r\n\r\n    const first_color_palette = image.pixels.indexed8.palette[0];\r\n    const first_pixel = image.pixels.indexed8.indices[0];\r\n}\r\n```\r\n\r\nIf you want to know the current pixel format use `Image.pixelFormat()`.\r\n\r\n### Using the color iterator\r\n\r\nYou can use the iterator to get each pixel as the universal `Colorf32` pixel format. (32-bit floating ploint RGBA)\r\n\r\n```zig\r\npub fn example() void {\r\n    // [...]\r\n    // Assuming you already have an image loaded\r\n\r\n    const color_it = image.iterator();\r\n\r\n    while (color_it.next()) |color| {\r\n        // Do something with color\r\n    }\r\n}\r\n```\r\n\r\n### Accessing animation frames\r\n\r\nIn the case of an `Image` containing multiple frames, you can use `Image.animation` to get access to the animation information. Use `Image.animation.frames` to access each indivial frame. Each frame contain the pixel data and a frame duration in seconds (32-bit floating point).\r\n\r\n`Image.pixels` will always point to the first frame of an animation also.\r\n\r\n```zig\r\npub fn example() void {\r\n    // [...]\r\n    // Assuming you already have an image loaded\r\n\r\n    const loop_count = image.animation.loop_count;\r\n\r\n    for (image.animation.frames) |frame| {\r\n        const rgb24_data = frame.pixels.rgb24;\r\n        const frame_duration = frame.duration;\r\n    }\r\n}\r\n```\r\n\r\n### Get raw bytes for texture transfer\r\n\r\n`Image` has helper functions to help you get the right data to upload your image to the GPU.\r\n\r\n```zig\r\npub fn example() void {\r\n    // [...]\r\n    // Assuming you already have an image loaded\r\n\r\n    const image_data = image.rawBytes();\r\n    const row_pitch = image.rowByteSize();\r\n    const image_byte_size = image.imageByteSize();\r\n}\r\n```\r\n\r\n## Detect image format\r\n\r\nYou can query the image format used by a file or a memory buffer.\r\n\r\n### From a file\r\n\r\nYou can use either a file path\r\n\r\n```zig\r\nconst std = @import(\"std\");\r\nconst zigimg = @import(\"zigimg\");\r\n\r\npub fn main() !void {\r\n    var read_buffer: [zigimg.io.DEFAULT_BUFFER_SIZE]u8 = undefined;\r\n    const image_format = try zigimg.Image.detectFormatFromFilePath(allocator, \"my_image.png\", read_buffer[0..]);\r\n\r\n    // Will print png\r\n    std.log.debug(\"Image format: {}\", .{image_format});\r\n}\r\n```\r\n\r\nor a `std.fs.File` directly\r\n\r\n```zig\r\nconst std = @import(\"std\");\r\nconst zigimg = @import(\"zigimg\");\r\n\r\npub fn main() !void {\r\n    var file = try std.fs.cwd().openFile(\"my_image.gif\", .{});\r\n    defer file.close();\r\n\r\n    var read_buffer: [zigimg.io.DEFAULT_BUFFER_SIZE]u8 = undefined;\r\n    const image_format = try zigimg.Image.detectFormatFromFile(allocator, file, read_buffer[0..]);\r\n\r\n    // Will print gif\r\n    std.log.debug(\"Image format: {}\", .{image_format});\r\n}\r\n```\r\n\r\n### From memory\r\n\r\n```zig\r\nconst std = @import(\"std\");\r\nconst zigimg = @import(\"zigimg\");\r\n\r\nconst image_data = @embedFile(\"test.bmp\");\r\n\r\npub fn main() !void {\r\n    const image_format = try zigimg.Image.detectFormatFromMemory(allocator, image_data[0..]);\r\n\r\n    // Will print bmp\r\n    std.log.debug(\"Image format: {}\", .{image_format});\r\n}\r\n```\r\n\r\n## Write an image\r\n\r\nEach 3 functions to write an image take a union of encoder options for the target format. To know the actual options you'll need to consult the source code. The active tag of the union determine the target format, not the file extension.\r\n\r\n### Write to a file path\r\n\r\n```zig\r\npub fn example() !void {\r\n    // [...]\r\n    // Assuming you already have an image loaded\r\n\r\n    var write_buffer: [zigimg.io.DEFAULT_BUFFER_SIZE]u8 = undefined;\r\n    try image.writeToFilePath(\"my_new_image.png\", write_buffer[0..], .{ .png = .{} });\r\n\r\n    // Or with encoder options\r\n    try image.writeToFilePath(\"my_new_image.png\", write_buffer[0..]. .{ .png = .{ .interlaced = true } });\r\n}\r\n```\r\n\r\n### Write to `std.fs.File`\r\n\r\n```zig\r\npub fn example() !void {\r\n    // [...]\r\n    // Assuming you already have an image loaded and the file already created\r\n\r\n    var write_buffer: [zigimg.io.DEFAULT_BUFFER_SIZE]u8 = undefined;\r\n    try image.writeToFile(file, write_buffer[0..], .{ .bmp = .{} });\r\n}\r\n```\r\n\r\n### Write to a memory buffer\r\n\r\nEnsure that you have enough place in your buffer before calling `writeToMemory()`\r\n\r\n```zig\r\npub fn example() !void {\r\n    // [...]\r\n    // Assuming you already have an image loaded and the buffer already allocated\r\n\r\n    // `allocator` is used for incidental allocations in the writing process.\r\n    // `buffer` is where the image will be written to. It must be large enough to fit the output.\r\n    try image.writeToMemory(allocator, buffer[0..], .{ .tga = .{} });\r\n}\r\n```\r\n\r\n## Create an image\r\n\r\nUse `Image.create()` and pass the width, height and the pixel format that you want.\r\n\r\n```zig\r\nconst std = @import(\"std\");\r\nconst zigimg = @import(\"zigimg\");\r\n\r\npub fn main() !void {\r\n    const allocator = std.heap.smp_allocator;\r\n\r\n    var image = try zigimg.Image.create(allocator, 1920, 1080, .rgba32);\r\n    defer image.deinit(allocator);\r\n\r\n    // Do something with your image\r\n}\r\n```\r\n\r\n## Interpret raw pixels\r\n\r\nIf you are not dealing with a image format, you can import your pixel data using `Image.fromRawPixels()`. It will create a copy of the pixels data. If you want the image to take ownership or just pass the data along to write it to a image format, use `Image.fromRawPixelsOwned()`.\r\n\r\nUsing `fromRawPixel()`:\r\n```zig\r\nconst std = @import(\"std\");\r\nconst zigimg = @import(\"zigimg\");\r\n\r\npub fn main() !void {\r\n    const allocator = std.heap.smp_allocator;\r\n\r\n    const my_raw_pixels = @embedData(\"raw_bgra32.bin\");\r\n\r\n    var image = try zigimg.Image.fromRawPixels(allocator, 1920, 1080, my_raw_pixels[0..], .bgra32);\r\n    defer image.deinit(allocator);\r\n\r\n    // Do something with your image\r\n}\r\n```\r\n\r\nUsing `fromRawPixelsOwned()`:\r\n```zig\r\nconst std = @import(\"std\");\r\nconst zigimg = @import(\"zigimg\");\r\n\r\npub fn main() !void {\r\n    const allocator = std.heap.smp_allocator;\r\n\r\n    const my_raw_pixels = @embedData(\"raw_bgra32.bin\");\r\n\r\n    var image = try zigimg.Image.fromRawPixelsOwned(1920, 1080, my_raw_pixels[0..], .bgra32);\r\n\r\n    // Do something with your image\r\n}\r\n```\r\n\r\n## Use image format directly\r\n\r\nIn the case you want more direct access to the image format, all the image formats are accessible from the `zigimg` module. However, you'll need to do a bit more manual steps in order to retrieve the pixel data.\r\n\r\n```zig\r\nconst std = @import(\"std\");\r\nconst zigimg = @import(\"zigimg\");\r\n\r\npub fn main() !void {\r\n    const allocator = std.heap.smp_allocator;\r\n\r\n    const image_data = @embedFile(\"windows_rgba_v5.bmp\");\r\n\r\n    var read_stream = zigimg.io.ReadStream.initMemory(image_data);\r\n\r\n    var bmp = zigimg.formats.bmp.BMP{};\r\n\r\n    const pixels = try bmp.read(allocator, read_stream.reader());\r\n    defer pixels.deinit(allocator);\r\n\r\n    std.log.info(\"BMP info header: {}\", .{bmp.info_header});\r\n}\r\n```\r\n\r\nFor the curious, the program above generate the following output:\r\n```\r\ninfo: BMP info header: src.formats.bmp.BitmapInfoHeader{ .v5 = src.formats.bmp.BitmapInfoHeaderV5{ .header_size = 124, .width = 240, .height = 160, .color_plane = 1, .bit_count = 32, .compression_method = src.formats.bmp.CompressionMethod.bitfields, .image_raw_size = 153600, .horizontal_resolution = 2835, .vertical_resolution = 2835, .palette_size = 0, .important_colors = 0, .red_mask = 16711680, .green_mask = 65280, .blue_mask = 255, .alpha_mask = 4278190080, .color_space = src.formats.bmp.BitmapColorSpace.srgb, .cie_end_points = src.formats.bmp.CieXyzTriple{ .red = src.formats.bmp.CieXyz{ ... }, .green = src.formats.bmp.CieXyz{ ... }, .blue = src.formats.bmp.CieXyz{ ... } }, .gamma_red = 0, .gamma_green = 0, .gamma_blue = 0, .intent = src.formats.bmp.BitmapIntent.graphics, .profile_data = 0, .profile_size = 0, .reserved = 0 } }\r\n```\r\n\r\n## Convert between pixel formats\r\n\r\nYou can use `Image.convert()` to convert between pixel formats. It will allocate the new pixel data and free the old one for you. It supports conversion from and to any pixel format. When converting down to indexed format, no dithering is done.\r\n\r\n```zig\r\npub fn example() !void {\r\n    // [...]\r\n    // Assuming you already have an image loaded\r\n\r\n    try image.convert(allocator, .float32);\r\n}\r\n```\r\n\r\n### PixelFormatConverter\r\n\r\nIf you prefer, you can use `PixelFormatConverter` directly.\r\n\r\n```zig\r\npub fn example(allocator: std.mem.Allocator) !void {\r\n    const indexed2_pixels = try zigimg.color.PixelStorage.init(allocator, .indexed2, 4);\r\n    defer indexed2_pixels.deinit(allocator);\r\n\r\n    // [...] Setup your indexed2 pixel data\r\n\r\n    const bgr24_pixels = try zigimg.PixelFormatConverter.convert(allocator, \u0026indexed2_pixels, .bgr24);\r\n    defer bgr24_pixels.deinit(allocator);\r\n}\r\n```\r\n\r\n### OctTreeQuantizer\r\n\r\nIf you prefer more granular control to create an indexed image, you can use the `OctTreeQuantizer` directly.\r\n\r\n```zig\r\npub fn example(allocator: std.mem.Allocator) !void {\r\n    const image_data = @embedFile(\"windows_rgba_v5.bmp\");\r\n\r\n    var image = try zigimg.Image.fromMemory(allocator, image_data[0..]);\r\n    defer image.deinit(allocator);\r\n\r\n    var quantizer = zigimg.OctTreeQuantizer.init(allocator);\r\n    defer quantizer.deinit();\r\n\r\n    var color_it = image.iterator();\r\n\r\n    while (color_it.next()) |pixel| {\r\n        try quantizer.addColor(pixel);\r\n    }\r\n\r\n    var palette_storage: [256]zigimg.color.Rgba32 = undefined;\r\n    const palette = quantizer.makePalette(255, palette_storage[0..]);\r\n\r\n    const palette_index = try quantizer.getPaletteIndex(zigimg.color.Rgba32.from.rgba(110, 0, 0, 255));\r\n}\r\n```\r\n\r\n## Get a color from a HTML hex string\r\n\r\nYou can get a color from a HTML hex string. The alpha component is always last. It also supports the shorthand version.\r\n\r\n```zig\r\npub fn example() !void {\r\n    const rgb24 = try zigimg.color.Rgb24.from.htmlHex(\"#123499\");\r\n    const rgba32 = try zigimg.color.Rgba32.from.htmlHex(\"FF000045\");\r\n\r\n    const red_rgb24 = try zigimg.color.Rgb24.from.htmlHex(\"#F00\");\r\n    const blue_rgba32 = try zigimg.color.Rgba32.from.htmlHex(\"#00FA\");\r\n}\r\n```\r\n\r\n## Predefined colors\r\n\r\nYou can access predefined colors for any pixel format using `Colors()`.\r\n\r\n```zig\r\nconst std = @import(\"std\");\r\nconst zigimg = @import(\"zigimg\");\r\n\r\npub fn main() !void {\r\n    const red_float32 = zigimg.Colors(zigimg.color.Colorf32).Red;\r\n    const blue_rgb24 = zigimg.Colors(zigimg.color.Rgb24).Blue;\r\n}\r\n```\r\n\r\n## Color management \u0026 color space\r\n\r\nWhile zigimg does not support ICC profile yet (see issue #36) it does support a variety of color models and color spaces. All color space and color model are done in 32-bit floating point. So if you are not using `Colorf32` / `float32` as your pixel format, you'll need to convert to that format first.\r\n\r\nThe following device-dependent color model are supported:\r\n* HSL (Hue, Saturation, Luminance)\r\n* HSV (Hue, Saturation, Value) or also known as HSB (Hue, Saturation, Brightness)\r\n* CMYK (Cyan-Magenta-Yellow-Black)\r\n\r\nThe following device-inpendent color spaces are supported, with or without alpha:\r\n* CIE XYZ\r\n* CIE Lab\r\n* CIE LCh(ab), the cylindral representation of CIE Lab\r\n* CIE Luv\r\n* CIE LCh(uv), the cylindral representation of CIE Luv\r\n* [HSLuv](https://www.hsluv.org/), a HSL representation of CIE LCh(uv) which is a cylindrical representation of CIE Luv color space\r\n* [Oklab](https://bottosson.github.io/posts/oklab/)\r\n* Oklch, the cylindrical representation of Oklab\r\n\r\n### Convert between linear and gamma-corrected color\r\n\r\nAll color space transformation are done assuming a linear version of the color. To convert between gamma-converted and linear, you need to use any RGB colorspace and then call `toGamma()` or `toLinear()`, in this example I'm using both `sRGB` and `BT709` (aka Rec.709).\r\n\r\nYou can use either the accurate version or the fast version. For example the sRGB transfer function is linear below a threshold and an exponent curve above the threshold but the fast version will use the approximate exponent curve for the whole range.\r\n\r\n```zig\r\npub fn example(linear_color: zigimg.color.Colorf32) {\r\n    const gamma_srgb = zigimg.color.sRGB.toGamma(linear_color);\r\n    const gamma_bt709 = zigimg.color.BT709.toGammaFast(linear_color);\r\n\r\n    const linear_srgb = zigimg.color.sRGB.toLinearFast(gamma_srgb);\r\n    const linear_bt709 = zigimg.color.BT709.toLinear(gamma_bt609);\r\n}\r\n```\r\n\r\n### Convert a single color to a different color space\r\n\r\nTo convert to a device independant color space, you need first to use a reference RGB color space. Usually the most common for computer purposes is `sRGB`. Then each RGB colorspace has functions to convert from and to various color spaces. They support both non-alpha and alpha of the color space.\r\n\r\nTo a color space:\r\n```zig\r\npub fn example(linear_color: zigimg.color.Colorf32) void {\r\n    const xyz = zigimg.color.sRGB.toXYZ(linear_color);\r\n    const lab_alpha = zigimg.color.sRGB.toLabAlpha(linear_color);\r\n    const lch_ab = zigimg.color.sRGB.toLCHab(linear_color);\r\n    const luv_alpha = zigimg.color.sRGB.toLuvAlpha(linear_color);\r\n    const lch_uv = zigimg.color.sRGB.toLCHuv(linear_color);\r\n    const hsluv = zigimg.color.sRGB.toHSLuv(linear_color);\r\n    const oklab = zigimg.color.sRGB.toOklab(linear_color);\r\n    const oklch = zigimg.color.sRGB.toOkLCh(linear_color);\r\n}\r\n```\r\n\r\nWhen converting from a color space to a RGB color space, you need to specify if you want the color to be clamped inside the RGB colorspace or not because the resulting color could be outside of the RGB color space.\r\n```zig\r\npub fn example(oklab: zigimg.color.Oklab) {\r\n    const linear_srgb_clamped = zigimg.color.sRGB.fromOklab(oklab, .clamp);\r\n    const linear_srgb = zigimg.color.sRGB.fromOklab(oklab, .none);\r\n}\r\n```\r\n\r\n### Convert a slice of color to a different color space\r\n\r\nConverting each pixel individually will be tedious if you want to use image processing on the CPU. Almost all color space conversion offer\r\nan slice in-place conversion or a slice copy conversion. The in-place will reuse the same memory but interpret the color data differently. When you are conversion from a color space to a RGB color space, you need to specify if you want clamping or not.\r\n\r\nThose conversions are only available with the alpha version of each color space.\r\n\r\n```zig\r\npub fn exampleInPlace(linear_srgb_image: []zigimg.color.Colorf32) void {\r\n    const slice_lab_alpha = zigimg.color.sRGB.sliceToLabAlphaInPlace(linear_srgb_image);\r\n\r\n    // Do your image manipulation in CIE L*a*b*\r\n\r\n    // Convert back to linear sRGB\r\n    _ = zigimg.color.sRGB.sliceFromLabAlphaInPlace(slice_lab_alpha, .clamp);\r\n\r\n    // or without clamping\r\n     _ = zigimg.color.sRGB.sliceFromLabAlphaInPlace(slice_lab_alpha, .none);\r\n}\r\n\r\npub fn exampleCopy(allocator: std.mem.Allocator, linear_srgb_image: []const zigimg.color.Colorf32) ![]zigimg.color.Colorf32 {\r\n    const slice_oklab_alpha = try zigimg.color.sRGB.sliceToOklabCopy(allocator, linear_srgb_image);\r\n\r\n    // Do your image manipulatioon in Oklab\r\n\r\n    // Convert back to linear sRGB\r\n    return try zigimg.color.sRGB.sliceFromOklabCopy(allocator, slice_oklab_alpha, .clamp);\r\n\r\n    // Or without clamping\r\n    return try zigimg.color.sRGB.sliceFromOklabCopy(allocator, slice_oklab_alpha, .none);\r\n}\r\n```\r\n\r\n### Convert between some cylindrical representation\r\n\r\nCIE Lab, CIE Luv and Oklab have cylindrical representation of their color space, each color has functions to convert from and to the cylindrical version.\r\n\r\n```zig\r\npub fn example() void {\r\n    const lab = zigimg.color.CIELab{ .l = 0.12, .a = -0.23, .b = 0.56 };\r\n    const luv_alpha = zigimg.color.CIELuvAlpha { .l = 0.4, .u = 0.5, .v = -0.2, .alpha = 0.8 };\r\n    const oklab = zigimg.color.Oklab{ .l = 0.67, .a = 0.1, .b = 0.56 };\r\n\r\n    const lch_ab = lab.toLCHab();\r\n    const lch_uv_alpha = luv_alpha.toLCHuvAlpha();\r\n    const oklch = oklab.toOkLCh();\r\n\r\n    const result_lab = lch_ab.toLab();\r\n    const result_luv_alpha = lch_uv_alpha.toLuvAlpha();\r\n    const result_oklab = oklch.toOklab();\r\n}\r\n```\r\n\r\n### Convert color between RGB color spaces\r\n\r\nTo convert a single color, use the `convertColor()` function on the `RgbColorspace` struct:\r\n```zig\r\npub fn example(linear_color: zigimg.color.Colorf32) void {\r\n    const pro_photo_color = zigimg.color.sRGB.convertColor(zigimg.color.ProPhotoRGB, linear_color);\r\n}\r\n```\r\n\r\nIf you want to convert a whole slice of pixels, use `convertColors()`, it will apply the conversion in-place:\r\n```zig\r\npub fn example(linear_image: []zigimg.color.Colorf32) void {\r\n    const adobe_image = zigimg.color.sRGB.convertColors(zigimg.color.AdobeRGB, linear_image);\r\n}\r\n```\r\n\r\nIf the target RGB colorspace have a different white point, it will do the [chromatic adapdation](http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html) for you using the Bradford method.\r\n\r\n### Predefined RGB color spaces\r\n\r\nHere the list of predefined RGB color spaces, all accessible from `zigimg.color` struct:\r\n\r\n* `BT601_NTSC`\r\n* `BT601_PAL`\r\n* `BT709`\r\n* `sRGB`\r\n* `DCIP3.Display`\r\n* `DCIP3.Theater`\r\n* `DCIP3.ACES`\r\n* `BT2020`\r\n* `AdobeRGB`\r\n* `AdobeWideGamutRGB`\r\n* `ProPhotoRGB`\r\n\r\n### Predefined white points\r\n\r\nAll predefined white point are accessed with `zigimg.color.WhitePoints`. All the standard illuminants are defined there.\r\n\r\n### Create your own RGB color space\r\n\r\nYou can create your own RGB color space using `zigimg.color.RgbColorspace.init()`. Each coordinate is in the 2D version of the CIE xyY color space.\r\n\r\nIf you don't care about linear and gamma conversion, just ignore those functions in the init struct.\r\n\r\n```zig\r\nfn myColorSpaceToGamma(value: f32) f32 {\r\n    return std.math.pow(f32, value, 1.0 / 2.4);\r\n}\r\n\r\nfn myColorSpaceToLinear(value: f32) f32 {\r\n    return std.math.pow(f32, value, 2.4);\r\n}\r\n\r\npub fn example() void {\r\n    pub const my_color_space = zigimg.color.RgbColorspace.init(.{\r\n        .red = .{ .x = 0.6400, .y = 0.3300 },\r\n        .green = .{ .x = 0.3000, .y = 0.6000 },\r\n        .blue = .{ .x = 0.1500, .y = 0.0600 },\r\n        .white = zigimg.color.WhitePoints.D50,\r\n        .to_gamma = myColorSpaceToGamma,\r\n        .to_gamma_fast = myColorSpaceToGamma,\r\n        .to_linear = myColorSpaceToLinear,\r\n        .to_linear_fast = myColorSpaceToLinear,\r\n    });\r\n}\r\n```\r\n","funding_links":[],"categories":["Zig","Libraries","Multimedia \u0026 Graphics"],"sub_categories":["Image and Video Processing"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzigimg%2Fzigimg","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzigimg%2Fzigimg","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzigimg%2Fzigimg/lists"}