diff options
| author | t <t@tjp.lol> | 2026-06-22 18:18:28 -0600 |
|---|---|---|
| committer | t <t@tjp.lol> | 2026-06-23 09:34:55 -0600 |
| commit | 538a5d926fa626a00cc3dc12c555f11f82867efd (patch) | |
| tree | 7ea3b84ea5086d67973d0b427ec7c382eb635e50 /libpanto/src/image.zig | |
| parent | a417727810cc6722b3c82e3c9e9b46982ef40a08 (diff) | |
libpanto ponytail-audit simplifications
Diffstat (limited to 'libpanto/src/image.zig')
| -rw-r--r-- | libpanto/src/image.zig | 50 |
1 files changed, 24 insertions, 26 deletions
diff --git a/libpanto/src/image.zig b/libpanto/src/image.zig index 0367373..26474d1 100644 --- a/libpanto/src/image.zig +++ b/libpanto/src/image.zig @@ -2,8 +2,8 @@ //! //! Two responsibilities: //! -//! 1. `detectMediaType` — identify an attachment's MIME type from its -//! leading bytes (magic numbers), not its file extension. +//! 1. `detectCodec` — identify an attachment's codec from its leading +//! bytes (magic numbers), not its file extension. //! 2. `maybeResize` — bound raster images to `max_dim` on each side so a //! single screenshot can't blow out the model's context. PDFs and //! already-small images pass through untouched. @@ -29,10 +29,9 @@ const jpeg_quality: c_int = 80; pub const Codec = enum { png, jpeg, gif, bmp, webp, pdf }; -/// Detect a supported media type from leading bytes. Returns null when the -/// bytes don't match any known signature. -pub fn detectMediaType(bytes: []const u8) ?[]const u8 { - return switch (detectCodec(bytes) orelse return null) { +/// The MIME type string for a codec (static; do not free). +pub fn mediaTypeForCodec(codec: Codec) []const u8 { + return switch (codec) { .png => "image/png", .jpeg => "image/jpeg", .gif => "image/gif", @@ -88,28 +87,27 @@ pub const Processed = struct { /// detection recognizes the bytes — the caller decides how to surface /// that (e.g. drop the attachment, or fall back to text). pub fn process(allocator: Allocator, bytes: []const u8, hint: ?[]const u8) !Processed { - const media_type = blk: { + const codec = blk: { if (hint) |h| { - if (codecForMediaType(h) != null) break :blk h; + if (codecForMediaType(h)) |hinted| break :blk hinted; } - break :blk detectMediaType(bytes) orelse return error.UnknownMediaType; + break :blk detectCodec(bytes) orelse return error.UnknownMediaType; }; - return maybeResize(allocator, bytes, media_type); + return maybeResize(allocator, bytes, codec); } /// Resize `bytes` so neither dimension exceeds `max_dim`, preserving the /// input codec where possible. Returns an owned copy of the (possibly /// unchanged) bytes plus the resulting media type. /// -/// - PDF or unknown: returned verbatim (a copy), media type unchanged. +/// - PDF: returned verbatim (a copy), media type unchanged. /// - raster <= max_dim on both sides: returned verbatim (a copy) — we /// skip the decode/encode round-trip to avoid quality loss + CPU. /// - stb-supported raster larger than max_dim: decode -> resize -> same /// codec. /// - WEBP larger than max_dim: jebp decode -> resize -> JPEG. -pub fn maybeResize(allocator: Allocator, bytes: []const u8, media_type: []const u8) !Processed { - const codec = codecForMediaType(media_type) orelse - return .{ .media_type = media_type, .data = try allocator.dupe(u8, bytes) }; +pub fn maybeResize(allocator: Allocator, bytes: []const u8, codec: Codec) !Processed { + const media_type = mediaTypeForCodec(codec); if (codec == .pdf) return .{ .media_type = media_type, .data = try allocator.dupe(u8, bytes) }; @@ -268,16 +266,16 @@ fn resizeWebp(allocator: Allocator, bytes: []const u8, media_type: []const u8) ! const testing = std.testing; -test "detectMediaType - magic bytes" { - try testing.expectEqualStrings("image/png", detectMediaType(&.{ 0x89, 'P', 'N', 'G', 0x0D, 0x0A, 0x1A, 0x0A }).?); - try testing.expectEqualStrings("image/jpeg", detectMediaType(&.{ 0xFF, 0xD8, 0xFF, 0xE0 }).?); - try testing.expectEqualStrings("image/gif", detectMediaType("GIF89a....").?); - try testing.expectEqualStrings("image/bmp", detectMediaType("BM....").?); - try testing.expectEqualStrings("application/pdf", detectMediaType("%PDF-1.7").?); +test "detectCodec + mediaTypeForCodec - magic bytes" { + try testing.expectEqualStrings("image/png", mediaTypeForCodec(detectCodec(&.{ 0x89, 'P', 'N', 'G', 0x0D, 0x0A, 0x1A, 0x0A }).?)); + try testing.expectEqualStrings("image/jpeg", mediaTypeForCodec(detectCodec(&.{ 0xFF, 0xD8, 0xFF, 0xE0 }).?)); + try testing.expectEqualStrings("image/gif", mediaTypeForCodec(detectCodec("GIF89a....").?)); + try testing.expectEqualStrings("image/bmp", mediaTypeForCodec(detectCodec("BM....").?)); + try testing.expectEqualStrings("application/pdf", mediaTypeForCodec(detectCodec("%PDF-1.7").?)); const webp = "RIFF" ++ &[_]u8{ 0, 0, 0, 0 } ++ "WEBP"; - try testing.expectEqualStrings("image/webp", detectMediaType(webp).?); - try testing.expect(detectMediaType("not an image") == null); - try testing.expect(detectMediaType(&.{0x89}) == null); + try testing.expectEqualStrings("image/webp", mediaTypeForCodec(detectCodec(webp).?)); + try testing.expect(detectCodec("not an image") == null); + try testing.expect(detectCodec(&.{0x89}) == null); } test "targetDims - skip when small, scale when large preserving aspect" { @@ -315,7 +313,7 @@ test "process - detects type from raw bytes when hint absent" { test "maybeResize - PDF passes through unchanged" { const a = testing.allocator; const pdf = "%PDF-1.7\nfake pdf body"; - const out = try maybeResize(a, pdf, "application/pdf"); + const out = try maybeResize(a, pdf, .pdf); defer a.free(out.data); try testing.expectEqualStrings("application/pdf", out.media_type); try testing.expectEqualStrings(pdf, out.data); @@ -334,7 +332,7 @@ test "maybeResize - small PNG round-trips, large PNG shrinks and stays PNG" { var sctx = StbWriteCtx{ .list = &small_png, .allocator = a }; try testing.expect(c.stbi_write_png_to_func(stbWriteCb, &sctx, small_w, small_h, 4, &small_px, small_w * 4) != 0); - const small_out = try maybeResize(a, small_png.items, "image/png"); + const small_out = try maybeResize(a, small_png.items, .png); defer a.free(small_out.data); try testing.expectEqualStrings("image/png", small_out.media_type); // Small image is returned verbatim (byte-identical copy). @@ -351,7 +349,7 @@ test "maybeResize - small PNG round-trips, large PNG shrinks and stays PNG" { var bctx = StbWriteCtx{ .list = &big_png, .allocator = a }; try testing.expect(c.stbi_write_png_to_func(stbWriteCb, &bctx, big_w, big_h, 4, big_px.ptr, big_w * 4) != 0); - const big_out = try maybeResize(a, big_png.items, "image/png"); + const big_out = try maybeResize(a, big_png.items, .png); defer a.free(big_out.data); try testing.expectEqualStrings("image/png", big_out.media_type); const dims = probeDims(big_out.data).?; |
