Created
January 15, 2013 05:39
-
-
Save sczizzo/4536468 to your computer and use it in GitHub Desktop.
Convert a gzipped LBM file into a GIF animation (not totally working...)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# | |
# Offset | Length | Description | |
# ======================================== | |
# 0 | 1 | minor width offset | |
# 1 | 1 | major width offset | |
# 2 | 1 | minor height offset | |
# 3 | 1 | major height offset | |
# 4 | 768 | 256-color pallet | |
# 772 | 1 | number of cycles (N) | |
# 773 | 6N | cycle information | |
# 773+6N | W*H | image information | |
# | |
require 'zlib' | |
require 'rmagick' | |
include Magick | |
module CycleImage | |
# Load raw drawing information | |
def self.load path | |
drawing = { | |
width: 0, | |
height: 0, | |
cycles: [], | |
pallet: [], | |
data: [], | |
colors: [], | |
pixels: [] | |
} | |
File.open(path, 'rb') do |f| | |
img = Zlib::GzipReader.new(f) | |
drawing[:width] = img.getbyte + 256 * img.getbyte | |
drawing[:height] = img.getbyte + 256 * img.getbyte | |
256.times do | |
drawing[:pallet] << { | |
red: img.getbyte, | |
green: img.getbyte, | |
blue: img.getbyte | |
} | |
end | |
img.getbyte.times do | |
drawing[:cycles] << { | |
reversed: img.getbyte + 256 * img.getbyte, | |
rate: img.getbyte + 256 * img.getbyte, | |
low: img.getbyte, | |
high: img.getbyte | |
} | |
end | |
(drawing[:width] * drawing[:height]).times do | |
drawing[:data] << img.getbyte | |
end | |
# Identify pixels that can change | |
drawing[:colors] = Array.new(256, false) | |
drawing[:cycles].each do |cycle| | |
next if cycle[:rate] == 0 | |
cycle[:low].upto(cycle[:high]) do |j| | |
drawing[:colors][j] = true | |
end | |
end | |
n = 0 | |
(drawing[:width] * drawing[:height]).times do |i| | |
pidx = drawing[:data][i] | |
if drawing[:colors][pidx] | |
drawing[:pixels][n] = i | |
n += 1 | |
end | |
end | |
end | |
return drawing | |
end | |
# Create an image from a drawing | |
def self.from drawing | |
img = Image.new(drawing[:width], drawing[:height]) do | |
self.depth = 8 | |
end | |
# Recreate the drawing pixel-by-pixel | |
drawing[:height].times do |y| | |
row = [] | |
drawing[:width].times do |x| | |
# Look up the pixel color in our pallet | |
off = y * drawing[:width] + x | |
pidx = drawing[:data][off] | |
color = drawing[:pallet][pidx] | |
# Convert the 8-bit colors to 16-bit | |
red = 257 * color[:red] | |
green = 257 * color[:green] | |
blue = 257 * color[:blue] | |
row << Pixel.new(red, green, blue, QuantumRange) | |
# img.store_pixels(x, y, 1, 1, []) | |
end | |
img.store_pixels(0, y, drawing[:width], 1, row) | |
end | |
return img | |
end | |
# Create a new image after cycling over time T | |
def self.update drawing, t | |
new_pallet = Marshal.load(Marshal.dump(drawing[:pallet])) | |
drawing[:cycles].each do |cycle| | |
low, high, rate = cycle[:low], cycle[:high], cycle[:rate] | |
next if rate == 0 | |
amt = t * rate | |
range = high - low + 1 | |
amount = amt.floor % range | |
amt -= amt.floor | |
range.times do |j| | |
c1 = drawing[:pallet][(low + j + 1) % range] | |
c2 = drawing[:pallet][low + j] | |
# Lerping by hand... | |
r = amt * c1[:red] + (1 - amt) * c2[:red] | |
g = amt * c1[:green] + (1 - amt) * c2[:green] | |
b = amt * c1[:blue] + (1 - amt) * c2[:blue] | |
idx = cycle[:low] + (j + amount) % range | |
new_pallet[idx] = { :red => r.floor, :green => g.floor, :blue => b.floor } | |
end | |
end | |
drawing[:pallet] = new_pallet | |
return from(drawing) | |
end | |
end | |
# Create frames and animate in a GIF | |
exit(-1) if ARGV.length != 1 | |
`rm -rf frames && mkdir frames` | |
drawing = CycleImage.load ARGV.first | |
t, n, dt, delay = 0, 10, 0.0001, 25 | |
n.times do |i| | |
CycleImage.update(drawing, t).write("frames/frame-#{i}.jpg") | |
t += dt | |
end | |
anim = ImageList.new(*Dir["frames/*.jpg"]) { self.delay = delay } | |
anim.write("out.gif") | |
`rm -rf frames` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment