@@ -39,3 +39,74 @@ pub fn write_bmp(self: @This(), writer: *std.Io.Writer) !void {
3939
4040 try writer .flush ();
4141}
42+
43+ const PNGChunkWriter = struct {
44+ writer : * std.Io.Writer ,
45+ crc : std.hash.Crc32 = .init (),
46+
47+ pub fn init (writer : * std.Io.Writer , data_length : u32 , chunk_type : []const u8 ) ! @This () {
48+ try writer .writeInt (u32 , data_length , .big );
49+ var r = @This (){ .writer = writer };
50+ std .debug .assert (chunk_type .len == 4 );
51+ try r .write (chunk_type );
52+ return r ;
53+ }
54+
55+ pub fn writeInt (self : * @This (), val : u32 ) ! void {
56+ return self .write (std .mem .asBytes (&@byteSwap (val )));
57+ }
58+
59+ pub fn write (self : * @This (), data : []const u8 ) ! void {
60+ try self .writer .writeAll (data );
61+ self .crc .update (data );
62+ }
63+
64+ pub fn finish (self : * @This ()) ! void {
65+ try self .writer .writeInt (u32 , self .crc .final (), .big );
66+ }
67+ };
68+
69+ pub fn write_png (self : @This (), allocator : std .mem .Allocator , writer : * std .Io .Writer ) ! void {
70+ var alloc_writer = try std .Io .Writer .Allocating .initCapacity (allocator , self .bgra .len );
71+ defer alloc_writer .deinit ();
72+ var compress_buffer : [std .compress .flate .max_window_len ]u8 = undefined ;
73+ var compress = try std .compress .flate .Compress .init (& alloc_writer .writer , & compress_buffer , .zlib , .default );
74+ for (0.. self .height ) | y | {
75+ try compress .writer .writeByte (0 ); // Filter Type
76+ for (0.. self .width ) | x | {
77+ const bgra = self .bgra [y * self .width * 4 + 4 * x .. ][0.. 4];
78+ try compress .writer .writeAll (&[_ ]u8 { bgra [2 ], bgra [1 ], bgra [0 ] });
79+ }
80+ }
81+ try compress .finish ();
82+
83+ const compressed = try alloc_writer .toOwnedSlice ();
84+ defer allocator .free (compressed );
85+
86+ // PNG Signature
87+ try writer .writeAll (&[_ ]u8 { 0x89 , 0x50 , 0x4E , 0x47 , 0x0D , 0x0A , 0x1A , 0x0A });
88+ {
89+ var w = try PNGChunkWriter .init (writer , 13 , "IHDR" );
90+ try w .writeInt (self .width );
91+ try w .writeInt (self .height );
92+ try w .write (&[_ ]u8 {
93+ 8 , // Bit-depth
94+ 2 , // Color type (2: Truecolour)
95+ 0 , // Compression Method
96+ 0 , // Filter Method
97+ 0 , // Interlace Method
98+ });
99+ try w .finish ();
100+ }
101+ {
102+ var w = try PNGChunkWriter .init (writer , @intCast (compressed .len ), "IDAT" );
103+ try w .write (compressed );
104+ try w .finish ();
105+ }
106+ {
107+ var w = try PNGChunkWriter .init (writer , 0 , "IEND" );
108+ try w .finish ();
109+ }
110+
111+ try writer .flush ();
112+ }
0 commit comments