diff --git a/tools/gif2c.rb b/tools/gif2c.rb new file mode 100644 index 0000000..71e166a --- /dev/null +++ b/tools/gif2c.rb @@ -0,0 +1,138 @@ +#!/usr/bin/env ruby +require 'rubygems' +require 'rmagick' +require 'pp' + +### +# Converts a gif file to c code to enable displaying animations. +# +# The data format is described in animations.h +### + +def compress(data, use_cutoff=true) + cutoff = 3 + escape = 255 + last = data[0] + result = [] + count = 0 + data.each do |x| + if x == last + count += 1 + else + if use_cutoff + if count <= cutoff + count.times { result << last } + else + result << escape << count << last + end + else + result << count << last + end + count = 1 + last = x + end + end + if use_cutoff + if count < cutoff + count.times { result << last } + else + result << escape << count << last + end + else + result << count << last + end + return result +end + +unless ARGV[0] && ARGV[1] + puts "Usage:" + puts "#{$0} " + puts "e.g. '#{$0} mario.gif mario'" + puts + puts "Resulting data format is described in animations.h" + exit 1 +end + +image_file=ARGV[0] +name=ARGV[1] +frames = Magick::ImageList.new(image_file) + +puts "Found #{frames.count} frames." + +print "Getting colors..." +colors = [] +frames.each do |frame| + frame.columns.times do |x| + frame.rows.times do |y| + color = frame.pixel_color(x, y).to_color(Magick::AllCompliance, true, 8, true) + colors << color + end + end +end +colors = colors.uniq + +puts " Found #{colors.count} colors." + +transparent = colors.select{|c| c.end_with? "00"} + +puts "#{transparent.count} color(s) being transparent." + +colors = (["#00000012", "#000000FF"] + (colors - transparent)).uniq +puts "Using #{colors.count} colors." +raise "Number of colors has to be 255 or less!" if colors.count>255 + +puts +puts +puts "uint32_t #{name}_colors[] = {#{colors.map{|c| "0x" + c[1, 6]}.join(", ")}};" + +p_frame = nil +frames_data = [] +frames.each_with_index do |frame, index| + data = [] + if index==0 # first frame + frame.rows.times do |y| + frame.columns.times do |x| + color = frame.pixel_color(x, y).to_color(Magick::AllCompliance, true, 8, true) + if transparent.include? color + data << 0 + else + data << colors.index(color) + end + end + end + else + frame.rows.times do |y| + frame.columns.times do |x| + color = frame.pixel_color(x, y).to_color(Magick::AllCompliance, true, 8, true) + p_color = p_frame.pixel_color(x, y).to_color(Magick::AllCompliance, true, 8, true) + + if color==p_color + data << 0 + elsif transparent.include? color + data << 1 + else + data << colors.index(color) + end + end + end + end + frames_data << data + p_frame = frame +end + +data = frames_data.map{|d| compress(d, true)} + +puts "uint8_t #{name}_data[] = {\n #{data.map{|d| d.join(",")}.join(",\n ")}\n};" +s=0 +puts "uint16_t #{name}_offsets[] = {#{(data.map{|d| t=s; s+=d.count; t} + [s]).join(",")}};" + +puts "AnimationData #{name} = {&#{name}_colors[0], &#{name}_data[0], &#{name}_offsets[0], #{colors.count}, #{frames_data.count}, 16, 16};" +puts +puts +puts "Space usage:" +puts " Colors: %6d bytes." % [s1=colors.count * 3] +puts " Data: %6d bytes." % [s2=data.flatten.count] +puts " Offsets: %6d bytes." % [s3=data.count * 2] +puts " TOTAL: %6d bytes." % [s1+s2+s3] +puts "Original size: %6d bytes." % [s4=File.new(image_file).size] +puts "Difference: %6d bytes." % [s1+s2+s3 - s4]