Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

Zig library for reading and writing different image formats

License

NotificationsYou must be signed in to change notification settings

zigimg/zigimg

Repository files navigation

This is a work in progress library to create, process, read and write different image formats withZig programming language.

LicenseIssueCommitCI

Join our Discord!

Install & Build

This library uses zig nominated2024.11.0-mach. To install usingzigup:

zigup 0.14.0-dev.2577+271452d22

Use zigimg in your project

How to add to your project:

As a submodule

  1. Clone this repository or add as a submodule
  2. Add to yourbuild.zig
pub fn build(b: *std.Build) void {    exe.root_module.addAnonymousModule("zigimg", .{ .root_source_file = b.path("zigimg.zig") });}

Through the package manager

  1. Run this command in your project folder to addzigimg to yourbuild.zig.zon
zig fetch --save git+https://github.com/zigimg/zigimg.git
  1. Get the module in yourbuild.zig file
constzigimg_dependency=b.dependency("zigimg", .{    .target=target,    .optimize=optimize,});exe.root_module.addImport("zigimg",zigimg_dependency.module("zigimg"));

After you are done setting up, you can look at the user guide below.

Test suite

To run the test suite, checkout thetest suite and run

  1. Checkout zigimg
  2. Go back one folder and checkout thetest suite
  3. Run the tests withzig build
zig build test

Supported image formats

Image FormatReadWrite
ANIM
BMP✔️ (Partial)✔️ (Partial)
Farbfeld✔️✔️
GIF✔️
ICO
IFF✔️
JPEG
PAM✔️✔️
PBM✔️✔️
PCX✔️✔️
PGM✔️ (Partial)✔️ (Partial)
PNG✔️✔️ (Partial)
PPM✔️ (Partial)✔️ (Partial)
QOI✔️✔️
TGA✔️✔️
TIFF
XBM
XPM

BMP - Bitmap

  • version 4 BMP
  • version 5 BMP
  • 24-bit RGB read & write
  • 32-bit RGBA read & write
  • Doesn't support any compression

GIF - Graphics Interchange Format

  • Support GIF87a and GIF89a
  • Support animated GIF with Netscape application extension for looping information
  • Supports interlaced
  • Supports tiled and layered images used to achieve pseudo true color and more.
  • The plain text extension is not supported

IFF - InterchangeFileFormat

  • Supports 1-8 bit, 24 bit, HAM6/8, EHB ILBM files
  • Supports uncompressed, byterun 1 & 2 (Atari) compressed ILBM files
  • Supports PBM (Deluxe Paint DOS) encoded files
  • Supports ACBM (Amiga Basic) files
  • Color cycle chunks are ignored
  • Mask is not supported (skipped)

PAM - Portable Arbitrary Map

Currently, this only supports a subset of PAMs where:

  • The tuple type is official (seeman 5 pam) or easily inferred (and by extension, depth is 4 or less)
  • 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 eachAnimationFrame has its ownPixelStorage, however, this is likely not expected by users of the library)
  • Grayscale,
  • Grayscale with alpha
  • Rgb555
  • Rgb24 and Rgba32
  • Bgr24 and Bgra32
  • Rgb48 and Rgba64

PBM - Portable Bitmap format

  • Everything is supported

PCX - ZSoft Picture Exchange format

  • Support monochrome, 4 color, 16 color and 256 color indexed images
  • Support 24-bit RGB images

PGM - Portable Graymap format

  • Support 8-bit and 16-bit grayscale images
  • 16-bit ascii grayscale loading not tested

PNG - Portable Network Graphics

  • Support all pixel formats supported by PNG (grayscale, grayscale+alpha, indexed, truecolor, truecolor with alpha) in 8-bit or 16-bit.
  • Support the mininal chunks in order to decode the image.
  • Not all images in Png Test Suite is covered but should be good enough for now.

PPM - Portable Pixmap format

  • Support 24-bit RGB (8-bit per channel)
  • Missing 48-bit RGB (16-bit per channel)

QOI - Quite OK Image Format

TGA - Truevision TGA format

  • 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.
  • Supports reading version 1 and version 2
  • Supports writing version 2

Supported Pixel formats

  • Indexed: 1bpp (bit per pixel), 2bpp, 4bpp, 8bpp, 16bpp
  • Grayscale: 1bpp, 2bpp, 4bpp, 8bpp, 16bpp, 8bpp with alpha, 16bpp with alpha
  • 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)
  • float: 32-bit float RGBA, this is the neutral format.

User Guide

Design philosophy

zigimg offers color and image functionality. The library is designed around either using the convenientImage (orImageUnmanaged) struct that can read and write image formats no matter the format.

Or you can also use the image format directly in case you want to extract more data from the image format. So if you find thatImage 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 thatImage hide from you.

Image vsImageUnmanaged

Image bundle a memory allocator andImageUnmanaged does not. Similar tostd.ArrayList() andstd.ArrayListUnmanaged() in Zig standard library. For all the examples we are going to useImage but it is similar withImageUnmanaged.

Read an image

It is pretty straightforward to read an image using theImage struct.

From a file

You can use either a file path

conststd=@import("std");constzigimg=@import("zigimg");pubfnmain()!void {vargpa=std.heap.GeneralPurposeAllocator(.{}){};defer_=gpa.deinit();constallocator=gpa.allocator();varimage=tryzigimg.Image.fromFilePath(allocator,"my_image.png");deferimage.deinit();// Do something with your image}

or astd.fs.File directly

conststd=@import("std");constzigimg=@import("zigimg");pubfnmain()!void {vargpa=std.heap.GeneralPurposeAllocator(.{}){};defer_=gpa.deinit();constallocator=gpa.allocator();varfile=trystd.fs.cwd().openFile(file_path, .{});deferfile.close();varimage=tryzigimg.Image.fromFile(allocator,file);deferimage.deinit();// Do something with your image}

From memory

conststd=@import("std");constzigimg=@import("zigimg");constimage_data=@embedFile("test.bmp");pubfnmain()!void {vargpa=std.heap.GeneralPurposeAllocator(.{}){};defer_=gpa.deinit();constallocator=gpa.allocator();constimage=tryzigimg.Image.fromMemory(allocator,image_data[0..]);deferimage.deinit();// Do something with your image}

Accessing pixel data

For a single image, they are two ways to get access to the pixel data.

Accessing a specific format directly

Yu can access the pixel data directly usingImage.pixels.pixels is an union of all supported pixel formats.

For RGB pixel formats, just use the pixel format enum value and addresss the data directly.

pubfnexample()void {// [...]// Assuming you already have an image loadedconstfirst_pixel=image.pixels.rgb24[0];}

For 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.

pubfnexample()void {// [...]// Assuming you already have an image loadedconstfirst_pixel=image.pixels.grayscale8Alpha[0];constgrayscale=first_pixel.value;constalpha=grayscale.alpha;}

For indexed formats, you need to first access the union value then either the indices or the palette. The palette color are stored in theRgba32 pixel format.

pubfnexample()void {// [...]// Assuming you already have an image loadedconstfirst_color_palette=image.pixels.indexed8.palette[0];constfirst_pixel=image.pixels.indexed8.indices[0];}

If you want to know the current pixel format useImage.pixelFormat().

Using the color iterator

You can use the iterator to get each pixel as the universalColorf32 pixel format. (32-bit floating ploint RGBA)

pubfnexample()void {// [...]// Assuming you already have an image loadedconstcolor_it=image.iterator();while (color_it.next())|color| {// Do something with color    }}

Accessing animation frames

In the case of anImage containing multiple frames, you can useImage.animation to get access to the animation information. UseImage.animation.frames to access each indivial frame. Each frame contain the pixel data and a frame duration in seconds (32-bit floating point).

Image.pixels will always point to the first frame of an animation also.

pubfnexample()void {// [...]// Assuming you already have an image loadedconstloop_count=image.animation.loop_count;for (image.animation.frames)|frame| {constrgb24_data=frame.pixels.rgb24;constframe_duration=frame.duration;    }}

Get raw bytes for texture transfer

Image has helper functions to help you get the right data to upload your image to the GPU.

pubfnexample()void {// [...]// Assuming you already have an image loadedconstimage_data=image.rawBytes();constrow_pitch=image.rowByteSize();constimage_byte_size=image.imageByteSize();}

Detect image format

You can query the image format used by a file or a memory buffer.

From a file

You can use either a file path

conststd=@import("std");constzigimg=@import("zigimg");pubfnmain()!void {constimage_format=tryzigimg.Image.detectFormatFromFilePath(allocator,"my_image.png");// Will print pngstd.log.debug("Image format: {}", .{image_format});}

or astd.fs.File directly

conststd=@import("std");constzigimg=@import("zigimg");pubfnmain()!void {varfile=trystd.fs.cwd().openFile("my_image.gif", .{});deferfile.close();constimage_format=tryzigimg.Image.detectFormatFromFile(allocator,file);// Will print gifstd.log.debug("Image format: {}", .{image_format});}

From memory

conststd=@import("std");constzigimg=@import("zigimg");constimage_data=@embedFile("test.bmp");pubfnmain()!void {constimage_format=tryzigimg.Image.detectFormatFromMemory(allocator,image_data[0..]);// Will print bmpstd.log.debug("Image format: {}", .{image_format});}

Write an image

Each 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.

Write to a file path

pubfnexample()!void {// [...]// Assuming you already have an image loadedtryimage.writeToFilePath("my_new_image.png", .{ .png= .{} });// Or with encoder optionstryimage.writeToFilePath("my_new_image.png", .{ .png= .{ .interlaced=true } });}

Write tostd.fs.File

pubfnexample()!void {// [...]// Assuming you already have an image loaded and the file already createdtryimage.writeToFile(file, .{ .bmp= .{} });}

Write to a memory buffer

Ensure that you have enough place in your buffer before callingwriteToMemory()

pubfnexample()!void {// [...]// Assuming you already have an image loaded and the buffer already allocatedtryimage.writeToMemory(buffer[0..], .{ .tga= .{} });}

Create an image

UseImage.create() and pass the width, height and the pixel format that you want.

conststd=@import("std");constzigimg=@import("zigimg");pubfnmain()!void {vargpa=std.heap.GeneralPurposeAllocator(.{}){};defer_=gpa.deinit();constallocator=gpa.allocator();varimage=tryzigimg.Image.create(allocator,1920,1080,.rgba32);deferimage.deinit();// Do something with your image}

Interpret raw pixels

If you are not dealing with a image format, you can import your pixel data usingImage.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, useImageUnmanaged.fromRawPixelsOwned().

UsingfromRawPixel():

conststd=@import("std");constzigimg=@import("zigimg");pubfnmain()!void {vargpa=std.heap.GeneralPurposeAllocator(.{}){};defer_=gpa.deinit();constallocator=gpa.allocator();constmy_raw_pixels=@embedData("raw_bgra32.bin");varimage=tryzigimg.Image.fromRawPixels(allocator,1920,1080,my_raw_pixels[0..],.bgra32);deferimage.deinit();// Do something with your image}

UsingfromRawPixelsOwned():

conststd=@import("std");constzigimg=@import("zigimg");pubfnmain()!void {vargpa=std.heap.GeneralPurposeAllocator(.{}){};defer_=gpa.deinit();constallocator=gpa.allocator();constmy_raw_pixels=@embedData("raw_bgra32.bin");varimage=tryzigimg.ImageUnmanaged.fromRawPixelsOwned(1920,1080,my_raw_pixels[0..],.bgra32);// Do something with your image}

Use image format directly

In the case you want more direct access to the image format, all the image formats are accessible from thezigimg module. However, you'll need to do a bit more manual steps in order to retrieve the pixel data.

conststd=@import("std");constzigimg=@import("zigimg");pubfnmain()!void {vargpa=std.heap.GeneralPurposeAllocator(.{}){};defer_=gpa.deinit();constallocator=gpa.allocator();constimage_data=@embedFile("windows_rgba_v5.bmp");varstream_source=std.io.StreamSource{ .const_buffer=std.io.fixedBufferStream(image_data) };varbmp=zigimg.formats.bmp.BMP{};constpixels=trybmp.read(allocator,&stream_source);deferpixels.deinit(allocator);std.log.info("BMP info header: {}", .{bmp.info_header});}

For the curious, the program above generate the following output:

info: 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 } }

Convert between pixel formats

You can useImage.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.

pubfnexample()!void {// [...]// Assuming you already have an image loadedtryimage.convert(.float32);}

PixelFormatConverter

If you prefer, you can usePixelFormatConverter directly.

pubfnexample(allocator:std.mem.Allocator)!void {constindexed2_pixels=tryzigimg.color.PixelStorage.init(allocator,.indexed2,4);deferindexed2_pixels.deinit(allocator);// [...] Setup your indexed2 pixel dataconstbgr24_pixels=tryzigimg.PixelFormatConverter.convert(allocator,&indexed2_pixels,.bgr24);deferbgr24_pixels.deinit(allocator);}

OctTreeQuantizer

If you prefer more granular control to create an indexed image, you can use theOctTreeQuantizer directly.

pubfnexample(allocator:std.mem.Allocator)!void {constimage_data=@embedFile("windows_rgba_v5.bmp");varimage=tryzigimg.Image.fromMemory(allocator,image_data[0..]);deferimage.deinit();varquantizer=zigimg.OctTreeQuantizer.init(allocator);deferquantizer.deinit();varcolor_it=image.iterator();while (color_it.next())|pixel| {tryquantizer.addColor(pixel);    }varpalette_storage: [256]zigimg.color.Rgba32=undefined;constpalette=quantizer.makePalette(255,palette_storage[0..]);constpalette_index=tryquantizer.getPaletteIndex(zigimg.color.Rgba32.initRgba(110,0,0,255));}

Get a color from a HTML hex string

You can get a color from a HTML hex string. The alpha component is always last. It also supports the shorthand version.

pubfnexample()!void {constrgb24=tryzigimg.color.Rgb24.fromHtmlHex("#123499");constrgba32=tryzigimg.color.Rgba32.fromHtmlHex("FF000045");constred_rgb24=tryzigimg.color.Rgb24.fromHtmlHex("#F00");constblue_rgba32=tryzigimg.clor.Rgba32.fromHtmlHex("#00FA");}

Predefined colors

You can access predefined colors for any pixel format usingColors().

conststd=@import("std");constzigimg=@import("zigimg");pubfnmain()!void {constred_float32=zigimg.Colors(zigimg.color.Colorf32).Red;constblue_rgb24=zigimg.Colors(zigimg.color.Rgb24).Blue;}

Color management & color space

While zigimg does not support ICC profile yet (see #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 usingColorf32 /float32 as your pixel format, you'll need to convert to that format first.

The following device-dependent color model are supported:

  • HSL (Hue, Saturation, Luminance)
  • HSV (Hue, Saturation, Value) or also known as HSB (Hue, Saturation, Brightness)
  • CMYK (Cyan-Magenta-Yellow-Black)

The following device-inpendent color spaces are supported, with or without alpha:

  • CIE XYZ
  • CIE Lab
  • CIE LCh(ab), the cylindral representation of CIE Lab
  • CIE Luv
  • CIE LCh(uv), the cylindral representation of CIE Luv
  • HSLuv, a HSL representation of CIE LCh(uv) which is a cylindrical representation of CIE Luv color space
  • Oklab
  • Oklch, the cylindrical representation of Oklab

Convert between linear and gamma-corrected color

All 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 calltoGamma() ortoLinear(), in this example I'm using bothsRGB andBT709 (aka Rec.709).

You 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.

pubfnexample(linear_color:zigimg.color.Colorf32) {constgamma_srgb=zigimg.color.sRGB.toGamma(linear_color);constgamma_bt709=zigimg.color.BT709.toGammaFast(linear_color);constlinear_srgb=zigimg.color.sRGB.toLinearFast(gamma_srgb);constlinear_bt709=zigimg.color.BT709.toLinear(gamma_bt609);}

Convert a single color to a different color space

To convert to a device independant color space, you need first to use a reference RGB color space. Usually the most common for computer purposes issRGB. 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.

To a color space:

pubfnexample(linear_color:zigimg.color.Colorf32)void {constxyz=zigimg.color.sRGB.toXYZ(linear_color);constlab_alpha=zigimg.color.sRGB.toLabAlpha(linear_color);constlch_ab=zigimg.color.sRGB.toLCHab(linear_color);constluv_alpha=zigimg.color.sRGB.toLuvAlpha(linear_color);constlch_uv=zigimg.color.sRGB.toLCHuv(linear_color);consthsluv=zigimg.color.sRGB.toHSLuv(linear_color);constoklab=zigimg.color.sRGB.toOklab(linear_color);constoklch=zigimg.color.sRGB.toOkLCh(linear_color);}

When 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.

pubfnexample(oklab:zigimg.color.Oklab) {constlinear_srgb_clamped=zigimg.color.sRGB.fromOklab(oklab,.clamp);constlinear_srgb=zigimg.color.sRGB.fromOklab(oklab,.none);}

Convert a slice of color to a different color space

Converting each pixel individually will be tedious if you want to use image processing on the CPU. Almost all color space conversion offeran 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.

Those conversions are only available with the alpha version of each color space.

pubfnexampleInPlace(linear_srgb_image: []zigimg.color.Colorf32)void {constslice_lab_alpha=zigimg.color.sRGB.sliceToLabAlphaInPlace(linear_srgb_image);// Do your image manipulation in CIE L*a*b*// Convert back to linear sRGB_=zigimg.color.sRGB.sliceFromLabAlphaInPlace(slice_lab_alpha,.clamp);// or without clamping_=zigimg.color.sRGB.sliceFromLabAlphaInPlace(slice_lab_alpha,.none);}pubfnexampleCopy(allocator:std.mem.Allocator,linear_srgb_image: []constzigimg.color.Colorf32)![]zigimg.color.Colorf32 {constslice_oklab_alpha=tryzigimg.color.sRGB.sliceToOklabCopy(allocator,linear_srgb_image);// Do your image manipulatioon in Oklab// Convert back to linear sRGBreturntryzigimg.color.sRGB.sliceFromOklabCopy(allocator,slice_oklab_alpha,.clamp);// Or without clampingreturntryzigimg.color.sRGB.sliceFromOklabCopy(allocator,slice_oklab_alpha,.none);}

Convert between some cylindrical representation

CIE Lab, CIE Luv and Oklab have cylindrical representation of their color space, each color has functions to convert from and to the cylindrical version.

pubfnexample()void {constlab=zigimg.color.CIELab{ .l=0.12, .a=-0.23, .b=0.56 };constluv_alpha=zigimg.color.CIELuvAlpha { .l=0.4, .u=0.5, .v=-0.2, .alpha=0.8 };constoklab=zigimg.color.Oklab{ .l=0.67, .a=0.1, .b=0.56 };constlch_ab=lab.toLCHab();constlch_uv_alpha=luv_alpha.toLCHuvAlpha();constoklch=oklab.toOkLCh();constresult_lab=lch_ab.toLab();constresult_luv_alpha=lch_uv_alpha.toLuvAlpha();constresult_oklab=oklch.toOklab();}

Convert color between RGB color spaces

To convert a single color, use theconvertColor() function on theRgbColorspace struct:

pubfnexample(linear_color:zigimg.color.Colorf32)void {constpro_photo_color=zigimg.color.sRGB.convertColor(zigimg.color.ProPhotoRGB,linear_color);}

If you want to convert a whole slice of pixels, useconvertColors(), it will apply the conversion in-place:

pubfnexample(linear_image: []zigimg.color.Colorf32)void {constadobe_image=zigimg.color.sRGB.convertColors(zigimg.color.AdobeRGB,linear_image);}

If the target RGB colorspace have a different white point, it will do thechromatic adapdation for you using the Bradford method.

Predefined RGB color spaces

Here the list of predefined RGB color spaces, all accessible fromzigimg.color struct:

  • BT601_NTSC
  • BT601_PAL
  • BT709
  • sRGB
  • DCIP3.Display
  • DCIP3.Theater
  • DCIP3.ACES
  • BT2020
  • AdobeRGB
  • AdobeWideGamutRGB
  • ProPhotoRGB

Predefined white points

All predefined white point are accessed withzigimg.color.WhitePoints. All the standard illuminants are defined there.

Create your own RGB color space

You can create your own RGB color space usingzigimg.color.RgbColorspace.init(). Each coordinate is in the 2D version of the CIE xyY color space.

If you don't care about linear and gamma conversion, just ignore those functions in the init struct.

fnmyColorSpaceToGamma(value:f32)f32 {returnstd.math.pow(f32,value,1.0/2.4);}fnmyColorSpaceToLinear(value:f32)f32 {returnstd.math.pow(f32,value,2.4);}pubfnexample()void {pubconstmy_color_space=zigimg.color.RgbColorspace.init(.{        .red= .{ .x=0.6400, .y=0.3300 },        .green= .{ .x=0.3000, .y=0.6000 },        .blue= .{ .x=0.1500, .y=0.0600 },        .white=zigimg.color.WhitePoints.D50,        .to_gamma=myColorSpaceToGamma,        .to_gamma_fast=myColorSpaceToGamma,        .to_linear=myColorSpaceToLinear,        .to_linear_fast=myColorSpaceToLinear,    });}

[8]ページ先頭

©2009-2025 Movatter.jp