summaryrefslogtreecommitdiff
path: root/src/jpg/segment.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/jpg/segment.zig')
-rw-r--r--src/jpg/segment.zig149
1 files changed, 120 insertions, 29 deletions
diff --git a/src/jpg/segment.zig b/src/jpg/segment.zig
index 9b55780..273f63d 100644
--- a/src/jpg/segment.zig
+++ b/src/jpg/segment.zig
@@ -6,23 +6,15 @@ pub const Segment = union(enum) {
reset: void,
start_of_image: void,
end_of_image: void,
- start_of_frame: void,
- define_quantization_table: void,
- define_huffman_table: void,
- define_restart_interval: void,
- define_number_of_lines: void,
- define_hierarchy_progression: void,
- extend_huffman_table: void,
jfif: jpg.Jfif,
- application_segment: void,
- comment: void,
- start_of_scan: void,
+ frame: jpg.Frame,
+ quantization_tables: [4]?jpg.QuantizationTable,
+ huffman_tables: []jpg.HuffmanTable,
+ scan: jpg.Scan,
const Self = @This();
pub fn read(gpa: std.mem.Allocator, reader: *std.Io.Reader) !Self {
- _ = gpa;
-
if (try reader.peekByte() != 0xff) {
return error.InvalidMarkerHeader;
}
@@ -37,35 +29,134 @@ pub const Segment = union(enum) {
.reset => .{ .reset = void{} },
.start_of_image => .{ .start_of_image = void{} },
.end_of_image => .{ .end_of_image = void{} },
- .application_segment => |n| switch (n) {
- 0 => jfif: {
- // NOTE: we can toss the length of this segment
- // since the JFIF segment has a predefined size
- reader.toss(2);
- break :jfif .{
- .jfif = try jpg.Jfif.read(reader)
- };
- },
- else => unreachable,
+ .application_segment => |n| read_application_segment(reader, n),
+ .define_quantization_table => dqt: {
+ var size = try reader.takeInt(u16, .big) - 2;
+ var tables: [4]?jpg.QuantizationTable = .{ null, null, null, null };
+
+ while (size > 0) {
+ const table = try jpg.QuantizationTable.read(reader);
+ tables[table.identifier] = table;
+ size -= @truncate(table.size_in_file());
+ }
+
+ break :dqt .{ .quantization_tables = tables };
+ },
+ .start_of_frame => |n| sof: {
+ reader.toss(2);
+ break :sof .{ .frame = try .read(gpa, n, reader) };
+ },
+ .define_huffman_table => dhf: {
+ var size = try reader.takeInt(u16, .big) - 2;
+ var tables: std.ArrayList(jpg.HuffmanTable) = .empty;
+ errdefer tables.deinit(gpa);
+
+ while (size > 0) {
+ const table = try tables.addOne(gpa);
+ table.* = try .read(gpa, reader);
+
+ size -= @truncate(table.size_in_file());
+ }
+
+ break :dhf .{ .huffman_tables = try tables.toOwnedSlice(gpa) };
+ },
+ .start_of_scan => sos: {
+ reader.toss(2);
+ break :sos .{ .scan = try .read(gpa, reader) };
},
else => {
std.debug.panic("unimplemented tag '{s}'\n", .{@tagName(marker)});
},
};
}
+
+ fn read_application_segment(reader: *std.Io.Reader, n: usize) !Self {
+ return switch (n) {
+ 0 => jfif: {
+ reader.toss(2);
+ break :jfif .{
+ .jfif = try .read(reader)
+ };
+ },
+ else => std.debug.panic("unimplemented app{}\n", .{n}),
+ };
+ }
+
+ pub fn deinit(self: *Self, gpa: std.mem.Allocator) void {
+ switch (self.*) {
+ .frame => |*frame| frame.deinit(gpa),
+ .huffman_tables => |tables| {
+ for (tables) |*table| table.deinit(gpa);
+ gpa.free(tables);
+ },
+ .scan => |*s| s.deinit(gpa),
+ else => {},
+ }
+ }
};
-test "file" {
+test "rgb" {
const buffer = @embedFile("../testing/rgb.jpg");
var reader: std.Io.Reader = .fixed(buffer);
- var segment: Segment = try .read(std.testing.allocator, &reader);
- try std.testing.expectEqual(.start_of_image, std.meta.activeTag(segment));
+ {
+ var segment: Segment = try .read(std.testing.allocator, &reader);
+ defer segment.deinit(std.testing.allocator);
+ try std.testing.expectEqual(.start_of_image, std.meta.activeTag(segment));
+ }
+
+ {
+ var segment: Segment = try .read(std.testing.allocator, &reader);
+ defer segment.deinit(std.testing.allocator);
+ try std.testing.expectEqual(.jfif, std.meta.activeTag(segment));
+ }
+
+ {
+ var segment: Segment = try .read(std.testing.allocator, &reader);
+ defer segment.deinit(std.testing.allocator);
+ const table = segment.quantization_tables[0].?;
+ try std.testing.expectEqual(.b8, table.precision);
+ try std.testing.expectEqual(0, table.identifier);
+ }
+
+ {
+ var segment: Segment = try .read(std.testing.allocator, &reader);
+ defer segment.deinit(std.testing.allocator);
+ const table = segment.quantization_tables[1].?;
+ try std.testing.expectEqual(.b8, table.precision);
+ try std.testing.expectEqual(1, table.identifier);
+ }
+
+ {
+ var segment: Segment = try .read(std.testing.allocator, &reader);
+ defer segment.deinit(std.testing.allocator);
+ try std.testing.expectEqual(0, segment.frame.index);
+ try std.testing.expectEqual(8, segment.frame.sample_precision);
+ try std.testing.expectEqual(450, segment.frame.number_of_lines);
+ try std.testing.expectEqual(600, segment.frame.samples_per_line);
+ try std.testing.expectEqual(3, segment.frame.components.len);
+
+ for (segment.frame.components, 0..) |component, index| {
+ try std.testing.expectEqual(index + 1, component.identifier);
+ }
+ }
+
+ for (0..4) |_| {
+ var segment: Segment = try .read(std.testing.allocator, &reader);
+ defer segment.deinit(std.testing.allocator);
+ try std.testing.expectEqual(.huffman_tables, std.meta.activeTag(segment));
+ }
- segment = try .read(std.testing.allocator, &reader);
- try std.testing.expectEqual(.jfif, std.meta.activeTag(segment));
+ {
+ var segment: Segment = try .read(std.testing.allocator, &reader);
+ defer segment.deinit(std.testing.allocator);
+ try std.testing.expectEqual(.scan, std.meta.activeTag(segment));
+ }
- segment = try .read(std.testing.allocator, &reader);
- try std.testing.expectEqual(.jfif, std.meta.activeTag(segment));
+ {
+ var segment: Segment = try .read(std.testing.allocator, &reader);
+ defer segment.deinit(std.testing.allocator);
+ try std.testing.expectEqual(.end_of_image, std.meta.activeTag(segment));
+ }
}