lib/png/src/decode/decoder.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

 1 const std = @import("std");
 2 const flate = std.compress.flate;
 3 const model = @import("model.zig");
 4 const pixels = @import("pixels.zig");
 5 const plan_mod = @import("plan.zig");
 6 const storage_mod = @import("storage.zig");
 7 const stream = @import("stream.zig");
 8 
 9 const Image = model.Image;
10 const Bounds = plan_mod.Bounds;
11 const Plan = plan_mod.Plan;
12 const Storage = storage_mod.Storage;
13 const Status = storage_mod.Status;
14 
15 pub const Error = model.Error || std.mem.Allocator.Error;
16 
17 pub const Decoding = struct {
18     storage: Storage,
19     image: Image,
20 
21     pub fn init(allocator: std.mem.Allocator, bytes: []const u8) Error!Decoding {
22         return initWithBounds(allocator, bytes, try Bounds.exact(bytes));
23     }
24 
25     pub fn initWithBounds(
26         allocator: std.mem.Allocator,
27         bytes: []const u8,
28         bounds: Bounds,
29     ) Error!Decoding {
30         var storage = try Storage.init(allocator, .{ .bytes = bytes, .bounds = bounds });
31         errdefer storage.deinit(allocator);
32         storage.activate();
33         return .{ .image = try imageFromBytes(&storage, bytes), .storage = storage };
34     }
35 
36     pub fn status(self: *const Decoding) Status {
37         return self.storage.status();
38     }
39 
40     pub fn deinit(self: *Decoding, allocator: std.mem.Allocator) void {
41         self.storage.reset();
42         self.storage.deinit(allocator);
43         self.* = undefined;
44     }
45 };
46 
47 pub fn imageFromBytes(storage: *Storage, bytes: []const u8) model.Error!Image {
48     var regions = try storage.acquire(bytes);
49     errdefer storage.reset();
50     const plan = storage.plan();
51     try decodeRows(plan, bytes, &regions);
52     return .{ .width = plan.width, .height = plan.height, .rgba8 = regions.output };
53 }
54 
55 fn decodeRows(plan: Plan, bytes: []const u8, regions: *storage_mod.Regions) model.Error!void {
56     @memset(regions.prior, 0);
57     var idat = stream.Source.init(bytes, plan.idat_bytes);
58     idat.attach();
59     var decompress = flate.Decompress.init(&idat.reader, .zlib, regions.window);
60     var prior = regions.prior;
61     var current = regions.current;
62     const row_output_bytes = try model.multiplied(try model.toUsize(plan.width), 4);
63 
64     for (0..try model.toUsize(plan.height)) |y| {
65         const filter = decompress.reader.takeByte() catch return error.TruncatedImage;
66         decompress.reader.readSliceAll(current) catch return error.TruncatedImage;
67         try pixels.unfilterRow(
68             filter,
69             current,
70             if (y == 0) null else prior,
71             plan.filter_bytes_per_pixel,
72         );
73         pixels.expandRow(
74             plan.header(),
75             plan.palette(bytes),
76             plan.transparency(bytes),
77             current,
78             regions.output[y * row_output_bytes ..][0..row_output_bytes],
79         );
80         std.mem.swap([]u8, &prior, &current);
81     }
82 
83     _ = decompress.reader.takeByte() catch |err| switch (err) {
84         error.EndOfStream => return,
85         error.ReadFailed => return error.TruncatedImage,
86     };
87     return error.TruncatedImage;
88 }