Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ jobs:
uses: actions/checkout@v3
- name: Install Zig
uses: mlugg/setup-zig@v2
- name: Test
run: zig build test
- name: Build and run
working-directory: ./example
run: zig build run -Doptimize=${{ matrix.opt }}
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,33 @@ for ([_]u21{ 'A', 'B', 'C' }) |codepoint| {
}
```

## Modules
The package provides two modules:

- `mist` — the full library: the freetype-backed font frontend (glyph outlines, metrics, kerning, atlas packing) on top
of everything below. Requires libc.
- `mist-core` — just the SDF generation core. It has no dependencies: no freetype, no libc, so it builds for
freestanding targets such as `wasm32-freestanding`. Feed it outlines yourself (e.g. from your own font parser) through
`ShapeSink`:

```zig
const mist = @import("mist-core");

var shape: mist.Shape = .{};
defer shape.deinit(allocator);

var sink: mist.ShapeSink = .{ .allocator = allocator, .shape = &shape, .scale = 1.0 / font_units_per_em };
try sink.moveTo(.{ x0, y0 });
try sink.lineTo(.{ x1, y1 });
try sink.quadTo(.{ cx, cy }, .{ x2, y2 });

const glyph = try mist.generateSingle(allocator, &shape, .{
.advance = advance,
.bearing_x = bearing_x,
.bearing_y = bearing_y,
}, &gen_opts);
defer glyph.deinit(allocator);
```

## Note
The library exposes a similar set of functionality to Chlumský's work, but it's not intended to be its equivalent, some features will be implemented differently and some will be intentionally omitted. Issues and PRs can still compare to msdfgen, like if the library generates a less correct SDF than it, but direct ports, or the request for them will be closed.
21 changes: 20 additions & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,17 @@ const std = @import("std");
pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target = b.standardTargetOptions(.{});

// Dependency-free SDF core; usable on any target, including freestanding
// ones (wasm), as it neither links nor imports freetype/libc.
const core_module = b.addModule("mist-core", .{
.root_source_file = b.path("src/mist-core/core.zig"),
});

_ = b.addModule("mist", .{
.root_source_file = b.path("src/Generator.zig"),
.root_source_file = b.path("src/mist/Generator.zig"),
.imports = &.{
.{ .name = "mist-core", .module = core_module },
.{
.name = "mach-freetype",
.module = b.dependency("mach_freetype", .{
Expand All @@ -22,4 +30,15 @@ pub fn build(b: *std.Build) void {
},
},
});

const test_step = b.step("test", "Run module tests");

const core_tests = b.addTest(.{
.root_module = b.createModule(.{
.root_source_file = b.path("src/mist-core/core.zig"),
.target = target,
.optimize = optimize,
}),
});
test_step.dependOn(&b.addRunArtifact(core_tests).step);
}
2 changes: 1 addition & 1 deletion build.zig.zon
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.{
.name = .mist,
.fingerprint = 0xfc749f7d2126c2dd,
.version = "1.0.0",
.version = "1.1.0",
.paths = .{
"LICENSE",
"libs",
Expand Down
File renamed without changes.
25 changes: 13 additions & 12 deletions src/Scanline.zig → src/mist-core/Scanline.zig
Original file line number Diff line number Diff line change
Expand Up @@ -36,36 +36,36 @@ pub fn overlap(a: Scanline, b: Scanline, x_from: f64, x_to: f64, fill_rule: Fill
var total: f64 = 0.0;
var a_inside = false;
var b_inside = false;
var ai: i32 = 0;
var bi: i32 = 0;
var ax = if (!a.intersections.empty()) a.intersections[ai].x else x_to;
var bx = if (!b.intersections.empty()) b.intersections[bi].x else x_to;
var ai: usize = 0;
var bi: usize = 0;
var ax = if (a.intersections.items.len != 0) a.intersections.items[ai].x else x_to;
var bx = if (b.intersections.items.len != 0) b.intersections.items[bi].x else x_to;
while (ax < x_from or bx < x_from) {
const x_next = @min(ax, bx);
if (ax == x_next and ai < a.intersections.items.len) {
a_inside = interpretFillRule(a.intersections.items[ai].dir, fill_rule);
ai += 1;
ax = if (ai < a.intersections.items.len) a.intersections[ai].x else x_to;
ax = if (ai < a.intersections.items.len) a.intersections.items[ai].x else x_to;
}
if (bx == x_next and bi < b.intersections.items.len) {
b_inside = interpretFillRule(b.intersections[bi].dir, fill_rule);
b_inside = interpretFillRule(b.intersections.items[bi].dir, fill_rule);
bi += 1;
bx = if (bi < b.intersections.items.len) b.intersections[bi].x else x_to;
bx = if (bi < b.intersections.items.len) b.intersections.items[bi].x else x_to;
}
}
var x = x_from;
while (ax < x_to or bx < x_to) {
const x_next = @min(ax, bx);
if (a_inside == b_inside) total += x_next - x;
if (ax == x_next and ai < a.intersections.items.len) {
a_inside = interpretFillRule(a.intersections[ai].dir, fill_rule);
a_inside = interpretFillRule(a.intersections.items[ai].dir, fill_rule);
ai += 1;
ax = if (ai < a.intersections.items.len) a.intersections[ai].x else x_to;
ax = if (ai < a.intersections.items.len) a.intersections.items[ai].x else x_to;
}
if (bx == x_next and bi < b.intersections.items.len) {
b_inside = interpretFillRule(b.intersections[bi].dir, fill_rule);
b_inside = interpretFillRule(b.intersections.items[bi].dir, fill_rule);
bi += 1;
bx = if (bi < b.intersections.items.len) b.intersections[bi].x else x_to;
bx = if (bi < b.intersections.items.len) b.intersections.items[bi].x else x_to;
}
x = x_next;
}
Expand All @@ -74,7 +74,8 @@ pub fn overlap(a: Scanline, b: Scanline, x_from: f64, x_to: f64, fill_rule: Fill
}

pub fn countIntersections(self: *Scanline, x: f64) i32 {
return (self.moveTo(x) orelse return 0) + 1;
const index = self.moveTo(x) orelse return 0;
return @intCast(index + 1);
}

pub fn sumIntersections(self: *Scanline, x: f64) i32 {
Expand Down
File renamed without changes.
File renamed without changes.
14 changes: 7 additions & 7 deletions src/coloring.zig → src/mist-core/coloring.zig
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const std = @import("std");

const EdgeSegment = @import("EdgeSegment.zig");
const f64i = @import("Generator.zig").f64i;
const f64i = @import("core.zig").f64i;
const math = @import("math.zig");
const Shape = @import("Shape.zig");

Expand Down Expand Up @@ -45,7 +45,7 @@ pub const EdgeColor = enum(u8) {

pub fn init(rng: *std.Random.DefaultPrng) EdgeColor {
const two_ch: [3]EdgeColor = .{ .red_green, .red_blue, .green_blue };
return two_ch[rng.next() % two_ch.len];
return two_ch[@intCast(rng.next() % two_ch.len)];
}

fn cmyWithExcls(excl_1: EdgeColor, excl_2: EdgeColor) EdgeColor {
Expand Down Expand Up @@ -90,7 +90,7 @@ pub const EdgeColor = enum(u8) {
switch (self.*) {
inline .red_green, .red_blue, .green_blue => |c| {
const switch_targets = comptime colorSwitchTargets(c);
self.* = switch_targets[rng.next() % switch_targets.len];
self.* = switch_targets[@intCast(rng.next() % switch_targets.len)];
},
else => unreachable,
}
Expand Down Expand Up @@ -379,19 +379,19 @@ fn colorGraph(colors: []EdgeColor, edge_matrix: []const []const bool, rng: *std.
switch (banned_colors) {
.{} => {
const rem_colors: [3]EdgeColor = .{ .red_green, .red_blue, .green_blue };
color.* = rem_colors[rng.next() % rem_colors.len];
color.* = rem_colors[@intCast(rng.next() % rem_colors.len)];
},
.{ .red_green = true } => {
const rem_colors: [2]EdgeColor = .{ .red_blue, .green_blue };
color.* = rem_colors[rng.next() % rem_colors.len];
color.* = rem_colors[@intCast(rng.next() % rem_colors.len)];
},
.{ .red_blue = true } => {
const rem_colors: [2]EdgeColor = .{ .red_green, .green_blue };
color.* = rem_colors[rng.next() % rem_colors.len];
color.* = rem_colors[@intCast(rng.next() % rem_colors.len)];
},
.{ .green_blue = true } => {
const rem_colors: [2]EdgeColor = .{ .red_green, .red_blue };
color.* = rem_colors[rng.next() % rem_colors.len];
color.* = rem_colors[@intCast(rng.next() % rem_colors.len)];
},
.{ .red_green = true, .red_blue = true } => color.* = .green_blue,
.{ .red_green = true, .green_blue = true } => color.* = .red_blue,
Expand Down
Loading
Loading