spritesheet-creator
Version:
Spritesheet Creator
729 lines (631 loc) • 23.6 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _jimp = _interopRequireDefault(require("jimp"));
var _globPromise = _interopRequireDefault(require("glob-promise"));
var _lodash = _interopRequireDefault(require("lodash"));
var _path = _interopRequireDefault(require("path"));
var _packer = _interopRequireDefault(require("packer"));
var _sorter = _interopRequireDefault(require("sorter"));
var _smart_crop = _interopRequireDefault(require("smart_crop"));
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
var DefaultOptions = {
trim: true,
padding: 1,
divisibleByTwo: true,
square: false,
sortMethod: 'width',
packAlgorithm: 'growing-binpacking',
powerOfTwo: true
};
var SpritesheetGenerator =
/*#__PURE__*/
function () {
function SpritesheetGenerator(_ref) {
var log = _ref.log,
exportFormat = _ref.exportFormat,
outputTexturePath = _ref.outputTexturePath,
outputDataPath = _ref.outputDataPath,
options = _ref.options;
(0, _classCallCheck2["default"])(this, SpritesheetGenerator);
this.log = log;
this.exportFormat = exportFormat;
this.outputTexturePath = outputTexturePath;
this.outputDataPath = outputDataPath;
this.options = options || {};
for (var key in DefaultOptions) {
if (this.options[key] === undefined) {
this.options[key] = DefaultOptions[key];
}
}
this.log.info(this.options);
}
(0, _createClass2["default"])(SpritesheetGenerator, [{
key: "generateFurther",
value: function generateFurther() {
var _this = this;
return _regenerator["default"].async(function generateFurther$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
this.printStats();
if (this.options.trim) {
this.trimImages();
this.printStats();
}
this.pad();
this.pack();
this.determineCanvasSize();
this.calcRealPositions();
_context.next = 8;
return _regenerator["default"].awrap(Promise.all([this.generateTexture().then(function () {
return _this.saveTexture();
}), this.generateTextureData()]));
case 8:
return _context.abrupt("return", this.files);
case 9:
case "end":
return _context.stop();
}
}
}, null, this);
}
}, {
key: "generateFromMemory",
value: function generateFromMemory(files) {
var _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, file;
return _regenerator["default"].async(function generateFromMemory$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
this.files = files;
_iteratorNormalCompletion = true;
_didIteratorError = false;
_iteratorError = undefined;
_context2.prev = 4;
_iterator = this.files[Symbol.iterator]();
case 6:
if (_iteratorNormalCompletion = (_step = _iterator.next()).done) {
_context2.next = 20;
break;
}
file = _step.value;
if (file.image) {
_context2.next = 10;
break;
}
throw new Error("file is missing image");
case 10:
_context2.next = 12;
return _regenerator["default"].awrap(_jimp["default"].create({
data: file.image.bitmap.data,
width: file.image.bitmap.width,
height: file.image.bitmap.height
}));
case 12:
file.image = _context2.sent;
file.padding = {
left: 0,
right: 0,
up: 0,
down: 0
};
file.margin = {
left: 0,
right: 0,
up: 0,
down: 0
};
file.padded = {
x: 0,
y: 0,
width: 0,
height: 0,
area: 0
};
file.real = {
x: 0,
y: 0,
width: 0,
height: 0,
area: 0
};
case 17:
_iteratorNormalCompletion = true;
_context2.next = 6;
break;
case 20:
_context2.next = 26;
break;
case 22:
_context2.prev = 22;
_context2.t0 = _context2["catch"](4);
_didIteratorError = true;
_iteratorError = _context2.t0;
case 26:
_context2.prev = 26;
_context2.prev = 27;
if (!_iteratorNormalCompletion && _iterator["return"] != null) {
_iterator["return"]();
}
case 29:
_context2.prev = 29;
if (!_didIteratorError) {
_context2.next = 32;
break;
}
throw _iteratorError;
case 32:
return _context2.finish(29);
case 33:
return _context2.finish(26);
case 34:
return _context2.abrupt("return", this.generateFurther());
case 35:
case "end":
return _context2.stop();
}
}
}, null, this, [[4, 22, 26, 34], [27,, 29, 33]]);
}
}, {
key: "generateFromFiles",
value: function generateFromFiles(inputPatterns) {
return _regenerator["default"].async(function generateFromFiles$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
_context3.next = 2;
return _regenerator["default"].awrap(this.getFileList(inputPatterns));
case 2:
if (!(this.files.length === 0)) {
_context3.next = 5;
break;
}
this.log.warn('no files were found, finishing');
return _context3.abrupt("return", []);
case 5:
_context3.next = 7;
return _regenerator["default"].awrap(this.readImages());
case 7:
return _context3.abrupt("return", this.generateFurther());
case 8:
case "end":
return _context3.stop();
}
}
}, null, this);
}
}, {
key: "formatPercentChange",
value: function formatPercentChange(oldValue, newValue) {
var percentChange = newValue / oldValue * 100 - 100;
return "".concat(percentChange > 0 ? '+' : '').concat(percentChange.toFixed(2), "%");
}
}, {
key: "printStats",
value: function printStats() {
var stats = {
totalBytes: 0,
totalPixels: 0
};
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = this.files[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var file = _step2.value;
var image = file.image;
if (!image) continue;
stats.totalBytes += image.bitmap.data.length;
stats.totalPixels += image.bitmap.width * image.bitmap.height;
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2["return"] != null) {
_iterator2["return"]();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
var btChangeString = '';
var pxChangeString = '';
if (this.stats) {
btChangeString = " (".concat(this.formatPercentChange(this.stats.totalBytes, stats.totalBytes), ")");
pxChangeString = " (".concat(this.formatPercentChange(this.stats.totalPixels, stats.totalPixels), ")");
}
this.log.info("".concat(stats.totalBytes, " total bytes").concat(btChangeString));
this.log.info("".concat(stats.totalPixels, " total pixels").concat(pxChangeString));
this.stats = stats;
}
}, {
key: "saveTexture",
value: function saveTexture() {
return _regenerator["default"].async(function saveTexture$(_context4) {
while (1) {
switch (_context4.prev = _context4.next) {
case 0:
if (this.outputTexturePath) {
_context4.next = 3;
break;
}
this.log.info("skipping texture save");
return _context4.abrupt("return");
case 3:
this.log.info("saving texture to ".concat(this.outputTexturePath));
_context4.next = 6;
return _regenerator["default"].awrap(this.texture.write(this.outputTexturePath));
case 6:
this.log.info("done saving texture");
case 7:
case "end":
return _context4.stop();
}
}
}, null, this);
}
}, {
key: "getFileList",
value: function getFileList(inputPatterns) {
return _regenerator["default"].async(function getFileList$(_context5) {
while (1) {
switch (_context5.prev = _context5.next) {
case 0:
this.log.info("getting file list from ".concat(inputPatterns.join(',')));
_context5.t0 = _lodash["default"];
_context5.next = 4;
return _regenerator["default"].awrap(Promise.all(inputPatterns.map(function (pattern) {
return (0, _globPromise["default"])(pattern).then(function (files) {
return files.map(function (name) {
return {
pattern: pattern,
name: name,
padding: {
left: 0,
right: 0,
up: 0,
down: 0
},
margin: {
left: 0,
right: 0,
up: 0,
down: 0
},
padded: {
x: 0,
y: 0,
width: 0,
height: 0,
area: 0
},
real: {
x: 0,
y: 0,
width: 0,
height: 0,
area: 0
}
};
});
});
})));
case 4:
_context5.t1 = _context5.sent;
this.files = _context5.t0.flatten.call(_context5.t0, _context5.t1);
this.log.info("got ".concat(this.files.length, " files"));
case 7:
case "end":
return _context5.stop();
}
}
}, null, this);
}
}, {
key: "readImages",
value: function readImages() {
return _regenerator["default"].async(function readImages$(_context6) {
while (1) {
switch (_context6.prev = _context6.next) {
case 0:
this.log.info("reading files into memory");
_context6.next = 3;
return _regenerator["default"].awrap(Promise.all(this.files.map(function (file) {
return _jimp["default"].read(file.name).then(function (image) {
file.image = image;
file.real = {
width: image.bitmap.width,
height: image.bitmap.height,
x: 0,
y: 0,
area: image.bitmap.width * image.bitmap.height
};
})["catch"](function (err) {
throw new Error("couldn't read ".concat(file.name, ": ").concat(err));
});
})));
case 3:
case "end":
return _context6.stop();
}
}
}, null, this);
}
}, {
key: "trimImages",
value: function trimImages() {
this.log.info("trimming images");
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = this.files[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var file = _step3.value;
var image = file.image;
if (!image) continue;
var _smartCrop = (0, _smart_crop["default"])({
image: image
}),
margin = _smartCrop.cropped;
file.real.width = image.bitmap.width;
file.real.height = image.bitmap.height;
file.real.area = file.real.width * file.real.height;
file.margin = margin;
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3["return"] != null) {
_iterator3["return"]();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
}
}, {
key: "pad",
value: function pad() {
this.log.info("padding files");
var padding = this.options.padding ? this.options.padding : 0;
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
for (var _iterator4 = this.files[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
var file = _step4.value;
var image = file.image;
if (!image) continue;
file.padding = {
up: padding,
right: padding,
down: padding,
left: padding
};
file.padded.width = file.real.width + padding * 2;
file.padded.height = file.real.height + padding * 2;
if (this.options.divisibleByTwo) {
if (file.padded.width & 1) {
file.padded.width += 1;
file.padding.left++;
}
if (file.padded.height & 1) {
file.padded.height += 1;
file.padding.up++;
}
}
file.padded.area = file.padded.width * file.padded.height;
}
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4["return"] != null) {
_iterator4["return"]();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
}
}
}, {
key: "roundToPowerOfTwo",
value: function roundToPowerOfTwo(value) {
var powers = 2;
while (value > powers) {
powers *= 2;
}
return powers;
}
}, {
key: "pack",
value: function pack() {
var sortMethod = this.options.sortMethod;
if (!sortMethod) {
throw new Error("sorting algorithm not found");
}
this.log.info("sorting files using '".concat(sortMethod, "' method"));
(0, _sorter["default"])(sortMethod, this.files);
var algorithm = this.options.packAlgorithm;
if (!algorithm) {
throw new Error("packing algorithm not found");
}
this.log.info("packing using '".concat(algorithm, "' algorithm"));
(0, _packer["default"])({
algorithm: algorithm,
files: this.files,
options: this.options
});
}
}, {
key: "calcRealPositions",
value: function calcRealPositions() {
var _iteratorNormalCompletion5 = true;
var _didIteratorError5 = false;
var _iteratorError5 = undefined;
try {
for (var _iterator5 = this.files[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
var file = _step5.value;
file.real.x = file.padded.x + file.padding.left;
file.real.y = file.padded.y + file.padding.up;
}
} catch (err) {
_didIteratorError5 = true;
_iteratorError5 = err;
} finally {
try {
if (!_iteratorNormalCompletion5 && _iterator5["return"] != null) {
_iterator5["return"]();
}
} finally {
if (_didIteratorError5) {
throw _iteratorError5;
}
}
}
}
}, {
key: "determineCanvasSize",
value: function determineCanvasSize() {
this.log.info("detemining minimum canvas size");
var _this$options = this.options,
width = _this$options.width,
height = _this$options.height;
if (!width || !height) {
throw new Error("couldn't detemine the minimum canvas size");
}
this.log.info("minimum canvas size required: ".concat(width, "x").concat(height));
if (this.options.square) {
width = height = Math.max(width, height);
this.log.info("square texture required: ".concat(width, "x").concat(height));
}
if (this.options.powerOfTwo) {
width = this.roundToPowerOfTwo(width);
height = this.roundToPowerOfTwo(height);
this.log.info("power of two required: ".concat(width, "x").concat(height));
}
if (this.options.maxTextureSize) {
if (width > this.options.maxTextureSize || height > this.options.maxTextureSize) {
throw new Error("texture size too large (> ".concat(this.options.maxTextureSize, ")"));
}
}
this.log.info("determined final texture size: ".concat(width, "x").concat(height));
this.options = _objectSpread({}, this.options, {
width: width,
height: height
});
}
}, {
key: "generateTexture",
value: function generateTexture() {
var _iteratorNormalCompletion6, _didIteratorError6, _iteratorError6, _iterator6, _step6, file;
return _regenerator["default"].async(function generateTexture$(_context7) {
while (1) {
switch (_context7.prev = _context7.next) {
case 0:
this.log.info("generating texture");
_context7.next = 3;
return _regenerator["default"].awrap(_jimp["default"].create(this.options.width, this.options.height));
case 3:
this.texture = _context7.sent;
_iteratorNormalCompletion6 = true;
_didIteratorError6 = false;
_iteratorError6 = undefined;
_context7.prev = 7;
for (_iterator6 = this.files[Symbol.iterator](); !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
file = _step6.value;
this.texture.composite(file.image, file.real.x, file.real.y);
}
_context7.next = 15;
break;
case 11:
_context7.prev = 11;
_context7.t0 = _context7["catch"](7);
_didIteratorError6 = true;
_iteratorError6 = _context7.t0;
case 15:
_context7.prev = 15;
_context7.prev = 16;
if (!_iteratorNormalCompletion6 && _iterator6["return"] != null) {
_iterator6["return"]();
}
case 18:
_context7.prev = 18;
if (!_didIteratorError6) {
_context7.next = 21;
break;
}
throw _iteratorError6;
case 21:
return _context7.finish(18);
case 22:
return _context7.finish(15);
case 23:
this.log.info("generated texture of ".concat(this.texture.bitmap.data.length, " bytes"));
case 24:
case "end":
return _context7.stop();
}
}
}, null, this, [[7, 11, 15, 23], [16,, 18, 22]]);
}
}, {
key: "generateTextureData",
value: function generateTextureData() {
var exportPlugin;
return _regenerator["default"].async(function generateTextureData$(_context8) {
while (1) {
switch (_context8.prev = _context8.next) {
case 0:
this.log.info("save data to '".concat(this.outputDataPath, "' with '").concat(this.exportFormat, "' export format"));
this.log.info("loading export plugin");
_context8.prev = 2;
// $FlowFixMe
exportPlugin = require(_path["default"].join(__dirname, "data_output_plugins/".concat(this.exportFormat, ".js")))["default"];
_context8.next = 9;
break;
case 6:
_context8.prev = 6;
_context8.t0 = _context8["catch"](2);
throw new Error("unsupport texture export format: ".concat(this.exportFormat));
case 9:
_context8.next = 11;
return _regenerator["default"].awrap(exportPlugin({
projectRoot: this.options.projectRoot,
files: this.files,
outputDataPath: this.outputDataPath,
outputTexturePath: this.outputTexturePath,
log: this.log
}));
case 11:
case "end":
return _context8.stop();
}
}
}, null, this, [[2, 6]]);
}
}]);
return SpritesheetGenerator;
}();
exports["default"] = SpritesheetGenerator;