Skip to content

Instantly share code, notes, and snippets.

@Yanrishatum
Created September 22, 2018 23:45
Show Gist options
  • Save Yanrishatum/227175798f65e1d10f8125b333e9bc31 to your computer and use it in GitHub Desktop.
Save Yanrishatum/227175798f65e1d10f8125b333e9bc31 to your computer and use it in GitHub Desktop.
Sprite2Sheet script
// This script exports animated sprite into a spritesheet to a document copy.
// Made by: Yanrishatum <[email protected]> | http://yanrishatum.ru
// Script version: 1.1.0
// 08.02.2016: Initial release.
// 10.02.2016: Added options.
//
// License info:
// This is free and unencumbered software released into the public domain.
//
// Anyone is free to copy, modify, publish, use, compile, sell, or
// distribute this software, either in source code form or as a compiled
// binary, for any purpose, commercial or non-commercial, and by any
// means.
//
// In jurisdictions that recognize copyright laws, the author or authors
// of this software dedicate any and all copyright interest in the
// software to the public domain. We make this dedication for the benefit
// of the public at large and to the detriment of our heirs and
// successors. We intend this dedication to be an overt act of
// relinquishment in perpetuity of all present and future rights to this
// software under copyright law.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// For more information, please refer to <http://unlicense.org>
//
// Also fuck GPL, it's about "not free - not for you" and not about sharing with people.
#target photoshop
//{ Utils
var ID_SELECT = charIDToTypeID("slct");
var ID_DUPLICATE = charIDToTypeID("Dplc");
var ID_DELETE = charIDToTypeID("Dlt ");
var ID_SET = charIDToTypeID("setd");
var ID_COPY_ALL = charIDToTypeID("CpyM");
var ID_PASTE = charIDToTypeID("past");
var ID_MOVE = charIDToTypeID("move");
var ID_NULL = charIDToTypeID("null");
var ID_ORDN = charIDToTypeID("Ordn"); // ?
var ID_T = charIDToTypeID("T ");
var ID_CHANNEL = charIDToTypeID("Chnl");
var ID_HORIZONTAL = charIDToTypeID("Hrzn");
var ID_VERTICAL = charIDToTypeID("Vrtc");
var ID_ANIMATION_FRAME = stringIDToTypeID("animationFrameClass");
var ID_ANIMATION_FRAME_DELAY = stringIDToTypeID("animationFrameDelay");
var ID_TRGT = charIDToTypeID("Trgt"); // ?
var ID_SELECTION = charIDToTypeID("fsel");
var ID_ALL = charIDToTypeID("Al ");
var ID_NONE = charIDToTypeID("None");
var ID_PIXEL = charIDToTypeID("#Pxl");
// Rectangle
var ID_RECT = charIDToTypeID("Rctn");
var ID_TOP = charIDToTypeID("Top ");
var ID_LEFT = charIDToTypeID("Left");
var ID_BOTTOM = charIDToTypeID("Btom");
var ID_RIGHT = charIDToTypeID("Rght");
function setFrame(frame)
{
var desc = new ActionDescriptor();
var ref = new ActionReference();
ref.putIndex(ID_ANIMATION_FRAME, frame+1); // Because PS frame indexing starts from 1.
desc.putReference(ID_NULL, ref);
executeAction(ID_SELECT, desc, DialogModes.NO);
}
function createFrame()
{
var desc = new ActionDescriptor();
var ref = new ActionReference();
ref.putEnumerated(ID_ANIMATION_FRAME, ID_ORDN, ID_TRGT);
desc.putReference(ID_NULL, ref);
executeAction(ID_DUPLICATE, desc, DialogModes.No);
}
function removeFrame(frame)
{
if (frame !== undefined) setFrame(frame);
var desc = new ActionDescriptor();
var ref = new ActionReference();
ref.putEnumerated(ID_ANIMATION_FRAME, ID_ORDN, ID_TRGT);
desc.putReference(ID_NULL, ref);
executeAction(ID_DELETE, desc, DialogModes.NO);
}
function setDelay(delay, frame)
{
if (frame !== undefined) setFrame(frame);
var desc = new ActionDescriptor();
var frameRef = new ActionReference();
frameRef.putEnumerated(ID_ANIMATION_FRAME, ID_ORDN, ID_TRGT);
desc.putReference(ID_NULL, frameRef);
var delayDesc = new ActionDescriptor();
delayDesc.putDouble(ID_ANIMATION_FRAME_DELAY, delay);
desc.putObject(ID_T, ID_ANIMATION_FRAME, delayDesc);
executeAction(ID_SET, desc, DialogModes.NO);
}
var METHOD_AUTO = stringIDToTypeID( "animationFrameReplaceAutoDispose" );
function setDisposeMethod(method, frame)
{
if (frame !== undefined) setFrame(frame);
var desc = new ActionDescriptor();
var frameRef = new ActionReference();
frameRef.putEnumerated(ID_ANIMATION_FRAME, ID_ORDN, ID_TRGT);
desc.putReference(ID_NULL, frameRef);
var dispose = new ActionDescriptor();
dispose.putEnumerated(stringIDToTypeID( "animationFrameReplace"), stringIDToTypeID( "animationFrameReplaceType" ), method);
desc.putObject(ID_T, ID_ANIMATION_FRAME, dispose);
executeAction(ID_SET, desc, DialogModes.NO);
}
function selectAll()
{
var desc = new ActionDescriptor();
var ref = new ActionReference();
ref.putProperty(ID_CHANNEL, ID_SELECTION);
desc.putReference(ID_NULL, ref);
desc.putEnumerated(ID_T, ID_ORDN, ID_ALL);
executeAction(ID_SET, desc, DialogModes.NO);
}
function deselect()
{
var desc = new ActionDescriptor();
var ref = new ActionReference();
ref.putProperty(ID_CHANNEL, ID_SELECTION);
desc.putReference(ID_NULL, ref);
desc.putEnumerated(ID_T, ID_ORDN, ID_NONE);
executeAction(ID_SET, desc, DialogModes.NO);
}
function copyAll()
{
executeAction(ID_COPY_ALL, undefined, DialogModes.NO);
}
function paste()
{
var desc = new ActionDescriptor();
desc.putEnumerated(charIDToTypeID( "AntA" ), charIDToTypeID( "Annt" ), charIDToTypeID( "Anno" ));
desc.putBoolean(stringIDToTypeID("inPlace"), true);
executeAction(ID_PASTE, desc, DialogModes.NO);
}
function offsetLayer(x, y)
{
var desc = new ActionDescriptor();
var ref = new ActionReference();
ref.putEnumerated(charIDToTypeID("Lyr "), ID_ORDN, ID_TRGT);
desc.putReference(ID_NULL, ref);
var point = new ActionDescriptor();
point.putUnitDouble(ID_HORIZONTAL, ID_PIXEL, x);
point.putUnitDouble(ID_VERTICAL, ID_PIXEL, y);
desc.putObject(ID_T, charIDToTypeID("Ofst"), point);
executeAction(ID_MOVE, desc, DialogModes.NO );
}
function getRect(x, y, w, h)
{
var rect = new ActionDescriptor();
rect.putUnitDouble(ID_TOP, ID_PIXEL, y);
rect.putUnitDouble(ID_LEFT, ID_PIXEL, x);
rect.putUnitDouble(ID_BOTTOM, ID_PIXEL, y+h);
rect.putUnitDouble(ID_RIGHT, ID_PIXEL, x+w);
return rect;
}
function selectArea(x, y, w, h)
{
var desc = new ActionDescriptor();
var ref = new ActionReference();
ref.putProperty(ID_CHANNEL, ID_SELECTION);
desc.putReference(ID_NULL, ref);
desc.putObject(ID_T, ID_RECT, getRect(x, y, w, h));
executeAction(ID_SET, desc, DialogModes.NO);
}
var ID_CANVAS = charIDToTypeID("CnvS");
var ID_WIDTH = charIDToTypeID("Wdth");
var ID_HEIGHT = charIDToTypeID("Hght");
var ID_HORIZONTAL_ENUM = charIDToTypeID("HrzL");
var ID_VERTICAL_ENUM = charIDToTypeID("VrtL");
function resizeCanvas(w, h)
{
var desc = new ActionDescriptor();
desc.putUnitDouble(ID_WIDTH, ID_PIXEL, w);
desc.putUnitDouble(ID_HEIGHT, ID_PIXEL, h);
desc.putEnumerated(ID_HORIZONTAL, ID_HORIZONTAL_ENUM, ID_LEFT);
desc.putEnumerated(ID_VERTICAL, ID_VERTICAL_ENUM, ID_TOP);
executeAction(ID_CANVAS, desc, DialogModes.NO);
}
function cloneDocument()
{
var desc = new ActionDescriptor();
var ref = new ActionReference();
ref.putEnumerated(charIDToTypeID( "Dcmn" ), charIDToTypeID( "Ordn" ), charIDToTypeID( "Frst" ));
desc.putReference(ID_NULL, ref);
executeAction(ID_DUPLICATE, desc, DialogModes.NO);
}
function lpad(val, pad, count)
{
val = val + "";
while (val.length < count) val = pad + val;
return val;
}
//}
//{ GUI
var wnd;
function estimateSize(e)
{
var verticalAlignment = wnd.verticalGeneration.value;
var length = frameCount;
var splits = parseInt(wnd.splits.text);
if (isNaN(splits))
{
splits = 1;
wnd.splitsError.text = "Use only numerical values for split amount (minimum 1)";
}
else if (splits < 0)
{
splits = 1;
wnd.splitsError.text = "Split amount should greater than 0";
}
else if (splits >= frameCount)
{
verticalAlignment = !verticalAlignment;
splits = 1;
wnd.splitsError.text = "Split amount should be less than frame count";
}
else wnd.splitsError.text = "";
if (verticalAlignment)
{
length = Math.ceil(length / splits) * frameHeight;
splits *= frameWidth;
wnd.sizeLabel.text = "Spritesheet size: " + splits + "x" + length;
return { w: splits, h: length };
}
else
{
length = Math.ceil(length / splits) * frameWidth;
splits *= frameHeight;
wnd.sizeLabel.text = "Spritesheet size: " + length + "x" + splits;
return { w: length, h:splits };
}
}
function showGUI()
{
wnd = new Window("dialog", "Sprite To Sheet");
var container = wnd.add('group', undefined);
container.orientation = "column";
container.alignment = "left";
wnd.horizontalGeneration = container.add("radiobutton", undefined, "Horizontal alignment");
wnd.horizontalGeneration.value = true;
wnd.horizontalGeneration.onClick = estimateSize;
wnd.verticalGeneration = container.add("radiobutton", undefined, "Vertical alignment");
wnd.verticalGeneration.onClick = estimateSize;
wnd.splitGroup = container.add("group", undefined);
wnd.splitGroup.orientation = "row";
wnd.splitGroup.alignment = "left";
wnd.splitLabel = wnd.splitGroup.add("statictext", undefined, "Splits:");
wnd.splits = wnd.splitGroup.add("edittext", undefined, "1", { enterKeySignalsOnChange:true });
wnd.splits.onChange = estimateSize;
wnd.splits.onChanging = estimateSize;
wnd.splits.preferredSize = [100, 20];
wnd.splitsError = container.add("statictext", undefined, "Use only numerical values for split amount (minimum 1)");
wnd.sizeLabel = container.add("statictext", undefined, "xxxx-xxxx");
wnd.buttons = wnd.add("group");
wnd.buttons.generate = wnd.buttons.add("button", undefined, "OK");
wnd.buttons.cancel = wnd.buttons.add("button", undefined, "Cancel");
estimateSize();
var result = wnd.show();
if (result == 1)
{
var size = estimateSize();
executeProgram(size.w, size.h, wnd.verticalGeneration.value);
}
return;
}
//}
var doc = app.activeDocument;
var oldRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
var frameWidth = doc.width|0;
var frameHeight = doc.height|0;
var frameCount = 0;
while(true)
{
try
{
setFrame(frameCount);
frameCount++;
}
catch(e)
{
break;
}
}
if (frameCount == 0) throw("There is no frames");
showGUI();
function executeProgram(width, height, vertical)
{
cloneDocument();
doc = app.activeDocument;
// resizeCanvas(frameCount * frameWidth, frameHeight);
resizeCanvas(width, height);
setFrame(0);
var lrs = [];
var x = 0;
var y = 0;
for (var i = 0; i < frameCount; i++)
{
selectArea(0, 0, frameWidth, frameHeight);
copyAll();
paste();
offsetLayer(x, y);
if (vertical)
{
y += frameHeight;
if (y == height) { y = 0; x += frameWidth; }
}
else
{
x += frameWidth;
if (x == width) { x = 0; y += frameHeight; }
}
// offsetLayer(i * frameWidth, 0);
if (i + 1 != frameCount) removeFrame();
var layer = doc.activeLayer;
layer.name = "sprite_frame_" + lpad(i, "0", frameCount.toString().length);
layer.visible = false;
lrs.push(layer);
}
setFrame(0);
setDelay(0);
setDisposeMethod(METHOD_AUTO);
for (var i = 0; i < doc.artLayers.length; i++)
{
var found = false;
for (var j = 0; j < lrs.length; j++)
{
if (doc.artLayers[i] == lrs[j])
{
found = true;
break;
}
}
if (!found)
{
doc.artLayers[i].remove();
i--;
}
else doc.artLayers[i].visible = true;
}
}
app.preferences.rulerUnits = oldRulerUnits;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment