mumuck/0打工人/LayersToPNG.jsx
2024-12-04 14:57:59 +08:00

196 lines
6.2 KiB
JavaScript

// This script exports photoshop layers as individual images.
// It also write a JSON file that can be imported into Spine
// where the images will be displayed in the same positions.
// Settings.
var ignoreHiddenLayers = true;
var savePNGs = true;
var saveJSON = true;
var scaleFactor = 1;
showDialog();
function main () {
// Output dir.
var dir = app.activeDocument.path + "/images/";
new Folder(dir).create();
var name = decodeURI(app.activeDocument.name);
name = name.substring(0, name.indexOf("."));
app.activeDocument.duplicate();
// Collect original layer visibility and hide all layers.
var layers = [];
getLayers(app.activeDocument, layers);
var layerCount = layers.length;
var layerVisibility = {};
for (var i = layerCount - 1; i >= 0; i--) {
var layer = layers[i];
layerVisibility[layer] = layer.visible;
layer.visible = false;
}
// Save JSON.
if (saveJSON || savePNGs) {
var json = "{\"bones\":[{\"name\":\"root\"}],\"slots\":[\n";
for (var i = layerCount - 1; i >= 0; i--) {
var layer = layers[i];
if (ignoreHiddenLayers && !layerVisibility[layer]) continue;
json += "{\"name\":\"" + trim(layer.name) + "\",\"bone\":\"root\",\"attachment\":\"" + trim(layer.name) + "\"},\n";
}
json += "],\"skins\":{\"default\":{\n";
for (var i = layerCount - 1; i >= 0; i--) {
var layer = layers[i];
if (ignoreHiddenLayers && !layerVisibility[layer]) continue;
var x = app.activeDocument.width.as("px") * scaleFactor;
var y = app.activeDocument.height.as("px") * scaleFactor;
layer.visible = true;
if (!layer.isBackgroundLayer)
app.activeDocument.trim(TrimType.TRANSPARENT, false, true, true, false);
x -= app.activeDocument.width.as("px") * scaleFactor;
y -= app.activeDocument.height.as("px") * scaleFactor;
if (!layer.isBackgroundLayer)
app.activeDocument.trim(TrimType.TRANSPARENT, true, false, false, true);
var width = app.activeDocument.width.as("px") * scaleFactor;
var height = app.activeDocument.height.as("px") * scaleFactor;
// Save image.
if (savePNGs) {
if (scaleFactor != 1) scaleImage();
var file = File(dir + "/" + trim(layer.name));
if (file.exists) file.remove();
activeDocument.saveAs(file, new PNGSaveOptions(), true, Extension.LOWERCASE);
if (scaleFactor != 1) stepHistoryBack();
}
if (!layer.isBackgroundLayer) {
stepHistoryBack();
stepHistoryBack();
}
layer.visible = false;
x += Math.round(width) / 2;
y += Math.round(height) / 2;
json += "\"" + trim(layer.name) + "\":{\"" + trim(layer.name) + "\":{\"x\":" + x + ",\"y\":" + y+ ",\"width\":" + Math.round(width) + ",\"height\":" + Math.round(height) + "}},\n";
}
json += "}}, \"animations\": { \"animation\": {} }}";
if (saveJSON) {
// Write file.
var file = new File(dir + name + ".json");
file.remove();
file.open("a");
file.lineFeed = "\n";
file.write(json);
file.close();
}
}
activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
// Unfinished!
function hasLayerSets (layerset) {
layerset = layerset.layerSets;
for (var i = 0; i < layerset.length; i++)
if (layerset[i].layerSets.length > 0) hasLayerSets(layerset[i]);
}
function scaleImage() {
var imageSize = app.activeDocument.width.as("px");
app.activeDocument.resizeImage(UnitValue(imageSize * scaleFactor, "px"), undefined, 72, ResampleMethod.BICUBICSHARPER);
}
function stepHistoryBack () {
var desc = new ActionDescriptor();
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID( "HstS" ), charIDToTypeID( "Ordn" ), charIDToTypeID( "Prvs" ));
desc.putReference(charIDToTypeID( "null" ), ref);
executeAction( charIDToTypeID( "slct" ), desc, DialogModes.NO );
}
function getLayers (layer, collect) {
if (!layer.layers || layer.layers.length == 0) return layer;
for (var i = 0, n = layer.layers.length; i < n; i++) {
// For checking if its an adjustment layer, but it also excludes
// LayerSets so we need to find the different types needed.
//if (layer.layers[i].kind == LayerKind.NORMAL) {
var child = getLayers(layer.layers[i], collect)
if (child) collect.push(child);
//}
}
}
function trim (value) {
return value.replace(/^\s+|\s+$/g, "");
}
function hasFilePath() {
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
return executeActionGet(ref).hasKey(stringIDToTypeID('fileReference'));
}
function showDialog () {
if (!hasFilePath()) {
alert("File path not found.\nYou need to save the document before continuing.");
return;
}
var dialog = new Window("dialog", "Export Layers");
dialog.savePNGs = dialog.add("checkbox", undefined, "Save PNGs");
dialog.savePNGs.value = savePNGs;
dialog.savePNGs.alignment = "left";
dialog.saveJSON = dialog.add("checkbox", undefined, "Save JSON");
dialog.saveJSON.alignment = "left";
dialog.saveJSON.value = saveJSON;
dialog.ignoreHiddenLayers = dialog.add("checkbox", undefined, "Ignore hidden layers");
dialog.ignoreHiddenLayers.alignment = "left";
dialog.ignoreHiddenLayers.value = ignoreHiddenLayers;
var scaleGroup = dialog.add("panel", [0, 0, 180, 50], "Image Scale");
var scaleText = scaleGroup.add("edittext", [10,10,40,30], scaleFactor * 100);
scaleGroup.add("statictext", [45, 12, 100, 20], "%");
var scaleSlider = scaleGroup.add("slider", [60, 10,165,20], scaleFactor * 100, 1, 100);
scaleText.onChanging = function() {
scaleSlider.value = scaleText.text;
if (scaleText.text < 1 || scaleText.text > 100) {
alert("Valid numbers are 1-100.");
scaleText.text = scaleFactor * 100;
scaleSlider.value = scaleFactor * 100;
}
};
scaleSlider.onChanging = function() { scaleText.text = Math.round(scaleSlider.value); };
var confirmGroup = dialog.add("group", [0, 0, 180, 50]);
var runButton = confirmGroup.add("button", [10, 10, 80, 35], "Ok");
var cancelButton = confirmGroup.add("button", [90, 10, 170, 35], "Cancel");
cancelButton.onClick = function() { this.parent.close(0); return; };
runButton.onClick = function() {
savePNGs = dialog.savePNGs.value;
saveJSON = dialog.saveJSON.value;
ignoreHiddenLayers = dialog.ignoreHiddenLayers.value;
scaleFactor = scaleSlider.value / 100;
main();
this.parent.close(0);
};
dialog.orientation = "column";
dialog.center();
dialog.show();
}