Skip to content

Commit a402676

Browse files
committed
WIP gaussian blur
1 parent b86adda commit a402676

File tree

3 files changed

+164
-1
lines changed

3 files changed

+164
-1
lines changed

build.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const EXAMPLES = [_][]const u8{
1414
"ellipse",
1515
"fill_and_stroke2",
1616
"fill_style",
17+
"gaussian_blur",
1718
"glyphs",
1819
"gradient",
1920
"group",

examples/gaussian_blur.zig

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
const std = @import("std");
2+
const exp = std.math.exp;
3+
const pi = std.math.pi;
4+
const log = std.log;
5+
const cairo = @import("cairo");
6+
const Format = cairo.image_surface.Format;
7+
const setBackground = @import("utils.zig").setBackground;
8+
9+
/// https://www.cairographics.org/samples/image/
10+
fn image(cr: *cairo.Context) !void {
11+
var surface = try cairo.Surface.createFromPng("data/romedalen.png");
12+
defer surface.destroy();
13+
14+
const w = try surface.getWidth();
15+
const h = try surface.getHeight();
16+
17+
cr.translate(128.0, 128.0);
18+
cr.rotate(45 * pi / 180.0);
19+
cr.scale(256.0 / @intToFloat(f64, w), 256.0 / @intToFloat(f64, h));
20+
cr.translate(-0.5 * @intToFloat(f64, w), -0.5 * @intToFloat(f64, h));
21+
22+
cr.setSourceSurface(&surface, 0, 0);
23+
cr.paint();
24+
}
25+
26+
/// https://www.cairographics.org/cookbook/blur.c/
27+
fn blurImageSurface(allocator: *std.mem.Allocator, surface: *cairo.Surface, radius: u16) !void {
28+
try cairo.checkSurfaceStatus(surface.surface);
29+
30+
const width = try surface.getWidth();
31+
const height = try surface.getHeight();
32+
const width_f64 = @intToFloat(f64, width);
33+
34+
// check that we are starting from a Cairo image surface
35+
switch (cairo.image_surface.getFormat(surface.surface)) {
36+
Format.A1 => {
37+
return error.GaussianBlurNotImplementedForThisSurface;
38+
},
39+
Format.A8 => log.debug("Can blur but set width /= 4;", .{}),
40+
Format.Argb32 => {
41+
log.debug("Ok, can blur", .{});
42+
},
43+
Format.Rgb24 => {
44+
log.debug("Ok, can blur", .{});
45+
},
46+
else => |format| {
47+
log.debug("Format: {}", .{format});
48+
return error.GaussianBlurNotImplementedForThisSurface;
49+
},
50+
}
51+
52+
var tmp = try cairo.image_surface.create(Format.Argb32, width, height);
53+
// defer tmp.destroy();
54+
try cairo.checkSurfaceStatus(tmp);
55+
56+
const src = try cairo.image_surface.getData(surface.surface);
57+
// log.debug("src {}", .{src});
58+
const src_stride = cairo.image_surface.getStride(surface.surface);
59+
// log.debug("src_stride {}", .{src_stride});
60+
61+
const dest = try cairo.image_surface.getData(tmp);
62+
const dest_stride = cairo.image_surface.getStride(tmp);
63+
64+
// build the gaussian kernel ///////////////////////////////////////////////
65+
// TODO: add link to mathematical function
66+
const size: usize = 17;
67+
const half = @intToFloat(f64, size) / 2.0;
68+
var kernel = std.ArrayList(f64).init(allocator);
69+
var a: f64 = 0;
70+
var i: usize = 0;
71+
while (i < size) : (i += 1) {
72+
const f = @intToFloat(f64, i) - half;
73+
const coeff = exp(-f * f / 30.0) * 80.0;
74+
try kernel.append(coeff);
75+
a += coeff;
76+
}
77+
78+
// blur the image //////////////////////////////////////////////////////////
79+
80+
// const s_addr = (@ptrToInt(src) + src_stride); // double-check the parentheses
81+
// const s = @intToPtr([*]u32, s_addr);
82+
// log.debug("s {}\n", .{@typeInfo(@TypeOf(s))});
83+
84+
// Horizontally blur from surface -> tmp
85+
i = 0;
86+
while (i < height) : (i += 1) {
87+
// log.debug("height px {}/{}", .{i+1, height});
88+
const s_addr = (@ptrToInt(src) + i * src_stride); // double-check the parentheses
89+
const d_addr = (@ptrToInt(dest) + i * dest_stride);
90+
const s = @intToPtr([*]u32, s_addr);
91+
const d = @intToPtr([*]u32, d_addr);
92+
// std.debug.print("s {}\n", .{@typeInfo(@TypeOf(s))});
93+
var j: usize = 0;
94+
while (j < width) : (j += 1) {
95+
// log.debug("width px {}/{}", .{j+1, width});
96+
if (radius < j and j < (width - radius)) {
97+
// log.debug("d[j] = s[j]", .{});
98+
d[j] = s[j];
99+
continue;
100+
} else {
101+
// log.debug("ELSE", .{});
102+
var x: f64 = 0;
103+
var y: f64 = 0;
104+
var z: f64 = 0;
105+
var w: f64 = 0;
106+
107+
var k: usize = 0;
108+
while (k < size) : (k += 1) {
109+
const k_f64 = @intToFloat(f64, k);
110+
const j_f64 = @intToFloat(f64, j);
111+
if ((j_f64 - half + k_f64 < 0) or (j_f64 - half + k_f64 >= width_f64)) {
112+
continue;
113+
} else {
114+
const idx = @floatToInt(usize, j_f64 - half + k_f64);
115+
const p = s[idx];
116+
// what are these things? What does this shifting mean?
117+
const px = @intToFloat(f64, (p >> 24) & 0xff);
118+
const py = @intToFloat(f64, (p >> 16) & 0xff);
119+
const pz = @intToFloat(f64, (p >> 8) & 0xff);
120+
const pw = @intToFloat(f64, (p >> 0) & 0xff);
121+
x += px * kernel.items[k];
122+
y += py * kernel.items[k];
123+
z += pz * kernel.items[k];
124+
w += pw * kernel.items[k];
125+
}
126+
}
127+
// d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a;
128+
const a_u32 = @floatToInt(u32, a);
129+
const cond1 = x / @intToFloat(f64, (a_u32 << 24));
130+
const cond2 = y / @intToFloat(f64, (a_u32 << 16));
131+
const cond3 = z / @intToFloat(f64, (a_u32 << 8));
132+
const cond4 = w / a;
133+
// d[j] = cond1 | cond2 | cond3 | cond4;
134+
}
135+
}
136+
}
137+
138+
// TODO: Then vertically blur from tmp -> surface
139+
140+
cairo.c.cairo_surface_destroy(tmp);
141+
cairo.c.cairo_surface_mark_dirty(surface.surface);
142+
}
143+
144+
pub fn main() !void {
145+
const width: u16 = 256;
146+
const height: u16 = 256;
147+
std.debug.print("gaussian_blur example ({}x{} px)\n", .{ width, height });
148+
149+
var surface = try cairo.Surface.image(width, height);
150+
defer surface.destroy();
151+
152+
var cr = try cairo.Context.create(&surface);
153+
defer cr.destroy();
154+
155+
setBackground(&cr);
156+
try image(&cr);
157+
158+
const radius: u16 = 20;
159+
try blurImageSurface(std.testing.allocator, &surface, radius);
160+
161+
_ = surface.writeToPng("examples/generated/gaussian_blur.png");
162+
}

src/cairo.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const std = @import("std");
2-
const c = @import("c.zig");
2+
pub const c = @import("c.zig");
33

44
usingnamespace @import("enums.zig");
55

0 commit comments

Comments
 (0)