Class: Flipbook::GIF::Writer
- Inherits:
-
Object
- Object
- Flipbook::GIF::Writer
- Defined in:
- lib/flipbook/gif/writer.rb,
sig/flipbook.rbs
Class Method Summary collapse
Instance Method Summary collapse
- #add(image, delay:) ⇒ Writer
- #close ⇒ Writer
-
#initialize(io, width:, height:, loop: true, palette: :per_frame, colors: 256, dither: :floyd_steinberg, transparent_index: nil, optimize: true) ⇒ Writer
constructor
A new instance of Writer.
Constructor Details
#initialize(io, width:, height:, loop: true, palette: :per_frame, colors: 256, dither: :floyd_steinberg, transparent_index: nil, optimize: true) ⇒ Writer
Returns a new instance of Writer.
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/flipbook/gif/writer.rb', line 10 def initialize(io, width:, height:, loop: true, palette: :per_frame, colors: 256, dither: :floyd_steinberg, transparent_index: nil, optimize: true) @io = io @width = Integer(width) @height = Integer(height) raise ArgumentError, "GIF dimensions must be between 1 and 65535" unless @width.between?(1, 65_535) && @height.between?(1, 65_535) raise ArgumentError, "colors must be between 2 and 256" unless colors.is_a?(Integer) && colors.between?(2, 256) raise ArgumentError, "unknown dither: #{dither}" unless %i[none ordered floyd_steinberg].include?(dither) raise TypeError, "optimize must be true or false" unless [true, false].include?(optimize) @loop = loop_value(loop) @colors = colors @dither = dither @optimize = optimize @previous_image = nil @elapsed = Rational(0) @rounded_delay = 0 @closed = false @warned_short_delay = false @frame_count = 0 @pending_frame = nil @canvas_cleared = false @palette_mode = palette if palette == :per_frame raise ArgumentError, "transparent_index is only valid with a global palette" unless transparent_index.nil? @global_palette = nil @global_transparent_index = 0 else raise TypeError, "palette must be :per_frame or an RGB array" unless palette.is_a?(Array) @global_palette = validate_palette(palette) if transparent_index raise ArgumentError, "transparent_index must be the last palette entry" unless transparent_index == @global_palette.length - 1 raise ArgumentError, "transparent_index is out of range" unless transparent_index.between?(0, @global_palette.length - 1) raise ArgumentError, "global palette must include opaque and transparent colors" if @global_palette.length < 2 end @global_transparent_index = transparent_index end write_header end |
Class Method Details
Instance Method Details
#add(image, delay:) ⇒ Writer
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/flipbook/gif/writer.rb', line 49 def add(image, delay:) raise IOError, "GIF writer is closed" if @closed raise TypeError, "frame must be a Tessel::Image" unless image.is_a?(Tessel::Image) raise ArgumentError, "frame dimensions must match the logical screen" unless image.width == @width && image.height == @height seconds = Timing.duration(delay) if seconds < Rational(1, 50) && !@warned_short_delay warn("Flipbook: delays below 1/50 second may be clamped by GIF viewers") @warned_short_delay = true end transparent_pixels = image.bytes.bytes.each_slice(4).any? { |_, _, _, alpha| alpha.zero? } if @pending_frame disposal = (@pending_frame[:transparent] || transparent_pixels) ? 2 : 1 write_pending(disposal) @canvas_cleared = disposal == 2 end elapsed = @elapsed + seconds target_delay = (elapsed * 100).floor frame_delay = target_delay - @rounded_delay raise ArgumentError, "GIF frame delays cannot exceed 655.35 seconds" if frame_delay > 65_535 rectangle = if @optimize && @previous_image && !transparent_pixels && !@canvas_cleared Diff.bounds(@previous_image, image) || [0, 0, 1, 1] else [0, 0, @width, @height] end @pending_frame = { image: image.dup, delay: frame_delay, rectangle:, transparent: transparent_pixels } @elapsed = elapsed @rounded_delay = target_delay @frame_count += 1 @previous_image = image.dup self end |
#close ⇒ Writer
83 84 85 86 87 88 89 90 91 92 |
# File 'lib/flipbook/gif/writer.rb', line 83 def close return if @closed raise Error, "GIF contains no frames" if @frame_count.zero? write_pending(@pending_frame[:transparent] ? 2 : 1) @io << ";".b @closed = true Output.finish(@io, @target_path, @target_mode) if @owns_io self end |