summaryrefslogtreecommitdiff
path: root/src/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/gui.zig125
1 files changed, 92 insertions, 33 deletions
diff --git a/src/gui/gui.zig b/src/gui/gui.zig
index c08c27fe..ef2422c5 100644
--- a/src/gui/gui.zig
+++ b/src/gui/gui.zig
@@ -198,10 +198,27 @@ const ImageInstance = extern struct {
};
const NativeImage = struct {
- serial: u32,
+ key: pardes.ImageCacheKey,
texture: *c.SDL_GPUTexture,
};
+fn updateCoreResize(core: *pardes.Pardes, cols: u16, rows: u16, cell_w: u32, cell_h: u32) void {
+ if (comptime pardes.pdf_enabled) {
+ const cell_px_w: u16 = @intCast(@min(@max(1, cell_w), std.math.maxInt(u16)));
+ const cell_px_h: u16 = @intCast(@min(@max(1, cell_h), std.math.maxInt(u16)));
+ if (cols == core.screen_w and rows == core.screen_h and
+ cell_px_w == core.cell_pixels.w and cell_px_h == core.cell_pixels.h) return;
+ core.update(.{ .resize = .{
+ .cols = cols,
+ .rows = rows,
+ .cell_pixels = .{ .w = cell_px_w, .h = cell_px_h },
+ } });
+ } else {
+ if (cols == core.screen_w and rows == core.screen_h) return;
+ core.update(.{ .resize = .{ .cols = cols, .rows = rows } });
+ }
+}
+
// ---- touch: per-finger tracking + shared scroll/tap machines ----
const TouchSample = struct { x: f32 = 0, y: f32 = 0, pressure: f32 = 1 };
@@ -1303,8 +1320,7 @@ fn runNative(init: std.process.Init, opts_in: pardes.Options) !void {
_ = c.SDL_GetWindowSizeInPixels(window, &pw, &ph);
const cols: u16 = @intCast(@max(1, @divTrunc(@as(u32, @intCast(@max(pw, 1))), g.cell_w)));
const rows: u16 = @intCast(@max(1, @divTrunc(@as(u32, @intCast(@max(ph, 1))), g.cell_h)));
- if (cols != core.screen_w or rows != core.screen_h)
- core.update(.{ .resize = .{ .cols = cols, .rows = rows } });
+ updateCoreResize(core, cols, rows, g.cell_w, g.cell_h);
// 7. render — the scroll animation steps LAST before it, while
// core.surface still holds the frame the last pass drew
stepScroll(&g, core, gpa);
@@ -1588,8 +1604,7 @@ fn webFrame(arg: ?*anyopaque) callconv(.c) void {
_ = c.SDL_GetWindowSizeInPixels(g.window, &pw, &ph);
const cols: u16 = @intCast(@max(1, @divTrunc(@as(u32, @intCast(@max(pw, 1))), g.cell_w)));
const rows: u16 = @intCast(@max(1, @divTrunc(@as(u32, @intCast(@max(ph, 1))), g.cell_h)));
- if (cols != core.screen_w or rows != core.screen_h)
- core.update(.{ .resize = .{ .cols = cols, .rows = rows } });
+ updateCoreResize(core, cols, rows, g.cell_w, g.cell_h);
// 4. render — scroll animation last before it, see runNative
stepScroll(g, core, st.gpa);
_ = st.frame_arena.reset(.retain_capacity);
@@ -2762,11 +2777,42 @@ fn validImageBytes(place: pardes.ImagePlace) ?u32 {
return @intCast(bytes);
}
+const NativeBounds = struct { w: u32, h: u32 };
+
+fn nativeBounds(g: *const Gui, place: pardes.ImagePlace) ?NativeBounds {
+ const bound_w = std.math.mul(u32, place.w, g.cell_w) catch return null;
+ const bound_h = std.math.mul(u32, place.h, g.cell_h) catch return null;
+ return .{ .w = bound_w, .h = bound_h };
+}
+
+fn nativePlaceGeometry(g: *const Gui, place: pardes.ImagePlace) ?pardes.image.NativeGeometry {
+ if (comptime pardes.pdf_enabled) {
+ const bounds = nativeBounds(g, place) orelse return null;
+ return pardes.image.nativeGeometry(
+ place.iw,
+ place.ih,
+ bounds.w,
+ bounds.h,
+ place.native.fit,
+ place.native.pan_x,
+ place.native.pan_y,
+ );
+ }
+ return null;
+}
+
+fn nativePlaceDrawable(g: *const Gui, place: pardes.ImagePlace) bool {
+ if (comptime pardes.pdf_enabled) return nativePlaceGeometry(g, place) != null;
+ const bounds = nativeBounds(g, place) orelse return false;
+ const fit = pardes.image.contain(place.iw, place.ih, bounds.w, bounds.h);
+ return fit.w != 0 and fit.h != 0;
+}
+
fn uploadNativeTexture(g: *Gui, cmd: *c.SDL_GPUCommandBuffer, place: pardes.ImagePlace) !void {
const byte_len = validImageBytes(place) orelse return error.BadImage;
const pane: usize = place.pane;
if (g.native_images[pane]) |cached| {
- if (cached.serial == place.serial) return;
+ if (cached.key.eql(place.cacheKey())) return;
releaseNativeImage(g, pane);
}
@@ -2817,10 +2863,10 @@ fn uploadNativeTexture(g: *Gui, cmd: *c.SDL_GPUCommandBuffer, place: pardes.Imag
// allocation is never needed again, so do not retain a second full image
// beside the texture for the life of the pane.
c.SDL_ReleaseGPUTransferBuffer(g.device, transfer);
- g.native_images[pane] = .{ .serial = place.serial, .texture = texture };
+ g.native_images[pane] = .{ .key = place.cacheKey(), .texture = texture };
}
-/// Upload new pane generations and the small per-frame placement buffer.
+/// Upload new pixel generations and the small per-frame placement buffer.
/// Returns the number of image instances drawNativeImagesGpu will consume.
fn prepareNativeImages(
g: *Gui,
@@ -2842,7 +2888,7 @@ fn prepareNativeImages(
for (surface.images[0..surface.nimages]) |maybe| {
const place = maybe orelse continue;
if (g.native_images[place.pane]) |cached| {
- if (cached.serial == place.serial) count += 1;
+ if (cached.key.eql(place.cacheKey()) and nativePlaceDrawable(g, place)) count += 1;
}
}
if (count == 0) return 0;
@@ -2855,28 +2901,41 @@ fn prepareNativeImages(
for (surface.images[0..surface.nimages]) |maybe| {
const place = maybe orelse continue;
const cached = g.native_images[place.pane] orelse continue;
- if (cached.serial != place.serial) continue;
+ if (!cached.key.eql(place.cacheKey())) continue;
- const bound_w = @as(u32, place.w) * g.cell_w;
- const bound_h = @as(u32, place.h) * g.cell_h;
- const fit = pardes.image.contain(place.iw, place.ih, bound_w, bound_h);
- if (fit.w == 0 or fit.h == 0) continue;
- const px0 = @as(u32, place.x) * g.cell_w;
- const py0 = @as(u32, place.y) * g.cell_h;
- const x0 = (@as(f32, @floatFromInt(px0)) / win_w) * 2.0 - 1.0;
- const x1 = (@as(f32, @floatFromInt(px0 + fit.w)) / win_w) * 2.0 - 1.0;
- const y0 = 1.0 - (@as(f32, @floatFromInt(py0)) / win_h) * 2.0;
- const y1 = 1.0 - (@as(f32, @floatFromInt(py0 + fit.h)) / win_h) * 2.0;
- instances[idx] = .{
- .x0 = x0,
- .y0 = y0,
- .x1 = x1,
- .y1 = y1,
- .u0 = 0,
- .v0 = 0,
- .u1 = 1,
- .v1 = 1,
- };
+ const body_x = @as(u32, place.x) * g.cell_w;
+ const body_y = @as(u32, place.y) * g.cell_h;
+ if (comptime pardes.pdf_enabled) {
+ const geometry = nativePlaceGeometry(g, place) orelse continue;
+ const px0 = body_x + geometry.dst.x;
+ const py0 = body_y + geometry.dst.y;
+ const image_w: f32 = @floatFromInt(place.iw);
+ const image_h: f32 = @floatFromInt(place.ih);
+ instances[idx] = .{
+ .x0 = (@as(f32, @floatFromInt(px0)) / win_w) * 2.0 - 1.0,
+ .y0 = 1.0 - (@as(f32, @floatFromInt(py0)) / win_h) * 2.0,
+ .x1 = (@as(f32, @floatFromInt(px0 + geometry.dst.w)) / win_w) * 2.0 - 1.0,
+ .y1 = 1.0 - (@as(f32, @floatFromInt(py0 + geometry.dst.h)) / win_h) * 2.0,
+ .u0 = @as(f32, @floatFromInt(geometry.src.x)) / image_w,
+ .v0 = @as(f32, @floatFromInt(geometry.src.y)) / image_h,
+ .u1 = @as(f32, @floatFromInt(geometry.src.x + geometry.src.w)) / image_w,
+ .v1 = @as(f32, @floatFromInt(geometry.src.y + geometry.src.h)) / image_h,
+ };
+ } else {
+ const bounds = nativeBounds(g, place) orelse continue;
+ const fit = pardes.image.contain(place.iw, place.ih, bounds.w, bounds.h);
+ if (fit.w == 0 or fit.h == 0) continue;
+ instances[idx] = .{
+ .x0 = (@as(f32, @floatFromInt(body_x)) / win_w) * 2.0 - 1.0,
+ .y0 = 1.0 - (@as(f32, @floatFromInt(body_y)) / win_h) * 2.0,
+ .x1 = (@as(f32, @floatFromInt(body_x + fit.w)) / win_w) * 2.0 - 1.0,
+ .y1 = 1.0 - (@as(f32, @floatFromInt(body_y + fit.h)) / win_h) * 2.0,
+ .u0 = 0,
+ .v0 = 0,
+ .u1 = 1,
+ .v1 = 1,
+ };
+ }
idx += 1;
}
c.SDL_UnmapGPUTransferBuffer(g.device, g.image_vxfer);
@@ -2897,7 +2956,8 @@ fn drawNativeImagesGpu(g: *Gui, rp: ?*c.SDL_GPURenderPass, surface: *pardes.Surf
for (surface.images[0..surface.nimages]) |maybe| {
const place = maybe orelse continue;
const cached = g.native_images[place.pane] orelse continue;
- if (cached.serial != place.serial) continue;
+ if (!cached.key.eql(place.cacheKey())) continue;
+ if (!nativePlaceDrawable(g, place)) continue;
const sampler = c.SDL_GPUTextureSamplerBinding{ .texture = cached.texture, .sampler = g.atlas_sampler };
c.SDL_BindGPUFragmentSamplers(pass, 0, &sampler, 1);
const binding = c.SDL_GPUBufferBinding{ .buffer = g.image_vbuf, .offset = idx * @sizeOf(ImageInstance) };
@@ -3190,8 +3250,7 @@ fn refitFont(g: *Gui, core: *pardes.Pardes) void {
_ = c.SDL_GetWindowSizeInPixels(g.window, &pw, &ph);
const cols: u16 = @intCast(@max(1, @divTrunc(@as(u32, @intCast(@max(pw, 1))), g.cell_w)));
const rows: u16 = @intCast(@max(1, @divTrunc(@as(u32, @intCast(@max(ph, 1))), g.cell_h)));
- if (cols != core.screen_w or rows != core.screen_h)
- core.update(.{ .resize = .{ .cols = cols, .rows = rows } });
+ updateCoreResize(core, cols, rows, g.cell_w, g.cell_h);
// ...and a smooth scroll in flight is measured in the OLD grid: scroll_rect
// is a rect of the pane the last frame drew, and scroll_edge is a saved row