lib/gif/src/decode/plan.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const model = @import("model.zig");
  3 const stream = @import("stream.zig");
  4 
  5 pub const Bounds = struct {
  6     canvas_pixels: usize,
  7     frame_pixels: usize,
  8     frames: usize,
  9     compressed_bytes: usize,
 10     retained_rgba8_bytes: usize,
 11 };
 12 
 13 pub const Error = model.DecodeError || model.Exhaustion || error{CapacityOverflow};
 14 
 15 pub const Plan = struct {
 16     input_bytes: usize,
 17     input_hash: u64,
 18     width: u32,
 19     height: u32,
 20     canvas_pixels: usize,
 21     canvas_rgba8_bytes: usize,
 22     frames: usize,
 23     frame_pixels: usize,
 24     compressed_bytes: usize,
 25     retained_rgba8_bytes: usize,
 26 
 27     pub fn inspect(bytes: []const u8, bounds: Bounds) Error!Plan {
 28         var reader = stream.Reader{ .bytes = bytes };
 29         const header = try stream.readHeader(&reader);
 30         const canvas_pixels = try multiplied(header.width, header.height);
 31         if (canvas_pixels > bounds.canvas_pixels) return error.CanvasPixelCapacityExceeded;
 32         const canvas_rgba8_bytes = try multiplied(canvas_pixels, 4);
 33 
 34         var frame_count: usize = 0;
 35         var frame_pixels: usize = 0;
 36         var compressed_bytes: usize = 0;
 37         var retained_rgba8_bytes: usize = 0;
 38         var trailer = false;
 39 
 40         while (!trailer) {
 41             switch (try reader.byte()) {
 42                 0x21 => {
 43                     const label = try reader.byte();
 44                     if (label == 0xf9) {
 45                         _ = try stream.readControl(&reader);
 46                     } else {
 47                         try stream.skipSubBlocks(&reader);
 48                     }
 49                 },
 50                 0x2c => {
 51                     const descriptor = try stream.readDescriptor(&reader, header.global_table);
 52                     const descriptor_pixels = descriptor.pixelCount();
 53                     if (descriptor_pixels > bounds.frame_pixels) return error.FramePixelCapacityExceeded;
 54                     frame_pixels = @max(frame_pixels, descriptor_pixels);
 55 
 56                     frame_count = std.math.add(usize, frame_count, 1) catch return error.CapacityOverflow;
 57                     if (frame_count > bounds.frames) return error.FrameCapacityExceeded;
 58 
 59                     const compressed = try stream.readSubBlocks(&reader, null, bounds.compressed_bytes);
 60                     compressed_bytes = @max(compressed_bytes, compressed);
 61 
 62                     retained_rgba8_bytes = std.math.add(
 63                         usize,
 64                         retained_rgba8_bytes,
 65                         canvas_rgba8_bytes,
 66                     ) catch return error.CapacityOverflow;
 67                     if (retained_rgba8_bytes > bounds.retained_rgba8_bytes) {
 68                         return error.RetainedRgba8CapacityExceeded;
 69                     }
 70                 },
 71                 0x3b => trailer = true,
 72                 else => return error.InvalidDescriptor,
 73             }
 74         }
 75 
 76         if (frame_count == 0) return error.NoFrames;
 77         return .{
 78             .input_bytes = bytes.len,
 79             .input_hash = std.hash.Wyhash.hash(0, bytes),
 80             .width = header.width,
 81             .height = header.height,
 82             .canvas_pixels = canvas_pixels,
 83             .canvas_rgba8_bytes = canvas_rgba8_bytes,
 84             .frames = frame_count,
 85             .frame_pixels = frame_pixels,
 86             .compressed_bytes = compressed_bytes,
 87             .retained_rgba8_bytes = retained_rgba8_bytes,
 88         };
 89     }
 90 
 91     pub fn exactBounds(self: Plan) Bounds {
 92         return .{
 93             .canvas_pixels = self.canvas_pixels,
 94             .frame_pixels = self.frame_pixels,
 95             .frames = self.frames,
 96             .compressed_bytes = self.compressed_bytes,
 97             .retained_rgba8_bytes = self.retained_rgba8_bytes,
 98         };
 99     }
100 };
101 
102 fn multiplied(left: anytype, right: anytype) error{CapacityOverflow}!usize {
103     return std.math.mul(usize, @intCast(left), @intCast(right)) catch error.CapacityOverflow;
104 }