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, ®ions);
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, ¤t);
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 }