diff options
Diffstat (limited to 'src/jpg/segment.zig')
| -rw-r--r-- | src/jpg/segment.zig | 149 |
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)); + } } |