Skip to content

Instantly share code, notes, and snippets.

@oberhamsi
Created October 1, 2010 12:46
Show Gist options
  • Save oberhamsi/606161 to your computer and use it in GitHub Desktop.
Save oberhamsi/606161 to your computer and use it in GitHub Desktop.
/**
* @fileOverview
*
* Montage single sprite images from Reiner's Tileset into a single spritemap.
*
* Every Sprite has multiple images: one per animation (e.g., walk, shoot, etc).
* Each row of the spritemap corresponds to a direction (n,s,w) in alphabetical order.
* The columns are the steps of a particular animation for a direction.
*
* requires ImageMagick
*/
var {list, join, write, list, isFile, isDirectory} = require('fs');
var {system, command} = require('ringo/subprocess');
var sys = require('system');
var strings = require('ringo/utils/strings');
var arrays = require('ringo/utils/arrays');
var $f = require('ringo/utils/strings').format;
//var {Parser} = require('ringo/args');
var {get} = require('ringo/httpclient');
// "tipping over sw0011.png"
// "stopped n0001.png"
var FILE_REGEX = /(.*) ([^0-9]{1,2})([^\.]+)\.png/;
var RAW_FILE_REGEX = /(.*) ([^0-9]{1,2})([^\.]+)\.bmp/;
/**
* Montage animations as described by output of `getAnimationInfo()` in
* directory `dir` into a single spritemap.
*/
function montage(dir, animations) {
for each (var animation in animations) {
if (isNaN(animation.count)) {
print ('invalid animation count ', animation.toSource());
continue;
}
var tilingArgs = ['-background', 'none',
'-geometry', '+0+0',
'-tile', (animation.count + 1) + 'x',
animation.name + '.png'
];
var allArgs = ["montage"].concat(animation.files).concat(tilingArgs).concat({dir: dir});
system.apply(this, allArgs);
}
return;
};
/**
* Returns #rgb string for top left pixel in image
*/
function getBackgroundColor(file) {
var args = ['convert', file, '-crop', '1x1+0+0','-unique-colors', 'txt:-'];
var output = command.apply(this, args);
var matches = output.match('(#[^ ]+)')
return matches[1];
};
/**
* @returns {Object} an object describing the animations defined by the image files.
*/
function getAnimationInfo(dir) {
var files = list(dir);
var animations = {};
files.forEach(function(file) {
var parts = file.match(FILE_REGEX);
if (!parts) return;
var animation = parts[1];
var direction = parts[2];
var number = parseInt(parts[3], 10);
var cani = animations[animation];
if (cani === undefined) {
cani = animations[animation] = {
name: animation,
count: number,
directions: [direction],
files: []
}
}
cani.files.push(file);
if (!arrays.contains(cani.directions, direction)) {
cani.directions.push(direction);
}
if (number > cani.count) {
cani.count = number;
}
});
for each (var animation in animations) {
animation.files.sort();
};
return animations;
};
/**
* download url to file
*/
function download(url, file) {
var response = get(url);
write(file, response.contentBytes, 'wb');
return;
};
/**
* unpack file to dir
*/
function unpack(file, dir) {
command.apply(this,
['unzip','-n', file, '-d', dir]
);
};
/**
* recursively fix file encoding to utf8 in diretory
*/
function fixFileEncoding(dir) {
var args = ['convmv', '-r', '--notest', '-f', 'latin1', '-t', 'utf8', './', {dir: dir}];
command.apply(this, args);
return;
};
/**
* convert bmp found in directory to png with background being transparent
*/
function toTransparentPng(dir) {
var files = list(dir);
var color = getBackgroundColor(join(dir, files[1]));
var args = ['mogrify', '-transparent', color, '-format', 'png', '*bmp', {dir: dir}];
system.apply(this, args);
}
/**
* get directories below given directory or directory itself, which hold animation pictures
*/
function getAnimationDirectories(root) {
var directories = list(root);
var animationDirectories = [];
if (directories.length && isFile(join(root, directories[1]))) {
animationDirectories = [root];
} else {
directories.forEach(function(directory) {
if (!isDirectory(directory)) return;
// try first file in directory
var potentialFiles = list(join(root, directory));
var isAnimation = potentialFiles.some(function(file) {
return file.match(RAW_FILE_REGEX) !== null;
});
if (isAnimation) {
animationDirectories.push(join(root, directory));
}
});
}
return animationDirectories;
};
function main(args) {
/**
* Print script help
*/
function help() {
print('Create a spritemap from Reiners Tileset');
print('Usage:');
print(' ringo ' + script + ' download url');
print('Options:');
print (parser.help());
return;
};
var script = args.shift();
// option: --idle stopped --noloop dying ...
// provide good defaults and search for them in file
// var parser = new Parser();
// parser.addOption('s', 'source', 'repository', 'Path to repository');
// var opts = parser.parse(args);
// FIXME rename stopped and other broken?
var zipUrl = args.shift();
var zipFile = join(module.directory, zipUrl.split('/').slice(-1)[0]);
print ('downloading');
download(zipUrl, zipFile);
var zipDir = join(zipFile.split('.').slice(0,-1), '/');
print ('unpacking');
unpack(zipFile, zipDir);
fixFileEncoding(zipDir);
print ('to transparent png');
var animationDirectories = getAnimationDirectories(zipDir);
animationDirectories.forEach(function(ad) {
toTransparentPng(ad);
});
print ('composing spritemap');
animationDirectories.forEach(function(animationDirectory) {
var animations = getAnimationInfo(animationDirectory);
montage(animationDirectory, animations);
});
// drop T_ prefix, replace %20, drop file ext
var fileName = zipFile.split('.').slice(-1)[0];
var spriteName = fileName.split('.')[0].replace('%20', '_').substr(2);
print (spriteName);
var targetDir = join(module.directory, 'animations', spriteName)
print (targetDir);
return;
};
if (require.main == module.id) {
try {
main(sys.args);
} catch (error) {
print(error);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment