require 'rubygems' require 'optparse' require 'rexml/document' require 'RMagick' require "#{File.dirname(__FILE__)}/rect_packer" include Magick
module REXML class Element def tag(key, attr = {}, &block) child = REXML::Element.new(key) child.add_attributes(attr) if block result = child.instance_eval(&block) if String === result child.add_text(result) end end add_element(child) return child end end end
class RMagickPacker def initialize @image_base_path = nil end
def set_image_base_path(path) @image_base_path = path end
def pack(files, margin, list_file_name, output_format) image_fn_map = {} images = files.map do |fn| path = fn if @image_base_path path = "#{@image_base_path}/#{fn}" end img = Magick::ImageList.new(path) image_fn_map[img] = fn img end
image_rects = images.map do |img| rect = RectPacker::Rect.new(0, 0, img.columns, img.rows) rect.object = img rect end
packer = RectPacker.new size = packer.pack(image_rects, margin) exit(1) unless size
w, h = size image_file_name = "#{list_file_name}.#{output_format}" output_image(image_file_name, w, h, image_rects) output_plist("#{list_file_name}.plist", image_file_name, w, h, image_rects, image_fn_map) end
def output_image(file_name, w, h, image_rects) packed_image = Image.new(w, h) { self.background_color = 'none' } image_rects.each do |image_rect| img = image_rect.object packed_image.composite!(img, image_rect.x, image_rect.y, CopyCompositeOp) end packed_image.write(file_name) end
def output_plist(file_name, image_file_name, w, h, image_rects, image_fn_map) doc = REXML::Document.new doc.add(REXML::XMLDecl.new(version='1.0', encoding='UTF-8')) doc.add(REXML::DocType.new('plist', 'PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"')) doc.tag('plist', {'version' => '1.0'}) do tag('dict') do tag('key') { 'frames' } tag('dict') do image_rects.each do |image_rect| img = image_rect.object x = image_rect.x y = image_rect.y w = image_rect.w h = image_rect.h tag('key') { image_fn_map[img] } tag('dict') do tag('key') { 'spriteColorRect' } tag('string') { "{{"{{"}}0, 0}, {#{w}, #{h}}}" } tag('key') { 'spriteOffset' } tag('string') { "{0, 0}" } tag('key') { 'spriteSize' } tag('string') { "{#{w}, #{h}}" } tag('key') { 'spriteSourceSize' } tag('string') { "{#{w}, #{h}}" } tag('key') { 'spriteTrimmed' } tag('false') tag('key') { 'textureRect' } tag('string') { "{{"{{"}}#{x}, #{y}}, {#{w}, #{h}}}" } tag('key') { 'textureRotated' } tag('false') end end end tag('key') { 'metadata' } tag('dict') do tag('key') { 'version' } tag('string') { '0.1' } tag('key') { 'format' } tag('integer') { '3' } tag('key') { 'size' } tag('string') { "{#{w}, #{h}}" } tag('key') { 'name' } tag('string') { image_file_name } tag('key') { 'textureFileName' } tag('string') { image_file_name } tag('key') { 'premultipliedAlpha' } tag('false') end end end
formatter = REXML::Formatters::Pretty.new(2) formatter.compact = true output = '' formatter.write(doc, output) open(file_name, 'w') do |out| out.print(output.gsub("'", '"')) end end end
def main list_file_name = 'images' output_format = 'png' image_base_path = nil margin = 0
opt = OptionParser.new opt.on('-o filename') {|v| list_file_name = v} opt.on('-f format') {|v| output_format = v} opt.on('-i image-base-path') {|v| image_base_path = v} opt.on('-m margin') {|v| margin = v.to_i} opt.parse!(ARGV)
files = [] while gets files.push($_.chomp) end
packer = RMagickPacker.new if image_base_path packer.set_image_base_path(image_base_path) end packer.pack(files, margin, list_file_name, output_format) end
if $0 == __FILE__ main end
|