const std = @import("std"); const Format = @import("Format.zig"); const format: Format = .{ .magic_length = magic.len, // NOTE The information (like width and height) is not in a fixed position .info_length = magic.len, .extension = "jpeg", .media_type = "image/jpeg", .isFormat = isJpeg, }; const magic = "\xFF\xD8\xFF"; const Info = union(enum) { partial: void, full: Header, pub fn makeFull(full: Header) Info { return .{ .full = full }; } test "refAllDecls" { std.testing.refAllDecls(@This()); } }; const Header = struct { width: u32, height: u32, }; const Marker = enum(u8) { /// Start of frame, Huffman coding, Baseline DCT SOF_0 = 0xC0, /// Start of frame, Huffman coding, Extended sequential DCT SOF_1 = 0xC1, /// Start of frame, Huffman coding, Progressive DCT SOF_2 = 0xC2, /// Start of frame, Huffman coding, Lossless (sequential) SOF_3 = 0xC3, /// Start of frame, Huffman coding, Differential sequential DCT SOF_5 = 0xC5, /// Start of frame, Huffman coding, Differential progressive DCT SOF_6 = 0xC6, /// Start of frame, Huffman coding, Differential lossless (sequential) SOF_7 = 0xC7, /// Start of frame, arithmetic coding, Extended sequential DCT SOF_9 = 0xC9, /// Start of frame, arithmetic coding, Progressive DCT SOF_10 = 0xCA, /// Start of frame, arithmetic coding, Lossless (sequential) SOF_11 = 0xCB, /// Start of frame, arithmetic coding, Differential sequential DCT SOF_13 = 0xCD, /// Start of frame, arithmetic coding, Differential progressive DCT SOF_14 = 0xCE, /// Start of frame, arithmetic coding, Differential lossless (sequential) SOF_15 = 0xCF, /// Define Huffman table(s) DHT = 0xC4, /// Define arithmetic coding conditioning(s) DAC = 0xCC, /// Start of image SOI = 0xD8, /// End of image EOI = 0xD9, /// Start of scan SOS = 0xDA, /// Define quantization table(s) DQT = 0xDB, /// Define number of lines DNL = 0xDC, /// Define restart interval DRI = 0xDD, /// Define hierarchical progression DHP = 0xDE, /// Expand reference component(s) EXP = 0xDF, /// Comment COM = 0xFE, /// For temporary private use in arithmetic coding TEM = 0x01, _, /// Restart with modulo 8 count `m` pub fn RST(m: u3) Marker { return @enumFromInt(0xD0 | m); } pub fn isRST(self: Marker) ?u3 { return if (@intFromEnum(self) & 0b1111_1000 == 0xD0) @intCast(@intFromEnum(self) & 0b0000_0111) else null; } /// Reserved for application segments pub fn APP(n: u4) Marker { return @enumFromInt(0xE0 | n); } pub fn isAPP(self: Marker) ?u3 { return if (@intFromEnum(self) & 0b1111_0000 == 0xE0) @intCast(@intFromEnum(self) & 0b0000_1111) else null; } /// A standalone marker has no content and is not followed by segment length /// parameter. pub fn isStandalone(self: Marker) bool { return self.isRST() != null or self == .SOI or self == .EOI or self == .TEM; } test "refAllDecls" { std.testing.refAllDecls(@This()); } }; /// The caller asserts that the buffer is at least `format.magic_length` bytes /// long. pub fn isJpeg(buffer: []const u8) bool { return std.mem.eql(u8, buffer[0..format.magic_length], magic); } /// The caller asserts that the buffer is at least `format.info_length` bytes /// long. The information is not in a fixed position, so you need to provide a /// substantial amount of data, in the order of kilobytes. Depending on the /// amount of metadata, the information might be in the first kilobyte or dozens /// of kilobytes in. /// /// This function returns: /// /// - `null` when the buffer is not a JPEG or it's a malformed JPEG /// - `.partial` when the buffer appears to be a part of a JPEG, but the /// information could not be found within the buffer provided /// - `.full` when the buffer appears to be a part of a JPEG and the information /// was fully contained within the buffer provided pub fn info(buffer: []const u8) ?Info { std.debug.assert(buffer.len >= format.info_length); if (!isJpeg(buffer)) { return null; } @panic("TODO"); } test "refAllDecls" { std.testing.refAllDecls(@This()); }