Adding spritesheet import

- Updated the import dialog to allow users to specify the number of frames in the image (which defaults to 1 x and 1 y)
- Setting the frame count for x and y will draw a dotted line in the preview that shows where the image will be split into individual frames
- When imported with a frame count above 1, the source image will be split into the different frames and loaded just as if it were an animated gif
- This allows users to import existing spritesheet pngs, including those produced by the piskel export function
This commit is contained in:
James Lissiak 2015-06-01 10:29:52 -07:00
parent 8d85093874
commit 48f24c0cf3
5 changed files with 147 additions and 15 deletions

View File

@ -3,16 +3,27 @@
/************************************************************************************************/
.import-section-preview {
display : inline-block;
height : 60px;
position: relative;
display: inline-block;
height: 60px;
width: 60px;
border: 1px dashed #999;
border-radius: 3px;
}
.import-section-preview.no-border {
border-color: transparent;
}
.import-section-preview canvas {
position: absolute;
left: 0;
top: 0;
}
.dialog-section-title {
display : inline-block;
width: 55px;
width: 70px;
}
.import-size-field:nth-of-type(2) {

View File

@ -67,7 +67,7 @@
#dialog-container.import-image {
width: 500px;
height: 300px;
height: 320px;
top : 50%;
left : 50%;
position : absolute;

View File

@ -1,6 +1,6 @@
(function () {
var ns = $.namespace('pskl.controller.dialogs');
var PREVIEW_HEIGHT = 60;
var PREVIEW_HEIGHT = 60;
ns.ImportImageController = function (piskelController) {
this.importedImage_ = null;
@ -20,10 +20,15 @@
this.resizeWidth = $('[name=resize-width]');
this.resizeHeight = $('[name=resize-height]');
this.smoothResize = $('[name=smooth-resize-checkbox]');
this.smoothResize = $('[name=smooth-resize-checkbox]');
this.frameCountX = $('[name=frame-count-x]');
this.frameCountY = $('[name=frame-count-y]');
this.resizeWidth.keyup(this.onResizeInputKeyUp_.bind(this, 'width'));
this.resizeHeight.keyup(this.onResizeInputKeyUp_.bind(this, 'height'));
this.frameCountX.keyup(this.onResizeInputKeyUp_.bind(this, 'frameCountX'));
this.frameCountY.keyup(this.onResizeInputKeyUp_.bind(this, 'frameCountY'));
this.importImageForm = $('[name=import-image-form]');
this.importImageForm.submit(this.onImportFormSubmit_.bind(this));
@ -49,7 +54,24 @@
}
var height = this.importedImage_.height;
var width = this.importedImage_.width;
if (from === 'width') {
var frameCountX = parseInt(this.frameCountX.val(), 10);
if (frameCountX <= 0 || isNaN(frameCountX)) {
this.frameCountX.val(1);
frameCountX = 1;
}
var frameCountY = parseInt(this.frameCountY.val(), 10);
if (frameCountY <= 0 || isNaN(frameCountY)) {
this.frameCountY.val(1);
frameCountY = 1;
}
if (from === 'frameCountX' || from === 'frameCountY') {
this.resizeWidth.val(Math.round(width / frameCountX));
this.resizeHeight.val(Math.round(height / frameCountY));
this.drawFramesGrid_();
} else if (from === 'width') {
this.resizeHeight.val(Math.round(value * height / width));
} else {
this.resizeWidth.val(Math.round(value * width / height));
@ -64,7 +86,7 @@
// FIXME : We remove the onload callback here because JsGif will insert
// the image again and we want to avoid retriggering the image onload
this.importedImage_.onload = function () {};
this.importedImage_.onload = function () { };
var fileName = this.extractFileNameFromPath_(this.file_.name);
this.fileNameContainer.html(fileName);
@ -72,6 +94,9 @@
this.resizeWidth.val(w);
this.resizeHeight.val(h);
this.frameCountX.val(1);
this.frameCountY.val(1);
this.importPreview.width('auto');
this.importPreview.html('');
this.importPreview.append(this.createImagePreview_());
@ -101,19 +126,23 @@
if (image) {
if (window.confirm('You are about to create a new Piskel, unsaved changes will be lost.')) {
var gifLoader = new window.SuperGif({
gif : image
gif: image
});
gifLoader.load({
success : function () {
success: function () {
var images = gifLoader.getFrames().map(function (frame) {
return pskl.utils.CanvasUtils.createFromImageData(frame.data);
});
this.createPiskelFromImages_(images);
this.closeDialog();
}.bind(this),
error : function () {
this.createPiskelFromImages_([image]);
error: function () {
var images = pskl.utils.CanvasUtils.createFramesFromImage(
image,
this.frameCountX.val(),
this.frameCountY.val());
this.createPiskelFromImages_(images);
this.closeDialog();
}.bind(this)
});
@ -143,4 +172,54 @@
pskl.app.piskelController.setPiskel(piskel);
pskl.app.previewController.setFPS(Constants.DEFAULT.FPS);
};
ns.ImportImageController.prototype.drawFramesGrid_ = function () {
var canvasWrapper = this.importPreview.children('canvas');
var countX = this.frameCountX.val();
var countY = this.frameCountY.val();
if (countX > 1 || countY > 1) {
var width = this.importedImage_.width;
var height = this.importedImage_.height;
var frameW = width / countX;
var frameH = height / countY;
var canvas = canvasWrapper.get(0);
if (!canvasWrapper.length) {
// Create a new canvas for the grid
canvas = pskl.utils.CanvasUtils.createCanvas(width + 1, height + 1);
this.importPreview.append(canvas);
canvasWrapper = $(canvas);
}
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
// Draw the vertical lines
for (var x = 0.5; x < width + 1; x += frameW) {
context.moveTo(x, 0);
context.lineTo(x, height);
}
// Draw the horizontal lines
for (var y = 0.5; y < height + 1; y += frameH) {
context.moveTo(0, y);
context.lineTo(width, y);
}
// Set the line style to dashed
context.lineWidth = 1;
context.setLineDash([2, 1]);
context.strokeStyle = '#000000';
context.stroke();
// Resize the canvas so that it matches the preview height and stretches correctly
canvasWrapper.height(PREVIEW_HEIGHT + 1);
canvasWrapper.show();
this.importPreview.addClass('no-border');
} else {
canvasWrapper.hide();
this.importPreview.removeClass('no-border');
}
};
})();

View File

@ -33,6 +33,43 @@
return canvas;
},
/**
* Splits the specified image into several new canvas elements based on the
* supplied horizontal (x) and vertical (y) counts.
* @param image The source image that will be split
* @param {Number} frameCountX The number of frames in the horizontal axis
* @param {Number} frameCountY The number of frames in the vertical axis
* @returns {Array} An array of canvas elements that contain the split frames
*/
createFramesFromImage : function (image, frameCountX, frameCountY) {
var canvasArray = [];
var frameWidth = image.width / frameCountX;
var frameHeight = image.height / frameCountY;
// Loop through the frames prioritizing the spritesheet as horizonal strips
for (var y = 0; y < frameCountY; y++) {
for (var x = 0; x < frameCountX; x++) {
var canvas = pskl.utils.CanvasUtils.createCanvas(frameWidth, frameHeight);
var context = canvas.getContext('2d');
// Blit the correct part of the source image into the new canvas
context.drawImage(
image,
x * frameWidth,
y * frameHeight,
frameWidth,
image.height,
0,
0,
frameWidth,
image.height);
canvasArray.push(canvas);
}
}
return canvasArray;
},
/**
* By default, all scaling operations on a Canvas 2D Context are performed using antialiasing.
* Resizing a 32x32 image to 320x320 will lead to a blurry output.

View File

@ -14,12 +14,17 @@
</div>
<div class="import-section">
<span class="dialog-section-title">Size :</span>
<input type="text" class="textfield import-size-field" name="resize-width"/>x
<input type="text" class="textfield import-size-field" name="resize-height"/>
<input type="text" class="textfield import-size-field" name="resize-width" />x
<input type="text" class="textfield import-size-field" name="resize-height" />
</div>
<div class="import-section">
<span class="dialog-section-title">Frames :</span>
<input type="text" class="textfield import-size-field" name="frame-count-x" />x
<input type="text" class="textfield import-size-field" name="frame-count-y" />
</div>
<div class="import-section">
<span class="import-section-title">Smooth resize :</span>
<input type="checkbox" checked="checked" name="smooth-resize-checkbox" value="1"/>
<input type="checkbox" checked="checked" name="smooth-resize-checkbox" value="1" />
</div>
<input type="submit" name="import-submit" class="button button-primary import-button" value="Import" />
</form>