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
12 changes: 12 additions & 0 deletions include/tile57.h
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,18 @@ tile57_status tile57_chart_tile_surface(tile57_chart *chart, uint8_t z, uint32_t
const tile57_mariner *m,
const tile57_surface_cb *surface, tile57_error *err);

/* Portray ONE MLT tile from CALLER-SUPPLIED bytes to a surface — the archive-less
* twin of tile57_chart_tile_surface. For a host that fetched a tile (e.g. over HTTP
* from a tile server) and wants it painted with NO chart open: `mlt`/`mlt_len` are
* the raw (DECOMPRESSED) MLT tile bytes; (z,x,y) place it. Same WORLD-SPACE tagged
* draw calls, callbacks, and PER-TILE decluttering as tile57_chart_tile_surface;
* the colour profile + symbol catalogue are the ones baked into the library. An
* undecodable tile paints nothing (still TILE57_OK). */
tile57_status tile57_render_mlt_tile(const uint8_t *mlt, size_t mlt_len,
uint8_t z, uint32_t x, uint32_t y,
const tile57_mariner *m,
const tile57_surface_cb *surface, tile57_error *err);

/* Release a chart and all cached tiles. Must not be called while any borrower
* (a compositor, a renderer thread) may still read from it. */
void tile57_chart_close(tile57_chart *chart);
Expand Down
23 changes: 23 additions & 0 deletions src/capi.zig
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,29 @@ export fn tile57_chart_tile_surface(
return OK;
}

/// Portray ONE MLT tile from CALLER-SUPPLIED bytes to a surface — the archive-less
/// twin of tile57_chart_tile_surface. For a host that fetched a tile (e.g. over
/// HTTP from a tile server) and wants it painted with no chart open: `mlt`/`mlt_len`
/// are the raw (decompressed) MLT tile bytes, (z,x,y) place it, decluttering is
/// per-tile. Same WORLD-SPACE tagged draw calls as tile57_chart_tile_surface. The
/// colour profile + symbol catalogue are the ones baked into the library. See tile57.h.
export fn tile57_render_mlt_tile(
mlt: ?[*]const u8,
mlt_len: usize,
z: u8,
x: u32,
y: u32,
m: ?*const CMariner,
surface: ?*const CSurface,
err: ?*CError,
) callconv(.c) c_int {
const b = mlt orelse return failWith(err, .badarg, "mlt bytes must not be null");
const sfc = surface orelse return failWith(err, .badarg, "surface must not be null");
const settings: mariner.Settings = if (m) |p| marinerFromC(p) else .{};
chart.renderMltTileSurface(b[0..mlt_len], z, x, y, paletteOf(&settings), &settings, sfc) catch |e| return fail(err, e);
return OK;
}

/// Release a chart and all cached tiles. Must not be called while any borrower
/// (a compositor, a renderer) may still read from it. See tile57.h.
export fn tile57_chart_close(handle: ?*Chart) callconv(.c) void {
Expand Down
74 changes: 74 additions & 0 deletions src/chart.zig
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,80 @@ fn sharedColors() !*render.resolve.Colors {
return &shared_colors;
}

// Process-global per-palette symbol stores — the handle-less twin of
// Chart.viewStoreFor, for a renderer that has tile bytes but no chart handle
// (renderMltTileSurface / tile57_render_mlt_tile). The SVG-catalogue parse is
// costly, so build each palette once, gpa-owned for the process lifetime. Same
// one-shot CAS+spin as sharedColors; contends only on the first tile per palette.
var store_state: [3]std.atomic.Value(u8) = .{ .init(0), .init(0), .init(0) };
var shared_stores: [3]?*sprite.CatalogStore = .{ null, null, null };
var shared_store_err: [3]?anyerror = .{ null, null, null };

fn sharedStore(palette: render.resolve.PaletteId) !*sprite.CatalogStore {
const i: usize = @intFromEnum(palette);
while (store_state[i].load(.acquire) != 2) {
if (store_state[i].cmpxchgStrong(@as(u8, 0), @as(u8, 1), .acquire, .monotonic) == null) {
if (viewSymbolStore(gpa, palette)) |st| {
shared_stores[i] = st;
} else |e| {
shared_store_err[i] = e;
}
store_state[i].store(2, .release);
break;
}
std.atomic.spinLoopHint();
}
if (shared_store_err[i]) |e| return e;
return shared_stores[i].?;
}

/// Portray ONE MLT tile from CALLER-SUPPLIED bytes to a surface — the handle-less
/// twin of Chart.renderSurfaceTile, for a host holding tile bytes (e.g. fetched
/// over HTTP from a tile server) but with no chart archive open. Colours and the
/// per-palette symbol store come from the process-global caches; decluttering is
/// per-tile (as with renderSurfaceTile). `bytes` are raw (decompressed) MLT.
pub fn renderMltTileSurface(bytes: []const u8, z: u8, x: u32, y: u32, palette: render.resolve.PaletteId, settings: *const render.resolve.Settings, cb: *const render.vector.CSurface) !void {
var arena = std.heap.ArenaAllocator.init(gpa);
defer arena.deinit();
const a = arena.allocator();

const colors = try sharedColors();
const store = try sharedStore(palette);

var vs = render.vector.VectorSurface.init(a, colors, palette, settings, cb);
vs.store = store.asStore();
vs.view_zoom = @floatFromInt(z); // declutter at the tile's native zoom
const surf = vs.asSurface();

try surf.beginScene(z);
if (mlt.decode(a, bytes)) |layers| {
vs.setTile(z, x, y);
scene.replayTile(a, surf, layers) catch {};
} else |_| {} // an undecodable tile paints nothing, not an error
_ = try surf.endScene(a);
}

test "renderMltTileSurface: handle-less colour/store/surface setup; undecodable tile is a no-op" {
const V = render.vector;
const noop = struct {
fn fill(_: ?*anyopaque, _: *const V.CFeature, _: *const V.CWorldRings, _: V.CColor, _: c_int) callconv(.c) void {}
fn stroke(_: ?*anyopaque, _: *const V.CFeature, _: *const V.CWorldRings, _: f32, _: f32, _: f32, _: V.CColor) callconv(.c) void {}
fn symbol(_: ?*anyopaque, _: *const V.CFeature, _: V.CWorldPt, _: *const V.CLocalRings, _: V.CColor, _: c_int, _: f32, _: V.CRotAlign) callconv(.c) void {}
fn text(_: ?*anyopaque, _: *const V.CFeature, _: V.CWorldPt, _: *const V.CLocalRings, _: V.CColor, _: V.CColor, _: f32, _: V.CRotAlign) callconv(.c) void {}
};
const cb = V.CSurface{
.ctx = null,
.fill_area = noop.fill,
.stroke_line = noop.stroke,
.draw_symbol = noop.symbol,
.draw_text = noop.text,
};
const settings = render.resolve.Settings{};
// No chart handle: this drives sharedColors + sharedStore + the whole surface
// lifecycle. Garbage bytes fail to decode and paint nothing — still success.
try renderMltTileSurface(&[_]u8{ 0, 1, 2, 3 }, 14, 4680, 6260, .day, &settings, &cb);
}

// Env access lives in C (Zig 0.16 puts env behind Io); returns the S-101 rules
// dir from TILE57_S101_RULES or null. Provided by the portrayal C shim.
extern fn tg_env_rules() callconv(.c) ?[*:0]const u8;
Expand Down
Loading