Skip to content

Latest commit

 

History

History
199 lines (149 loc) · 5.79 KB

README.md

File metadata and controls

199 lines (149 loc) · 5.79 KB

pb2zig

CLI utility for translating Pixel Bender kernel into Zig language code. You can also access its functionality through an API.

Installation

npm install -g pb2zig

Versioning

The major and minor version numbers of this program correspond to the version of the Zig compiler it's designed for. The current version is 0.11.1. It works with Zig 0.11.0 and 0.12.0.

Usage

pb2zig [OPTION]... [FILE]...

Options:
  --kernel-only,  -ko             Omit image processing code
  --output-dir,   -od [DIR]       Set output directory
  --input-pixel,  -ip [TYPE]      Set input pixel type (default: u8)
  --output-pixel, -op [TYPE]      Set output pixel type (default: u8)
  --version,      -v              Show version number

For example, the follow command would translate simple.pbk in the current working directory and save it in the same directory as simple.zig:

pb2zig simple.pbk

Multiple files can be translated at the same time:

pb2zig hue.pbk saturation.pbk liminosity.pbk

Use the --output-dir or -od option to set the destination directory:

pb2zig simple.pbk --output-dir ~/my-zig-project

By default, pb2zig includes image processing functions for making use of the kernel in the output code. If you want to work with the kernel directly, you can specify the --kernel-only option to omit these functions:

pb2zig simple.pbk --kernel-only

By default, the bundled image processing functions expect and produce pixels with 8-bit channels. If you wish to use 16-bit integers or 32-bit floating points instead, use the --input-pixel (or -ip) and --output-pixel (or -op) options:

pb2zig simple.pbk --input-pixel u16 --output-pixel u16

Valid pixel types are u8, i16, u16, and f32. Floating points should contain values between 0.0 and 1.0. This is the representation used internally by Pixel Bender.

Using API

The library exports a single function:

function convertPixelBender(code: string, options: object): string

Options are kernelOnly, inputPixelType, and outputPixelType.

Function and types in translated code

Functions

pub fn createOutput(
    allocator: std.mem.Allocator,
    width: u32,
    height: u32,
    input: Input,
    params: Parameters,
) !Output;

Create an image of the specified size using the provided input images and parameters, allocating memory from allocator.

pub fn createPartialOutput(
    allocator: std.mem.Allocator,
    width: u32,
    height: u32,
    start: u32,
    count: u32,
    input: Input,
    params: Parameters,
) !Output;

Create a slice of the output image with count scanlines, starting from start.

Input and output structures

pub const kernel = struct {
    // ...
};

A namespace containing information about the kernel and a function for creating the kernel.

pub const Input = KernelInput(u8, kernel);

A structure that holds input images for the kernel. It is empty if a kernel generates an image based on parameters alone.

pub const Output = KernelOutput(u8, kernel);

A structure that holds output images from the kernel.

pub const Parameters = KernelParameters(kernel);

A structure holding parameters for the kernel.

Image structure

pub fn Image(comptime T: type, comptime len: comptime_int, comptime writable: bool) type {
    return struct {
        pub const Pixel = @Vector(4, T);
        pub const FPixel = @Vector(len, f32);
        pub const channels = len;

        data: if (writable) []Pixel else []const Pixel,
        width: u32,
        height: u32,
        colorSpace: ColorSpace = .srgb,
        offset: usize = 0,

        // ...
    };
}

Four channel pixel data is always expected even when the kernel calls for fewer. For image3, the first three channels are used, that is R, G, and B. For image2, R and A are used. For image1 only the R channel is used.

colorSpace serves no purpose. It's only there to accommodate the web browser's ImageData object.

offset is employed when the structure holds data for only some part of the full image.

Using Zig code in Node.js

With the help of node-zigar, you can run Zig code in Node.js. This offer some advantage over using WebAssembly. Native code generally run faster than WASM (though the difference is not huge). Native code can also access memory of buffers in JavaScript directly whereas WASM instances have their own memory space and image data must be copied into it.

The following example uses sharp to open a JPEG file and obtain its pixel data. It then applies the effect from the translated kernel "crystalize" (zig|pbk) and saves the result to a PNG file:

import { createOutput } from 'crystallize.zig';
import sharp from 'sharp';

// create image object, ensure that it has an alpha channel
const img = sharp(`./socha.jpg`).ensureAlpha().raw();
// extract raw data
const { data, info } = await img.toBuffer({ resolveWithObject: true });
const { width, height, channels } = info;
// call createOutput() with input image and params
const input = { src: { data, width, height } };
const params = { size: 25 };
const output = createOutput(width, height, input, params);
// obtain Uint8Array from image "dst"; its data property is of the type `[]@Vector(4, u8)`
// since an array of x4 u8 vector has the same memory layout as an array of u8, Zigar
// attaches a "typedArray" property to the object for our convenience
const { typedArray } = output.dst.data;
// save the raw data as a PNG file
sharp(typedArray, { raw: { width, height, channels } }).png().toFile(`./crystallize.png`);