lib/gif/src/decode/stream.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const model = @import("model.zig");
  3 
  4 pub const Error = model.DecodeError || error{CompressedByteCapacityExceeded};
  5 
  6 pub const Reader = struct {
  7     bytes: []const u8,
  8     offset: usize = 0,
  9 
 10     pub fn take(self: *Reader, count: usize) model.DecodeError![]const u8 {
 11         if (count > self.bytes.len -| self.offset) return error.TruncatedInput;
 12         const slice = self.bytes[self.offset..][0..count];
 13         self.offset += count;
 14         return slice;
 15     }
 16 
 17     pub fn byte(self: *Reader) model.DecodeError!u8 {
 18         return (try self.take(1))[0];
 19     }
 20 
 21     pub fn word(self: *Reader) model.DecodeError!u16 {
 22         const slice = try self.take(2);
 23         return std.mem.readInt(u16, slice[0..2], .little);
 24     }
 25 };
 26 
 27 pub const Header = struct {
 28     width: u32,
 29     height: u32,
 30     global_table: []const u8,
 31 };
 32 
 33 pub fn readHeader(reader: *Reader) model.DecodeError!Header {
 34     const header = try reader.take(6);
 35     if (!std.mem.eql(u8, header[0..4], &model.signature)) return error.InvalidSignature;
 36     if (!std.mem.eql(u8, header[4..6], "7a") and !std.mem.eql(u8, header[4..6], "9a")) {
 37         return error.InvalidSignature;
 38     }
 39 
 40     const width: u32 = try reader.word();
 41     const height: u32 = try reader.word();
 42     const flags = try reader.byte();
 43     _ = try reader.byte();
 44     _ = try reader.byte();
 45     if (width == 0 or height == 0) return error.InvalidDescriptor;
 46 
 47     var global_table: []const u8 = &.{};
 48     if (flags & 0x80 != 0) {
 49         const entries = @as(usize, 2) << @intCast(flags & 0x07);
 50         global_table = try reader.take(entries * 3);
 51     }
 52     return .{ .width = width, .height = height, .global_table = global_table };
 53 }
 54 
 55 pub const Descriptor = struct {
 56     left: u32,
 57     top: u32,
 58     width: u32,
 59     height: u32,
 60     interlaced: bool,
 61     table: []const u8,
 62     min_code_size: u8,
 63 
 64     pub fn pixelCount(self: Descriptor) usize {
 65         return @as(usize, self.width) * @as(usize, self.height);
 66     }
 67 };
 68 
 69 pub fn readDescriptor(reader: *Reader, global_table: []const u8) model.DecodeError!Descriptor {
 70     const left: u32 = try reader.word();
 71     const top: u32 = try reader.word();
 72     const width: u32 = try reader.word();
 73     const height: u32 = try reader.word();
 74     const flags = try reader.byte();
 75     if (width == 0 or height == 0) return error.InvalidDescriptor;
 76 
 77     var table = global_table;
 78     if (flags & 0x80 != 0) {
 79         const entries = @as(usize, 2) << @intCast(flags & 0x07);
 80         table = try reader.take(entries * 3);
 81     }
 82     if (table.len == 0) return error.MissingColorTable;
 83 
 84     return .{
 85         .left = left,
 86         .top = top,
 87         .width = width,
 88         .height = height,
 89         .interlaced = flags & 0x40 != 0,
 90         .table = table,
 91         .min_code_size = try reader.byte(),
 92     };
 93 }
 94 
 95 pub fn skipSubBlocks(reader: *Reader) model.DecodeError!void {
 96     while (true) {
 97         const size = try reader.byte();
 98         if (size == 0) return;
 99         _ = try reader.take(size);
100     }
101 }
102 
103 pub fn readSubBlocks(reader: *Reader, destination: ?[]u8, limit: usize) Error!usize {
104     var used: usize = 0;
105     while (true) {
106         const size = try reader.byte();
107         if (size == 0) return used;
108         const data = try reader.take(size);
109         if (data.len > limit -| used) return error.CompressedByteCapacityExceeded;
110         if (destination) |bytes| {
111             if (data.len > bytes.len -| used) return error.CompressedByteCapacityExceeded;
112             @memcpy(bytes[used..][0..data.len], data);
113         }
114         used += data.len;
115     }
116 }
117 
118 pub fn readControl(reader: *Reader) model.DecodeError!model.Control {
119     if (try reader.byte() != 4) return error.InvalidDescriptor;
120     const data = try reader.take(4);
121     if (try reader.byte() != 0) return error.InvalidDescriptor;
122     const flags = data[0];
123     return .{
124         .disposal = @fromBackingInt(@intCast(@as(u3, @intCast((flags >> 2) & 0x07)))),
125         .delay_cs = std.mem.readInt(u16, data[1..3], .little),
126         .transparent_index = if (flags & 0x01 != 0) data[3] else null,
127     };
128 }