#!/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]