summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/pardes.zig58
-rw-r--r--test/image_harness.zig60
2 files changed, 64 insertions, 54 deletions
diff --git a/src/pardes.zig b/src/pardes.zig
index d490c6f3..324a9603 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -378,11 +378,17 @@ test "MuPDF pane renders, navigates, searches, and round-trips its page" {
p.setPdfPage(pane, next_page);
try std.testing.expectEqualStrings(long_query, pane.pdf.?.search_query);
+ // PDF dumps intentionally retain the existing image-compatible schema:
+ // page/path are restored, while pane-local tint starts from the fresh-PDF
+ // default rather than being serialized in the image record.
+ p.togglePdfTint(pane);
+ try std.testing.expectEqual(PdfTintMode.full, pane.pdf.?.tint);
try p.dumpState();
const restored = try Pardes.initFromDump(gpa, .{}, p.dump_out.?);
defer restored.deinit();
try std.testing.expect(hasPdf(restored.panes[0].?));
try std.testing.expectEqual(pane.pdf.?.page, restored.panes[0].?.pdf.?.page);
+ try std.testing.expectEqual(PdfTintMode.filtered, restored.panes[0].?.pdf.?.tint);
}
test "PDF normal adapter consumes unsupported actions and navigates page fallback" {
@@ -534,29 +540,31 @@ test "PdfTint cycles pane-local state and exposes it in the live PDF tag" {
const tint_builtin = std.meta.stringToEnum(Builtin, "PdfTint") orelse
return error.MissingPdfTintBuiltin;
- try std.testing.expectEqual(PdfTintMode.disabled, pv.tint);
+ try std.testing.expectEqual(PdfTintMode.filtered, pv.tint);
const initial_tag = try p.tagText(p.scratch.allocator(), pane);
try std.testing.expect(std.mem.indexOf(
u8,
initial_tag,
- "width PdfFit disabled PdfTint",
+ "width PdfFit filtered PdfTint",
) != null);
p.runBuiltin(tint_builtin, 0, "", null);
- try std.testing.expectEqual(PdfTintMode.filtered, pv.tint);
- const filtered_tag = try p.tagText(p.scratch.allocator(), pane);
- try std.testing.expect(std.mem.indexOf(u8, filtered_tag, "filtered PdfTint") != null);
+ try std.testing.expectEqual(PdfTintMode.full, pv.tint);
+ const full_tag = try p.tagText(p.scratch.allocator(), pane);
+ try std.testing.expect(std.mem.indexOf(u8, full_tag, "full PdfTint") != null);
// The launch companion is a terminal. Running the same pane-scoped word
// there cannot mutate the PDF next to it.
try std.testing.expect(!hasPdf(p.panes[1].?));
p.runBuiltin(tint_builtin, 1, "", null);
- try std.testing.expectEqual(PdfTintMode.filtered, pv.tint);
-
- p.runBuiltin(tint_builtin, 0, "", null);
try std.testing.expectEqual(PdfTintMode.full, pv.tint);
+
p.runBuiltin(tint_builtin, 0, "", null);
try std.testing.expectEqual(PdfTintMode.disabled, pv.tint);
+ const disabled_tag = try p.tagText(p.scratch.allocator(), pane);
+ try std.testing.expect(std.mem.indexOf(u8, disabled_tag, "disabled PdfTint") != null);
+ p.runBuiltin(tint_builtin, 0, "", null);
+ try std.testing.expectEqual(PdfTintMode.filtered, pv.tint);
try std.testing.expectEqual(PdfFitMode.width, pv.fit);
// `dark` intentionally leaves page bg/fg null. PDF tint resolves those
@@ -590,8 +598,9 @@ test "PDF tint and tinted theme changes rebuild every visible raster only" {
const viewport = p.pdfViewport(pane).?;
pv.document_scroll_y = @floatFromInt(pv.page_starts[1] -| viewport.pixel_h / 2);
_ = frame.reset(.retain_capacity);
- const plain_surface = try p.render(frame.allocator());
- try std.testing.expectEqual(@as(usize, 2), plain_surface.nimages);
+ const default_surface = try p.render(frame.allocator());
+ try std.testing.expectEqual(PdfTintMode.filtered, pv.tint);
+ try std.testing.expectEqual(@as(usize, 2), default_surface.nimages);
try std.testing.expectEqual(@as(usize, 2), pv.rasters.items.len);
const Snapshot = struct {
@@ -632,20 +641,9 @@ test "PDF tint and tinted theme changes rebuild every visible raster only" {
}
}
};
- const plain = try Capture.get(plain_surface);
-
- p.togglePdfTint(pane);
- try std.testing.expectEqual(PdfTintMode.filtered, pv.tint);
- for (pv.rasters.items) |raster| try std.testing.expect(!raster.tried);
- _ = frame.reset(.retain_capacity);
- const filtered_surface = try p.render(frame.allocator());
- const filtered = try Capture.get(filtered_surface);
- try Capture.expectGeometry(plain, filtered);
- try std.testing.expectEqual(@as(usize, 2), pv.rasters.items.len);
- for (plain, filtered) |old, new| {
- try std.testing.expect(new.revision > old.revision);
- try std.testing.expect(new.checksum != old.checksum);
- }
+ const default_filtered = try Capture.get(default_surface);
+ for (pv.rasters.items) |raster|
+ try std.testing.expectEqual(PdfTintMode.filtered, raster.tint_key.?.mode);
const dark_index = for (themes, 0..) |th, i| {
if (std.mem.eql(u8, th.name, "dark")) break i;
@@ -655,8 +653,8 @@ test "PDF tint and tinted theme changes rebuild every visible raster only" {
_ = frame.reset(.retain_capacity);
const themed_surface = try p.render(frame.allocator());
const themed = try Capture.get(themed_surface);
- try Capture.expectGeometry(filtered, themed);
- for (filtered, themed) |old, new| {
+ try Capture.expectGeometry(default_filtered, themed);
+ for (default_filtered, themed) |old, new| {
try std.testing.expect(new.revision > old.revision);
try std.testing.expect(new.checksum != old.checksum);
}
@@ -695,9 +693,9 @@ test "PDF tint and tinted theme changes rebuild every visible raster only" {
const disabled_surface = try p.render(frame.allocator());
const disabled = try Capture.get(disabled_surface);
try Capture.expectGeometry(full, disabled);
- for (plain, disabled) |original, restored| {
- try std.testing.expect(restored.revision > original.revision);
- try std.testing.expectEqual(original.checksum, restored.checksum);
+ for (full, disabled) |old, source| {
+ try std.testing.expect(source.revision > old.revision);
+ try std.testing.expect(source.checksum != old.checksum);
}
const acme_index = for (themes, 0..) |th, i| {
@@ -3132,7 +3130,7 @@ const PdfView = if (pdf_enabled) struct {
layout_anchor_fraction: f64 = 0,
next_raster_revision: u32 = 0,
fit: PdfFitMode = .width,
- tint: PdfTintMode = .disabled,
+ tint: PdfTintMode = .filtered,
/// Normalized 0..65535 pan positions. NativeGeometry maps the extrema
/// exactly onto the source overflow, independent of page or cell size.
pan_x: u16 = 0,
diff --git a/test/image_harness.zig b/test/image_harness.zig
index 73142459..d4cb8955 100644
--- a/test/image_harness.zig
+++ b/test/image_harness.zig
@@ -22,8 +22,11 @@ extern "c" fn unsetenv(name: [*:0]const u8) c_int;
const gpa = std.heap.page_allocator;
const fixture_rgb = [3]u8{ 13, 77, 231 };
-const pdf_red = [3]u8{ 255, 0, 0 };
-const pdf_green = [3]u8{ 0, 255, 0 };
+// The harness isolates configuration, so fresh PDFs use the boot `helix`
+// theme (#080808 paper, #bdbdbd ink). These are the exact fixed-point
+// filtered-tint results for the fixture's saturated red and green pages.
+const pdf_filtered_red = [3]u8{ 255, 77, 77 };
+const pdf_filtered_green = [3]u8{ 0, 162, 0 };
pub fn main(init: std.process.Init) !void {
var arena_state: std.heap.ArenaAllocator = .init(gpa);
@@ -240,11 +243,14 @@ fn runKittyPdf(arena: std.mem.Allocator, exe: [:0]const u8, base: [:0]const u8)
_ = try h.pumpOnce(50);
const sent = std.mem.indexOf(u8, h.raw.items, transmit) != null;
const placed = std.mem.indexOf(u8, h.raw.items, "\x1b_Ga=p,i=") != null;
- if (sent and placed and h.contains("pdf 1/4 width PdfFit")) break;
+ if (sent and placed and h.contains("pdf 1/4 width PdfFit filtered PdfTint")) break;
}
try h.expectRawContains(transmit, "first PDF page was not transmitted as the 96x64 Kitty raster");
try h.expectRawContains("\x1b_Ga=p,i=", "first PDF page was not placed in the pane body");
- try h.expectContains("pdf 1/4 width PdfFit", "PDF pane did not expose its initial width fit");
+ try h.expectContains(
+ "pdf 1/4 width PdfFit filtered PdfTint",
+ "fresh PDF pane did not expose filtered tint as its default",
+ );
// Fit-width is the initial PDF policy. The short fixture pages make the
// first boundary visible immediately, so both page rasters must coexist.
@@ -472,27 +478,27 @@ fn runGuiPdf(arena: std.mem.Allocator, exe: [:0]const u8, base: [:0]const u8) !v
var h = try eh.Harness.initArgs(gpa, exe, 10, 20, pdf_path);
defer h.deinit();
- const fit_width = try waitForCaptureBounds(&h, capture_path, pdf_red, 500, 5000);
- const initial_green = try waitForCaptureBounds(&h, capture_path, pdf_green, 500, 5000);
- try expectThemeGap(capture_path, fit_width, initial_green);
+ const fit_width = try waitForCaptureBounds(&h, capture_path, pdf_filtered_red, 500, 5000);
+ const initial_green = try waitForCaptureBounds(&h, capture_path, pdf_filtered_green, 500, 5000);
+ try expectThemeGap(capture_path, fit_width, initial_green, pdf_filtered_red, pdf_filtered_green);
// One row moves both native quads by the exact document distance. They
// remain together on screen, separated by the same theme-colored gap; a
// page-at-a-time implementation would replace red with green here.
_ = libc.unlink(capture_path);
try h.send("j");
- const moved_red = try waitForCaptureBounds(&h, capture_path, pdf_red, 500, 5000);
- const moved_green = try waitForCaptureBounds(&h, capture_path, pdf_green, 500, 5000);
+ const moved_red = try waitForCaptureBounds(&h, capture_path, pdf_filtered_red, 500, 5000);
+ const moved_green = try waitForCaptureBounds(&h, capture_path, pdf_filtered_green, 500, 5000);
if (moved_red.max_y >= fit_width.max_y or moved_green.min_y >= initial_green.min_y)
return error.SdlPdfContinuousStepDidNotMove;
- try expectThemeGap(capture_path, moved_red, moved_green);
+ try expectThemeGap(capture_path, moved_red, moved_green, pdf_filtered_red, pdf_filtered_green);
// Fit-height fills/crops the full body height. These are GPU readback
// extents, so this exercises SDL's UV crop and destination rectangle
// rather than merely observing the page color.
_ = libc.unlink(capture_path);
try h.send(" tz");
- const fit_height = try waitForCaptureBounds(&h, capture_path, pdf_red, 500, 5000);
+ const fit_height = try waitForCaptureBounds(&h, capture_path, pdf_filtered_red, 500, 5000);
const width_delta = if (fit_width.width() > fit_height.width())
fit_width.width() - fit_height.width()
else
@@ -507,17 +513,17 @@ fn runGuiPdf(arena: std.mem.Allocator, exe: [:0]const u8, base: [:0]const u8) !v
// leave page two at the top, proving continuous traversal in GPU output.
_ = libc.unlink(capture_path);
try h.send("j");
- const height_red = try waitForCaptureBounds(&h, capture_path, pdf_red, 500, 5000);
- const height_green = try waitForCaptureBounds(&h, capture_path, pdf_green, 200, 5000);
- try expectThemeGap(capture_path, height_red, height_green);
+ const height_red = try waitForCaptureBounds(&h, capture_path, pdf_filtered_red, 500, 5000);
+ const height_green = try waitForCaptureBounds(&h, capture_path, pdf_filtered_green, 200, 5000);
+ try expectThemeGap(capture_path, height_red, height_green, pdf_filtered_red, pdf_filtered_green);
_ = libc.unlink(capture_path);
try h.send("jjjjjjjj");
- const green = try waitForCaptureBounds(&h, capture_path, pdf_green, 500, 5000);
+ const green = try waitForCaptureBounds(&h, capture_path, pdf_filtered_green, 500, 5000);
const final_capture = try readFile(gpa, capture_path);
defer gpa.free(final_capture);
const final_pixels = ppmPixels(final_capture) orelse return error.BadCapture;
- if (countColor(final_pixels, pdf_red) != 0) return error.SdlPdfOffscreenPageStillDrawn;
+ if (countColor(final_pixels, pdf_filtered_red) != 0) return error.SdlPdfOffscreenPageStillDrawn;
// Exercise the actual PdfSections output + Look stepping path in SDL too.
// The first two nested outline rows stay on red page one at different y
@@ -525,22 +531,22 @@ fn runGuiPdf(arena: std.mem.Allocator, exe: [:0]const u8, base: [:0]const u8) !v
// the nested page-one destination.
_ = libc.unlink(capture_path);
try h.send("gg");
- _ = try waitForCaptureBounds(&h, capture_path, pdf_red, 500, 5000);
+ _ = try waitForCaptureBounds(&h, capture_path, pdf_filtered_red, 500, 5000);
_ = libc.unlink(capture_path);
try h.send(" ts");
- _ = try waitForCaptureBounds(&h, capture_path, pdf_red, 500, 5000);
+ _ = try waitForCaptureBounds(&h, capture_path, pdf_filtered_red, 500, 5000);
_ = libc.unlink(capture_path);
try h.send("n");
- _ = try waitForCaptureBounds(&h, capture_path, pdf_red, 200, 5000);
+ _ = try waitForCaptureBounds(&h, capture_path, pdf_filtered_red, 200, 5000);
_ = libc.unlink(capture_path);
try h.send("n");
- _ = try waitForCaptureBounds(&h, capture_path, pdf_red, 100, 5000);
+ _ = try waitForCaptureBounds(&h, capture_path, pdf_filtered_red, 100, 5000);
_ = libc.unlink(capture_path);
try h.send("n");
- _ = try waitForCaptureBounds(&h, capture_path, pdf_green, 200, 5000);
+ _ = try waitForCaptureBounds(&h, capture_path, pdf_filtered_green, 200, 5000);
_ = libc.unlink(capture_path);
try h.send("N");
- _ = try waitForCaptureBounds(&h, capture_path, pdf_red, 100, 5000);
+ _ = try waitForCaptureBounds(&h, capture_path, pdf_filtered_red, 100, 5000);
std.debug.print("PDF harness ok: SDL continuous strip + nested PdfSections red->green->red ({d}->{d} pixels)\n", .{ fit_width.count, green.count });
}
@@ -586,7 +592,13 @@ fn waitForCaptureBounds(
return error.PdfPageColorMissing;
}
-fn expectThemeGap(path: [:0]const u8, upper: ColorBounds, lower: ColorBounds) !void {
+fn expectThemeGap(
+ path: [:0]const u8,
+ upper: ColorBounds,
+ lower: ColorBounds,
+ upper_color: [3]u8,
+ lower_color: [3]u8,
+) !void {
if (lower.min_y <= upper.max_y + 1) return error.SdlPdfGapMissing;
const gap = lower.min_y - upper.max_y - 1;
if (gap < 7 or gap > 9) return error.SdlPdfGapWrongSize;
@@ -598,7 +610,7 @@ fn expectThemeGap(path: [:0]const u8, upper: ColorBounds, lower: ColorBounds) !v
const x1 = @min(upper.max_x, lower.max_x) -| 2;
if (x0 > x1 or y >= ppm.height) return error.SdlPdfGapOutOfBounds;
const first = ppm.pixels[(y * ppm.width + x0) * 3 ..][0..3];
- if (std.mem.eql(u8, first, &pdf_red) or std.mem.eql(u8, first, &pdf_green))
+ if (std.mem.eql(u8, first, &upper_color) or std.mem.eql(u8, first, &lower_color))
return error.SdlPdfGapCovered;
var x = x0 + 1;
while (x <= x1) : (x += 1) {