-
Notifications
You must be signed in to change notification settings - Fork 122
GIF Writer #301
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GIF Writer #301
Conversation
mlarouche
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks again for doing this! See my comments for the requested changes.
src/compressions/lzw.zig
Outdated
|
|
||
| // Hash table: maps (prefix_code << 8 | byte) -> code | ||
| // Entry format: (key << 12) | code, where key = (prefix << 8 | byte) | ||
| table: [table_size]u32 = [_]u32{invalid_entry} ** table_size, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use table: [table_size]u32 = @splat(invalid_entry), here
| pub const AnimationFrame = struct { | ||
| pixels: color.PixelStorage, | ||
| duration: f32, | ||
| /// Frame disposal method (format-specific, 0 = none/unspecified) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At first I was a bit afraid it is too GIF specific but this looks that it should be common enough for another animated format such as Animated PNG.
src/compressions/lzw.zig
Outdated
|
|
||
| /// Increment next code and handle overflow/reset | ||
| /// Returns true if table was reset (out of codes) | ||
| fn incNextCode(self: *Self, writer: *std.Io.Writer) Error!bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
incNextCode -> incrementNextCode
src/formats/gif.zig
Outdated
| return .rgb24; | ||
| } | ||
|
|
||
| fn writeHeader(writer: *std.Io.Writer, image: Image, pixels: *const color.PixelStorage, loop_count: i32) Image.WriteError!void { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
writeHeader should not dependent of Image
| _ = write_stream; | ||
| _ = image; | ||
| _ = encoder_options; | ||
| const writer = write_stream.writer(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no way to write GIF without using Image. This function should fill out the fields already present in GIF and the write function should use those fields like frames, comments, application_infos, global_color_table. So you can write GIF that have filled those fields manually.
| var converted_pixels: ?color.PixelStorage = null; | ||
| defer if (converted_pixels) |pixels| pixels.deinit(allocator); | ||
|
|
||
| var pixels_to_use = &image.pixels; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The auto conversion only applies to the first image in an animation, is that intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was not intended, maybe leftover behavior from the tiny animated GIFs I tested with.
src/formats/gif.zig
Outdated
| } | ||
| }; | ||
|
|
||
| const paletteEntryCounts = [_]usize{ 2, 4, 8, 16, 32, 64, 128, 256 }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be palette_entry_counts or PALETTE_ENTRY_COUNTS
src/formats/gif.zig
Outdated
|
|
||
| fn writeLoopExtension(writer: *std.Io.Writer, loop_count: u16) Image.WriteError!void { | ||
| try writer.writeAll(&[_]u8{ | ||
| 0x21, // Extension Introducer. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should either pack those into a extern struct for a data block header or use the enums already present in this file.
Example:
const data_block_header: ExtensionDataHeader = .{
.data_kind = .extension,
.extension_kind = .application_extension,
.block_size = 0x0b,Added a few writer helpers. Leverage existing enums.
|
Thanks again @braheezy ! |
Here lies a functional GIF writer. It includes:
falseby default...I think that's right? At least, it's an option.global_paletteto track shared colors across frames, ultimately saving spaceAs I did with the JPEG writer, this is a loose port of the GIF encoder from the Go standard library.
Closes #11
To manually test and see the results, here's a client program that takes in a GIF and writes it back out, the reads it again.
Client program
Example output:
./zig-out/bin/gif_client rotating_earth.gif out.gif Reading GIF from rotating_earth.gif... Read GIF: 400x400, format: indexed8 Palette size: 256 colors Writing GIF to out.gif... GIF successfully written to out.gif Testing round-trip: reading back the GIF... Successfully read back GIF: 400x400, format: indexed8