Skip to content

Instantly share code, notes, and snippets.

@shiwano
Created September 11, 2012 11:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shiwano/3697844 to your computer and use it in GitHub Desktop.
Save shiwano/3697844 to your computer and use it in GitHub Desktop.
Generate a sprite from animation frames
/*
<javascriptresource>
<name>Generate a sprite from animation frames</name>
<category>web</category>
</javascriptresource>
*/
var AnimationSpriteGenerator, console, main, showInputSizeDialog;
console = {
log: function(string) {
return $.writeln(string);
}
};
AnimationSpriteGenerator = (function() {
function AnimationSpriteGenerator(psdDir, width, height) {
this.psdDir = psdDir;
this.psdFiles = new Folder(psdDir).getFiles('*.psd');
this.width = width;
this.height = height;
}
AnimationSpriteGenerator.prototype.revert = function() {
var idRvrt;
idRvrt = charIDToTypeID("Rvrt");
return executeAction(idRvrt, void 0, DialogModes.NO);
};
AnimationSpriteGenerator.prototype.changeAnimationFrame = function(index) {
var desc66, idanimationFrameClass, idnull, idslct, ref52;
idslct = charIDToTypeID("slct");
desc66 = new ActionDescriptor();
idnull = charIDToTypeID("null");
ref52 = new ActionReference();
idanimationFrameClass = stringIDToTypeID("animationFrameClass");
ref52.putIndex(idanimationFrameClass, index);
desc66.putReference(idnull, ref52);
return executeAction(idslct, desc66, DialogModes.NO);
};
AnimationSpriteGenerator.prototype.getAnimationLength = function() {
var count;
count = 1;
while (true) {
try {
this.changeAnimationFrame(count);
} catch (error) {
break;
}
count += 1;
}
return count;
};
AnimationSpriteGenerator.prototype.save = function() {
var file, options;
app.activeDocument = this.spriteDocument;
file = new File(this.activeDocument.fullName + '.png');
options = new PNGSaveOptions();
options.interlaced = false;
this.spriteDocument.saveAs(file, options, true, Extension.LOWERCASE);
return app.activeDocument = this.activeDocument;
};
AnimationSpriteGenerator.prototype.formSpriteLayerPosition = function(animationIndex) {
var layer;
app.activeDocument = this.spriteDocument;
layer = this.spriteDocument.artLayers.getByName("" + animationIndex + " frame");
layer.translate(this.width * (animationIndex - 1), 0);
return app.activeDocument = this.activeDocument;
};
AnimationSpriteGenerator.prototype.getVisibleArtLayers = function(result, layers) {
var layer, _i, _len;
if (result == null) {
result = [];
}
if (layers == null) {
layers = this.activeDocument.layers;
}
for (_i = 0, _len = layers.length; _i < _len; _i++) {
layer = layers[_i];
if (layer.isBackgroundLayer || !layer.visible) {
continue;
}
switch (layer.typename) {
case 'LayerSet':
this.getVisibleArtLayers(result, layer.artLayers);
break;
case 'ArtLayer':
result.push(layer);
}
}
return result;
};
AnimationSpriteGenerator.prototype.mergeVisibleLayers = function() {
var artLayers;
artLayers = this.getVisibleArtLayers();
if (artLayers.length > 0) {
this.activeDocument.activeLayer = artLayers[0];
}
if (artLayers.length > 1) {
return this.activeDocument.mergeVisibleLayers();
}
};
AnimationSpriteGenerator.prototype.duplicateAnimationFrameToSprite = function(animationIndex) {
this.activeDocument.resizeImage(this.width, this.height);
this.changeAnimationFrame(animationIndex);
this.mergeVisibleLayers();
this.activeDocument.activeLayer.name = "" + animationIndex + " frame";
this.activeDocument.activeLayer.duplicate(this.spriteDocument.activeLayer, ElementPlacement.PLACEAFTER);
this.formSpriteLayerPosition(animationIndex);
return this.revert();
};
AnimationSpriteGenerator.prototype.duplicateAnimationFrames = function(file) {
var animationIndex, animationLength, _i;
this.activeDocument = open(file);
animationLength = this.getAnimationLength();
if (animationLength === 1) {
return;
}
this.spriteDocument = documents.add(this.width * (animationLength - 1), this.height, 72, 'sprite', NewDocumentMode.RGB, DocumentFill.TRANSPARENT);
app.activeDocument = this.activeDocument;
for (animationIndex = _i = 1; 1 <= animationLength ? _i < animationLength : _i > animationLength; animationIndex = 1 <= animationLength ? ++_i : --_i) {
this.duplicateAnimationFrameToSprite(animationIndex);
}
this.save();
this.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
return this.spriteDocument.close(SaveOptions.DONOTSAVECHANGES);
};
AnimationSpriteGenerator.prototype.run = function() {
var file, _i, _len, _ref, _results;
_ref = this.psdFiles;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
file = _ref[_i];
_results.push(this.duplicateAnimationFrames(file));
}
return _results;
};
return AnimationSpriteGenerator;
})();
showInputSizeDialog = function(message, defaultSize) {
var dialog, size;
if (defaultSize == null) {
defaultSize = 100;
}
dialog = new Window("dialog", message);
dialog.bounds = [400, 400, 600, 500];
dialog.cancelButton = dialog.add("button", [40, 60, 110, 60 + 25], "Cancel", {
name: "cancel"
});
dialog.okButton = dialog.add("button", [120, 60, 170, 60 + 25], "OK", {
name: "ok"
});
dialog.sizeText = dialog.add("edittext", [10, 10, 110, 10 + 25], defaultSize);
if (dialog.show() === 1) {
size = dialog.sizeText.text;
}
size = Number(size);
if (isNaN(size) || size <= 0) {
throw Error('Invalid size');
}
return size;
};
main = function() {
var height, psdDir, width;
width = showInputSizeDialog('Input width');
height = showInputSizeDialog('Input height');
psdDir = Folder.selectDialog('Choose PSD file directory');
return new AnimationSpriteGenerator(psdDir, width, height).run();
};
main();
###
<javascriptresource>
<name>Generate a sprite from animation frames</name>
<category>web</category>
</javascriptresource>
###
# console.log
console = log: (string) -> $.writeln string
class AnimationSpriteGenerator
constructor: (psdDir, width, height) ->
@psdDir = psdDir
@psdFiles = new Folder(psdDir).getFiles('*.psd')
@width = width
@height = height
# 「ファイル -> 復帰」
revert: ->
idRvrt = charIDToTypeID( "Rvrt" )
executeAction(idRvrt, undefined, DialogModes.NO)
# アニメーションフレームの切り替え
changeAnimationFrame: (index) ->
idslct = charIDToTypeID("slct")
desc66 = new ActionDescriptor()
idnull = charIDToTypeID("null")
ref52 = new ActionReference()
idanimationFrameClass = stringIDToTypeID("animationFrameClass")
ref52.putIndex idanimationFrameClass, index
desc66.putReference idnull, ref52
executeAction idslct, desc66, DialogModes.NO
# アニメーションフレームの最大数を返す(APIがなかったので力技)
getAnimationLength: ->
count = 1
while true
try
@changeAnimationFrame count
catch error
break
count += 1
count
save: ->
app.activeDocument = @spriteDocument
file = new File(@activeDocument.fullName + '.png')
options = new PNGSaveOptions()
options.interlaced = false
@spriteDocument.saveAs(file, options, true, Extension.LOWERCASE)
app.activeDocument = @activeDocument
formSpriteLayerPosition: (animationIndex) ->
app.activeDocument = @spriteDocument
layer = @spriteDocument.artLayers.getByName "#{animationIndex} frame"
layer.translate @width * (animationIndex - 1), 0
app.activeDocument = @activeDocument
getVisibleArtLayers: (result = [], layers = @activeDocument.layers) ->
for layer in layers
continue if layer.isBackgroundLayer or not layer.visible
switch layer.typename
when 'LayerSet'
@getVisibleArtLayers result, layer.artLayers
when 'ArtLayer'
result.push layer
result
mergeVisibleLayers: ->
artLayers = @getVisibleArtLayers()
@activeDocument.activeLayer = artLayers[0] if artLayers.length > 0
@activeDocument.mergeVisibleLayers() if artLayers.length > 1
duplicateAnimationFrameToSprite: (animationIndex) ->
@activeDocument.resizeImage @width, @height
@changeAnimationFrame animationIndex
@mergeVisibleLayers()
@activeDocument.activeLayer.name = "#{animationIndex} frame"
@activeDocument.activeLayer.duplicate @spriteDocument.activeLayer,
ElementPlacement.PLACEAFTER
@formSpriteLayerPosition animationIndex
@revert()
duplicateAnimationFrames: (file) ->
@activeDocument = open(file)
animationLength = @getAnimationLength()
return if animationLength is 1
@spriteDocument = documents.add @width * (animationLength - 1), @height,
72, 'sprite', NewDocumentMode.RGB, DocumentFill.TRANSPARENT
app.activeDocument = @activeDocument
for animationIndex in [1...animationLength]
@duplicateAnimationFrameToSprite(animationIndex)
@save()
@activeDocument.close(SaveOptions.DONOTSAVECHANGES)
@spriteDocument.close(SaveOptions.DONOTSAVECHANGES)
run: ->
for file in @psdFiles
@duplicateAnimationFrames file
showInputSizeDialog = (message, defaultSize = 100) ->
dialog = new Window("dialog", message)
dialog.bounds = [400, 400, 600, 500]
dialog.cancelButton = dialog.add "button", [40, 60, 110, 60 + 25], "Cancel",
name: "cancel"
dialog.okButton = dialog.add "button", [120, 60, 170, 60 + 25], "OK",
name: "ok"
dialog.sizeText = dialog.add "edittext", [10, 10, 110, 10 + 25], defaultSize
size = dialog.sizeText.text if dialog.show() is 1
size = Number size
if isNaN(size) or size <= 0
throw Error 'Invalid size'
size
main = ->
width = showInputSizeDialog 'Input width'
height = showInputSizeDialog 'Input height'
psdDir = Folder.selectDialog 'Choose PSD file directory'
new AnimationSpriteGenerator(psdDir, width, height).run()
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment