From e302328628d287d98d374404c61103cbf457af48 Mon Sep 17 00:00:00 2001
From: Julek <dnjulek@hotmail.com>
Date: Fri, 12 Apr 2024 22:23:50 -0300
Subject: [PATCH] add example for zigapi

---
 example/README.md                             |  21 ----
 examples/README.md                            |   4 +
 examples/example-zigapi/README.md             |  21 ++++
 .../example-zigapi}/build.zig                 |   0
 .../example-zigapi}/build.zig.zon             |   4 +-
 .../example-zigapi/src/invert_example.zig     | 107 ++++++++++++++++++
 examples/example/README.md                    |  21 ++++
 examples/example/build.zig                    |  53 +++++++++
 examples/example/build.zig.zon                |  11 ++
 .../example}/src/invert_example.zig           |   0
 test/src/invert_example.zig                   |  56 +++++----
 11 files changed, 246 insertions(+), 52 deletions(-)
 delete mode 100644 example/README.md
 create mode 100644 examples/README.md
 create mode 100644 examples/example-zigapi/README.md
 rename {example => examples/example-zigapi}/build.zig (100%)
 rename {example => examples/example-zigapi}/build.zig.zon (58%)
 create mode 100644 examples/example-zigapi/src/invert_example.zig
 create mode 100644 examples/example/README.md
 create mode 100644 examples/example/build.zig
 create mode 100644 examples/example/build.zig.zon
 rename {example => examples/example}/src/invert_example.zig (100%)

diff --git a/example/README.md b/example/README.md
deleted file mode 100644
index 21f5570..0000000
--- a/example/README.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# invert_example
-
-To use this module in your project you will need:
-1. A [build.zig.zon](/example/build.zig.zon) file like this.
-2. These lines in your [build.zig](/example/build.zig):
-
-```zig
-const vapoursynth_dep = b.dependency("vapoursynth", .{
-    .target = target,
-    .optimize = optimize,
-});
-
-lib.root_module.addImport("vapoursynth", vapoursynth_dep.module("vapoursynth"));
-```
-
-The [invert_example.zig](/example/src/invert_example.zig) is based on [invert_example.c](https://github.com/vapoursynth/vapoursynth/blob/master/sdk/invert_example.c), from the VapourSynth SDK, I recommend checking it out first if you don't know the framework.
-
-## Building
-Zig version should be the master, 0.11.0 not supported.
-
-``zig build -Doptimize=ReleaseFast``
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000..da73a35
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,4 @@
+# Examples
+
+[example](/example/) shows how to use the C binding only.\
+[example-zigapi](/example-zigapi/) shows how to use the C binding with extra Zig struct/methods.
diff --git a/examples/example-zigapi/README.md b/examples/example-zigapi/README.md
new file mode 100644
index 0000000..ebdd3bc
--- /dev/null
+++ b/examples/example-zigapi/README.md
@@ -0,0 +1,21 @@
+# invert_example
+
+To use this module in your project you will need:
+1. A [build.zig.zon](/examples/example-zigapi/build.zig.zon) file like this.
+2. These lines in your [build.zig](/examples/example-zigapi/build.zig):
+
+```zig
+const vapoursynth_dep = b.dependency("vapoursynth", .{
+    .target = target,
+    .optimize = optimize,
+});
+
+lib.root_module.addImport("vapoursynth", vapoursynth_dep.module("vapoursynth"));
+```
+
+The [invert_example.zig](/examples/example-zigapi/src/invert_example.zig) is based on [invert_example.c](https://github.com/vapoursynth/vapoursynth/blob/master/sdk/invert_example.c), from the VapourSynth SDK, I recommend checking it out first if you don't know the framework.
+
+## Building
+Zig version should be the master, 0.11.0 not supported.
+
+``zig build -Doptimize=ReleaseFast``
diff --git a/example/build.zig b/examples/example-zigapi/build.zig
similarity index 100%
rename from example/build.zig
rename to examples/example-zigapi/build.zig
diff --git a/example/build.zig.zon b/examples/example-zigapi/build.zig.zon
similarity index 58%
rename from example/build.zig.zon
rename to examples/example-zigapi/build.zig.zon
index 7167ff7..e2cdd0c 100644
--- a/example/build.zig.zon
+++ b/examples/example-zigapi/build.zig.zon
@@ -4,8 +4,8 @@
     .paths = .{""},
     .dependencies = .{
         .vapoursynth = .{
-            .url = "https://github.com/dnjulek/vapoursynth-zig/archive/6b5dfc82c74fc81de502b3c01b6fe9893f8359e6.tar.gz",
-            .hash = "12205f58b4ccddbbccea25cae0e801030e3959885e980454a607e0015707758f1398",
+            .url = "https://github.com/dnjulek/vapoursynth-zig/archive/2f6a59a6cccf23cec7ca8a3abb3f316593c5ed7b.tar.gz",
+            .hash = "12201cb4befda1224caa03a57a2188d5d5e985a7fdd7a8b3277d385dba4cb0e272cb",
         },
     },
 }
diff --git a/examples/example-zigapi/src/invert_example.zig b/examples/example-zigapi/src/invert_example.zig
new file mode 100644
index 0000000..431a340
--- /dev/null
+++ b/examples/example-zigapi/src/invert_example.zig
@@ -0,0 +1,107 @@
+//! https://github.com/vapoursynth/vapoursynth/blob/master/sdk/invert_example.c
+
+const std = @import("std");
+const vapoursynth = @import("vapoursynth");
+
+const math = std.math;
+const vs = vapoursynth.vapoursynth4;
+const vsh = vapoursynth.vshelper;
+const zapi = vapoursynth.zigapi;
+
+// https://ziglang.org/documentation/master/#Choosing-an-Allocator
+const allocator = std.heap.c_allocator;
+
+const InvertData = struct {
+    node: ?*vs.Node,
+    vi: *const vs.VideoInfo,
+    enabled: bool,
+};
+
+export fn invertGetFrame(n: c_int, activation_reason: vs.ActivationReason, instance_data: ?*anyopaque, frame_data: ?*?*anyopaque, frame_ctx: ?*vs.FrameContext, core: ?*vs.Core, vsapi: ?*const vs.API) callconv(.C) ?*const vs.Frame {
+    _ = frame_data;
+    const d: *InvertData = @ptrCast(@alignCast(instance_data));
+
+    if (activation_reason == .Initial) {
+        vsapi.?.requestFrameFilter.?(n, d.node, frame_ctx);
+    } else if (activation_reason == .AllFramesReady) {
+        var src = zapi.Frame.init(d.node, n, frame_ctx, core, vsapi);
+        defer src.deinit();
+        var dst = src.newVideoFrame();
+
+        var plane: u32 = 0;
+        while (plane < d.vi.format.numPlanes) : (plane += 1) {
+            var srcp = src.getReadPtr(plane);
+            var dstp = dst.getWritePtr(plane);
+
+            // getDimensions returns a tuple with [width, height, stride],
+            // use getDimensions2 if you want a struct.
+            const w, const h, const stride = src.getDimensions(plane);
+
+            var y: u32 = 0;
+            while (y < h) : (y += 1) {
+                var x: u32 = 0;
+                while (x < w) : (x += 1) {
+                    dstp[x] = if (d.enabled) ~(srcp[x]) else srcp[x];
+                }
+
+                dstp = dstp[stride..];
+                srcp = srcp[stride..];
+            }
+        }
+
+        return dst.frame;
+    }
+
+    return null;
+}
+
+export fn invertFree(instance_data: ?*anyopaque, core: ?*vs.Core, vsapi: ?*const vs.API) callconv(.C) void {
+    _ = core;
+    const d: *InvertData = @ptrCast(@alignCast(instance_data));
+    vsapi.?.freeNode.?(d.node);
+    allocator.destroy(d);
+}
+
+export fn invertCreate(in: ?*const vs.Map, out: ?*vs.Map, user_data: ?*anyopaque, core: ?*vs.Core, vsapi: ?*const vs.API) callconv(.C) void {
+    _ = user_data;
+    var d: InvertData = undefined;
+    var map = zapi.Map.init(in, out, vsapi);
+
+    // getNodeVi returns a tuple with [vs.Node, vs.VideoInfo],
+    // use getNodeVi2 if you want a struct.
+    d.node, d.vi = map.getNodeVi("clip");
+
+    if (!vsh.isConstantVideoFormat(d.vi) or (d.vi.format.sampleType != .Integer) or (d.vi.format.bitsPerSample != 8)) {
+        map.setError("Invert: only constant format 8bit integer input supported");
+        vsapi.?.freeNode.?(d.node);
+        return;
+    }
+
+    // https://ziglang.org/documentation/master/#Optionals
+    const enabled = map.getInt(i32, "enabled") orelse 1;
+
+    if ((enabled < 0) or (enabled > 1)) {
+        map.setError("Invert: enabled must be 0 or 1");
+        vsapi.?.freeNode.?(d.node);
+        return;
+    }
+
+    d.enabled = enabled == 1;
+
+    const data: *InvertData = allocator.create(InvertData) catch unreachable;
+    data.* = d;
+
+    var deps = [_]vs.FilterDependency{
+        vs.FilterDependency{
+            .source = d.node,
+            .requestPattern = .StrictSpatial,
+        },
+    };
+
+    vsapi.?.createVideoFilter.?(out, "Invert", d.vi, invertGetFrame, invertFree, .Parallel, &deps, deps.len, data, core);
+}
+
+export fn VapourSynthPluginInit2(plugin: *vs.Plugin, vspapi: *const vs.PLUGINAPI) void {
+    _ = vspapi.configPlugin.?("com.example.zinvert", "zinvert", "VapourSynth Invert Example", vs.makeVersion(1, 0), vs.VAPOURSYNTH_API_VERSION, 0, plugin);
+    _ = vspapi.registerFunction.?("Filter", "clip:vnode;enabled:int:opt;", "clip:vnode;", invertCreate, null, plugin);
+}
diff --git a/examples/example/README.md b/examples/example/README.md
new file mode 100644
index 0000000..35a7c08
--- /dev/null
+++ b/examples/example/README.md
@@ -0,0 +1,21 @@
+# invert_example
+
+To use this module in your project you will need:
+1. A [build.zig.zon](/examples/example/build.zig.zon) file like this.
+2. These lines in your [build.zig](/examples/example/build.zig):
+
+```zig
+const vapoursynth_dep = b.dependency("vapoursynth", .{
+    .target = target,
+    .optimize = optimize,
+});
+
+lib.root_module.addImport("vapoursynth", vapoursynth_dep.module("vapoursynth"));
+```
+
+The [invert_example.zig](/examples/example/src/invert_example.zig) is based on [invert_example.c](https://github.com/vapoursynth/vapoursynth/blob/master/sdk/invert_example.c), from the VapourSynth SDK, I recommend checking it out first if you don't know the framework.
+
+## Building
+Zig version should be the master.
+
+``zig build -Doptimize=ReleaseFast``
diff --git a/examples/example/build.zig b/examples/example/build.zig
new file mode 100644
index 0000000..24733a0
--- /dev/null
+++ b/examples/example/build.zig
@@ -0,0 +1,53 @@
+const std = @import("std");
+
+pub const min_zig_version = std.SemanticVersion{ .major = 0, .minor = 12, .patch = 0, .pre = "dev.2158" };
+
+pub fn build(b: *std.Build) void {
+    ensureZigVersion() catch return;
+    const target = b.standardTargetOptions(.{});
+    const optimize = b.standardOptimizeOption(.{});
+
+    const lib = b.addSharedLibrary(.{
+        .name = "invert_example",
+        .root_source_file = .{ .path = "src/invert_example.zig" },
+        .target = target,
+        .optimize = optimize,
+    });
+
+    const vapoursynth_dep = b.dependency("vapoursynth", .{
+        .target = target,
+        .optimize = optimize,
+    });
+
+    lib.root_module.addImport("vapoursynth", vapoursynth_dep.module("vapoursynth"));
+    lib.linkLibC();
+
+    if (lib.root_module.optimize == .ReleaseFast) {
+        lib.root_module.strip = true;
+    }
+
+    b.installArtifact(lib);
+}
+
+fn ensureZigVersion() !void {
+    var installed_ver = @import("builtin").zig_version;
+    installed_ver.build = null;
+
+    if (installed_ver.order(min_zig_version) == .lt) {
+        std.log.err("\n" ++
+            \\---------------------------------------------------------------------------
+            \\
+            \\Installed Zig compiler version is too old.
+            \\
+            \\Min. required version: {any}
+            \\Installed version: {any}
+            \\
+            \\Please install newer version and try again.
+            \\Latest version can be found here: https://ziglang.org/download/
+            \\
+            \\---------------------------------------------------------------------------
+            \\
+        , .{ min_zig_version, installed_ver });
+        return error.ZigIsTooOld;
+    }
+}
diff --git a/examples/example/build.zig.zon b/examples/example/build.zig.zon
new file mode 100644
index 0000000..e2cdd0c
--- /dev/null
+++ b/examples/example/build.zig.zon
@@ -0,0 +1,11 @@
+.{
+    .name = "invert_example",
+    .version = "1.0.0",
+    .paths = .{""},
+    .dependencies = .{
+        .vapoursynth = .{
+            .url = "https://github.com/dnjulek/vapoursynth-zig/archive/2f6a59a6cccf23cec7ca8a3abb3f316593c5ed7b.tar.gz",
+            .hash = "12201cb4befda1224caa03a57a2188d5d5e985a7fdd7a8b3277d385dba4cb0e272cb",
+        },
+    },
+}
diff --git a/example/src/invert_example.zig b/examples/example/src/invert_example.zig
similarity index 100%
rename from example/src/invert_example.zig
rename to examples/example/src/invert_example.zig
diff --git a/test/src/invert_example.zig b/test/src/invert_example.zig
index 888fcfc..431a340 100644
--- a/test/src/invert_example.zig
+++ b/test/src/invert_example.zig
@@ -6,12 +6,14 @@ const vapoursynth = @import("vapoursynth");
 const math = std.math;
 const vs = vapoursynth.vapoursynth4;
 const vsh = vapoursynth.vshelper;
+const zapi = vapoursynth.zigapi;
 
 // https://ziglang.org/documentation/master/#Choosing-an-Allocator
 const allocator = std.heap.c_allocator;
 
 const InvertData = struct {
     node: ?*vs.Node,
+    vi: *const vs.VideoInfo,
     enabled: bool,
 };
 
@@ -22,38 +24,32 @@ export fn invertGetFrame(n: c_int, activation_reason: vs.ActivationReason, insta
     if (activation_reason == .Initial) {
         vsapi.?.requestFrameFilter.?(n, d.node, frame_ctx);
     } else if (activation_reason == .AllFramesReady) {
-        const src = vsapi.?.getFrameFilter.?(n, d.node, frame_ctx);
+        var src = zapi.Frame.init(d.node, n, frame_ctx, core, vsapi);
+        defer src.deinit();
+        var dst = src.newVideoFrame();
 
-        // https://ziglang.org/documentation/master/#defer
-        defer vsapi.?.freeFrame.?(src);
+        var plane: u32 = 0;
+        while (plane < d.vi.format.numPlanes) : (plane += 1) {
+            var srcp = src.getReadPtr(plane);
+            var dstp = dst.getWritePtr(plane);
 
-        const fi = vsapi.?.getVideoFrameFormat.?(src);
+            // getDimensions returns a tuple with [width, height, stride],
+            // use getDimensions2 if you want a struct.
+            const w, const h, const stride = src.getDimensions(plane);
 
-        const height = vsapi.?.getFrameHeight.?(src, 0);
-        const width = vsapi.?.getFrameWidth.?(src, 0);
-        const dst = vsapi.?.newVideoFrame.?(fi, width, height, src, core);
-
-        var plane: c_int = 0;
-        while (plane < fi.numPlanes) : (plane += 1) {
-            var srcp: [*]const u8 = vsapi.?.getReadPtr.?(src, plane);
-            var dstp: [*]u8 = vsapi.?.getWritePtr.?(dst, plane);
-            const stride: usize = @intCast(vsapi.?.getStride.?(src, plane));
-            const h: usize = @intCast(vsapi.?.getFrameHeight.?(src, plane));
-            const w: usize = @intCast(vsapi.?.getFrameWidth.?(src, plane));
-
-            var y: usize = 0;
+            var y: u32 = 0;
             while (y < h) : (y += 1) {
-                var x: usize = 0;
+                var x: u32 = 0;
                 while (x < w) : (x += 1) {
                     dstp[x] = if (d.enabled) ~(srcp[x]) else srcp[x];
                 }
 
-                dstp += stride;
-                srcp += stride;
+                dstp = dstp[stride..];
+                srcp = srcp[stride..];
             }
         }
 
-        return dst;
+        return dst.frame;
     }
 
     return null;
@@ -69,21 +65,23 @@ export fn invertFree(instance_data: ?*anyopaque, core: ?*vs.Core, vsapi: ?*const
 export fn invertCreate(in: ?*const vs.Map, out: ?*vs.Map, user_data: ?*anyopaque, core: ?*vs.Core, vsapi: ?*const vs.API) callconv(.C) void {
     _ = user_data;
     var d: InvertData = undefined;
+    var map = zapi.Map.init(in, out, vsapi);
 
-    d.node = vsapi.?.mapGetNode.?(in, "clip", 0, null).?;
-    const vi: *const vs.VideoInfo = vsapi.?.getVideoInfo.?(d.node);
+    // getNodeVi returns a tuple with [vs.Node, vs.VideoInfo],
+    // use getNodeVi2 if you want a struct.
+    d.node, d.vi = map.getNodeVi("clip");
 
-    if (!vsh.isConstantVideoFormat(vi) or (vi.format.sampleType != .Integer) or (vi.format.bitsPerSample != @as(c_int, 8))) {
-        vsapi.?.mapSetError.?(out, "Invert: only constant format 8bit integer input supported");
+    if (!vsh.isConstantVideoFormat(d.vi) or (d.vi.format.sampleType != .Integer) or (d.vi.format.bitsPerSample != 8)) {
+        map.setError("Invert: only constant format 8bit integer input supported");
         vsapi.?.freeNode.?(d.node);
         return;
     }
 
     // https://ziglang.org/documentation/master/#Optionals
-    const enabled = vsh.mapGetN(i32, in, "enabled", 0, vsapi) orelse 1;
+    const enabled = map.getInt(i32, "enabled") orelse 1;
 
     if ((enabled < 0) or (enabled > 1)) {
-        vsapi.?.mapSetError.?(out, "Invert: enabled must be 0 or 1");
+        map.setError("Invert: enabled must be 0 or 1");
         vsapi.?.freeNode.?(d.node);
         return;
     }
@@ -100,10 +98,10 @@ export fn invertCreate(in: ?*const vs.Map, out: ?*vs.Map, user_data: ?*anyopaque
         },
     };
 
-    vsapi.?.createVideoFilter.?(out, "Invert", vi, invertGetFrame, invertFree, .Parallel, &deps, deps.len, data, core);
+    vsapi.?.createVideoFilter.?(out, "Invert", d.vi, invertGetFrame, invertFree, .Parallel, &deps, deps.len, data, core);
 }
 
 export fn VapourSynthPluginInit2(plugin: *vs.Plugin, vspapi: *const vs.PLUGINAPI) void {
-    _ = vspapi.configPlugin.?("com.example.invert", "invert", "VapourSynth Invert Example", vs.makeVersion(1, 0), vs.VAPOURSYNTH_API_VERSION, 0, plugin);
+    _ = vspapi.configPlugin.?("com.example.zinvert", "zinvert", "VapourSynth Invert Example", vs.makeVersion(1, 0), vs.VAPOURSYNTH_API_VERSION, 0, plugin);
     _ = vspapi.registerFunction.?("Filter", "clip:vnode;enabled:int:opt;", "clip:vnode;", invertCreate, null, plugin);
 }