const std = @import("std"); const util = @import("util.zig"); const threads = @import("threads.zig"); const output = @import("output.zig"); // helper func, computes the appropriate coords for luminance // [macro_block][macro_block][intra_macro_block] inline fn lum_idxs(i: usize, j: usize) struct { usize, usize, usize } { return .{ i / 2, j / 2, 2 * (i % 2) + (j % 2), }; } // either 2 separate funcs (see read_chrom) or multiple if statements, will be bad anyway // incrementally copies 8 byte sequences from the input buffer to the appropriate coords in the Y buffer // sends any completed blocks off to be processed for quantization as they fill fn read_lum(f: std.fs.File, source_buff: [][][4]util.Block, target_buff: [][][4]util.BlockQuantized, io_buff: []u8, queue: *util.JobQueue) !void { const block_h = source_buff.len; const block_w = source_buff[0].len; for (0..block_h * 2) |i| { if (io_buff.len != try f.readAll(io_buff)) return util.Errors.EOFError; var io_idx: usize = 0; for (0..8) |I| { for (0..block_w * 2) |j| { const idxs = lum_idxs(i, j); @memcpy(source_buff[idxs.@"0"][idxs.@"1"][idxs.@"2"][I * 8 .. (I + 1) * 8], io_buff[io_idx .. io_idx + 8]); io_idx += 8; if (I == 7) { try queue.prepend(util.Job{ .source = &source_buff[idxs.@"0"][idxs.@"1"][idxs.@"2"], .target = &target_buff[idxs.@"0"][idxs.@"1"][idxs.@"2"], .is_lum = true, }); } } } } } // either 2 separate funcs (see read_lum) or multiple if statements, will be bad anyway // incrementally copies 8 byte sequences from the input buffer to the appropriate coords in the U or V buffer // sends any completed blocks off to be processed for quantization as they fill fn read_chrom(f: std.fs.File, source_buff: [][]util.Block, target_buff: [][]util.BlockQuantized, io_buff: []u8, queue: *util.JobQueue) !void { const block_h = source_buff.len; const block_w = source_buff[0].len; for (0..block_h) |i| { if (io_buff.len != try f.readAll(io_buff)) return util.Errors.EOFError; var io_idx: usize = 0; for (0..8) |I| { for (0..block_w) |j| { @memcpy(source_buff[i][j][I * 8 .. (I + 1) * 8], io_buff[io_idx .. io_idx + 8]); io_idx += 8; if (I == 7) { try queue.prepend(util.Job{ .source = &source_buff[i][j], .target = &target_buff[i][j], .is_lum = false, }); } } } } } pub fn main_loop(f: std.fs.File, buffs: util.Buffers, thread_mgr: *threads.ThreadManager, alloc: std.mem.Allocator) !void { defer thread_mgr.quit(); while (true) { // resets control atomic variables, resumes quantizers thread_mgr.unblock(); try read_lum(f, buffs.Y, buffs.Y_quant, buffs.input_buff, thread_mgr.queue_wrp.queue); // U and V are downsampled, half len buffers try read_chrom(f, buffs.U, buffs.U_quant, buffs.input_buff[0 .. buffs.input_buff.len / 2], thread_mgr.queue_wrp.queue); try read_chrom(f, buffs.V, buffs.V_quant, buffs.input_buff[0 .. buffs.input_buff.len / 2], thread_mgr.queue_wrp.queue); // wait until all blocks have been processed while (thread_mgr.signals.processed.load(.Acquire) != buffs.num_blocks) : (std.time.sleep(1)) {} // sets the eof signal atomic variable, prevents quantizers from checking jobs when none can generate thread_mgr.eof(); try output.generate_jpg(buffs, alloc); } }