mirror of
https://github.com/piskelapp/piskel.git
synced 2023-08-10 21:12:52 +03:00
Issue #414: part6: Support transparency when exporting as PNG spritesheet
Added flattenFrameAt to LayerUtils. Added renderFrameAt to PiskelController (using flattenFrameAt) Use renderFrameAt in PiskelRenderer (which is used for PNG spritesheet) chore: renamed createLayerFromSpritesheet to createFramesFromSpritesheet (in LayerUtils)
This commit is contained in:
parent
d2dc42e7cf
commit
7bf2662b66
@ -103,6 +103,10 @@
|
|||||||
return mergedFrame;
|
return mergedFrame;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ns.PiskelController.prototype.renderFrameAt = function (index, preserveOpacity) {
|
||||||
|
return pskl.utils.LayerUtils.flattenFrameAt(this.getLayers(), index, preserveOpacity);
|
||||||
|
};
|
||||||
|
|
||||||
ns.PiskelController.prototype.hasFrameAt = function (index) {
|
ns.PiskelController.prototype.hasFrameAt = function (index) {
|
||||||
return !!this.getCurrentLayer().getFrameAt(index);
|
return !!this.getCurrentLayer().getFrameAt(index);
|
||||||
};
|
};
|
||||||
|
@ -5,10 +5,30 @@
|
|||||||
ns.PiskelRenderer = function (piskelController) {
|
ns.PiskelRenderer = function (piskelController) {
|
||||||
var frames = [];
|
var frames = [];
|
||||||
for (var i = 0 ; i < piskelController.getFrameCount() ; i++) {
|
for (var i = 0 ; i < piskelController.getFrameCount() ; i++) {
|
||||||
frames.push(piskelController.getMergedFrameAt(i));
|
frames.push(piskelController.renderFrameAt(i, true));
|
||||||
}
|
}
|
||||||
ns.FramesheetRenderer.call(this, frames);
|
this.piskelController = piskelController;
|
||||||
|
this.frames = frames;
|
||||||
};
|
};
|
||||||
|
|
||||||
pskl.utils.inherit(ns.PiskelRenderer, ns.FramesheetRenderer);
|
ns.PiskelRenderer.prototype.renderAsCanvas = function () {
|
||||||
|
var canvas = this.createCanvas_();
|
||||||
|
for (var i = 0 ; i < this.frames.length ; i++) {
|
||||||
|
var frame = this.frames[i];
|
||||||
|
this.drawFrameInCanvas_(frame, canvas, i * this.piskelController.getWidth(), 0);
|
||||||
|
}
|
||||||
|
return canvas;
|
||||||
|
};
|
||||||
|
|
||||||
|
ns.PiskelRenderer.prototype.drawFrameInCanvas_ = function (frame, canvas, offsetWidth, offsetHeight) {
|
||||||
|
var context = canvas.getContext('2d');
|
||||||
|
context.drawImage(frame, offsetWidth, offsetHeight, frame.width, frame.height);
|
||||||
|
};
|
||||||
|
|
||||||
|
ns.PiskelRenderer.prototype.createCanvas_ = function () {
|
||||||
|
var count = this.frames.length;
|
||||||
|
var width = count * this.piskelController.getWidth();
|
||||||
|
var height = this.piskelController.getHeight();
|
||||||
|
return pskl.utils.CanvasUtils.createCanvas(width, height);
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
@ -3,12 +3,15 @@
|
|||||||
|
|
||||||
ns.LayerUtils = {
|
ns.LayerUtils = {
|
||||||
/**
|
/**
|
||||||
* Create a pskl.model.Layer from an Image object.
|
* Create a Frame array from an Image object.
|
||||||
* Transparent pixels will either be converted to completely opaque or completely transparent pixels.
|
* Transparent pixels will either be converted to completely opaque or completely transparent pixels.
|
||||||
|
* TODO : move to FrameUtils
|
||||||
|
*
|
||||||
* @param {Image} image source image
|
* @param {Image} image source image
|
||||||
* @return {pskl.model.Frame} corresponding frame
|
* @param {Number} frameCount number of frames in the spritesheet
|
||||||
|
* @return {Array<Frame>}
|
||||||
*/
|
*/
|
||||||
createLayerFromSpritesheet : function (image, frameCount) {
|
createFramesFromSpritesheet : function (image, frameCount) {
|
||||||
var width = image.width;
|
var width = image.width;
|
||||||
var height = image.height;
|
var height = image.height;
|
||||||
var frameWidth = width / frameCount;
|
var frameWidth = width / frameCount;
|
||||||
@ -37,6 +40,22 @@
|
|||||||
});
|
});
|
||||||
var mergedLayer = pskl.model.Layer.fromFrames(layerA.getName(), mergedFrames);
|
var mergedLayer = pskl.model.Layer.fromFrames(layerA.getName(), mergedFrames);
|
||||||
return mergedLayer;
|
return mergedLayer;
|
||||||
|
},
|
||||||
|
|
||||||
|
flattenFrameAt : function (layers, index, preserveOpacity) {
|
||||||
|
var width = layers[0].getFrameAt(index).getWidth();
|
||||||
|
var height = layers[0].getFrameAt(index).getHeight();
|
||||||
|
var canvas = pskl.utils.CanvasUtils.createCanvas(width, height);
|
||||||
|
var context = canvas.getContext('2d');
|
||||||
|
layers.forEach(function (l) {
|
||||||
|
var frameRender = pskl.utils.FrameUtils.toImage(l.getFrameAt(index));
|
||||||
|
if (preserveOpacity) {
|
||||||
|
context.globalAlpha = l.getOpacity();
|
||||||
|
}
|
||||||
|
context.drawImage(frameRender, 0, 0, width, height, 0, 0, width, height);
|
||||||
|
});
|
||||||
|
|
||||||
|
return canvas;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
// 2 - attach the onload callback that will be triggered asynchronously
|
// 2 - attach the onload callback that will be triggered asynchronously
|
||||||
image.onload = function () {
|
image.onload = function () {
|
||||||
// 5 - extract the frames from the loaded image
|
// 5 - extract the frames from the loaded image
|
||||||
var frames = pskl.utils.LayerUtils.createLayerFromSpritesheet(image, layerData.frameCount);
|
var frames = pskl.utils.LayerUtils.createFramesFromSpritesheet(image, layerData.frameCount);
|
||||||
// 6 - add each image to the layer
|
// 6 - add each image to the layer
|
||||||
this.addFramesToLayer(frames, layer, index);
|
this.addFramesToLayer(frames, layer, index);
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
|
@ -16,10 +16,10 @@
|
|||||||
* we expect this to be a 3x2 image, one black line above a white line.
|
* we expect this to be a 3x2 image, one black line above a white line.
|
||||||
*
|
*
|
||||||
* However Frame.createFromGrid needs the following input to create such an image :
|
* However Frame.createFromGrid needs the following input to create such an image :
|
||||||
*
|
*
|
||||||
* [[black, white],
|
* [[black, white],
|
||||||
* [black, white],
|
* [black, white],
|
||||||
* [black, white]]
|
* [black, white]]
|
||||||
*
|
*
|
||||||
* This helper will build the second array from the first array.
|
* This helper will build the second array from the first array.
|
||||||
*/
|
*/
|
||||||
@ -41,4 +41,32 @@
|
|||||||
expect(color).toBe(grid[row][col]);
|
expect(color).toBe(grid[row][col]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ns.imageEqualsGrid = function (image, grid) {
|
||||||
|
for (var x = 0 ; x < grid.length ; x++) {
|
||||||
|
for (var y = 0 ; y < grid[x].length ; y++) {
|
||||||
|
var expected = tinycolor(grid[x][y]).toRgbString();
|
||||||
|
var color = tinycolor(ns.getRgbaAt(image, x, y)).toRgbString();
|
||||||
|
expect(color).toBe(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ns.getRgbaAt = function (image, x, y) {
|
||||||
|
var w = image.width;
|
||||||
|
var h = image.height;
|
||||||
|
var canvas = pskl.utils.CanvasUtils.createCanvas(w, h);
|
||||||
|
var context = canvas.getContext('2d');
|
||||||
|
|
||||||
|
context.drawImage(image, 0, 0, w, h, 0, 0, w, h);
|
||||||
|
var imageData = context.getImageData(0, 0, w, h).data;
|
||||||
|
var i = (y * w + x) * 4;
|
||||||
|
|
||||||
|
return {
|
||||||
|
r : imageData[i],
|
||||||
|
g : imageData[i + 1],
|
||||||
|
b : imageData[i + 2],
|
||||||
|
a : imageData[i + 3]/255
|
||||||
|
};
|
||||||
|
}
|
||||||
})();
|
})();
|
@ -74,7 +74,7 @@ describe("FrameUtils suite", function() {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it ("[LayerUtils] creates a layer from a simple spritesheet", function () {
|
it ("[LayerUtils] creates frames from a simple spritesheet", function () {
|
||||||
var B = black, R = red;
|
var B = black, R = red;
|
||||||
|
|
||||||
// original image in 4x2
|
// original image in 4x2
|
||||||
@ -86,7 +86,7 @@ describe("FrameUtils suite", function() {
|
|||||||
var spritesheet = pskl.utils.FrameUtils.toImage(frame);
|
var spritesheet = pskl.utils.FrameUtils.toImage(frame);
|
||||||
|
|
||||||
// split the spritesheet by 4
|
// split the spritesheet by 4
|
||||||
var frames = pskl.utils.LayerUtils.createLayerFromSpritesheet(spritesheet, 4);
|
var frames = pskl.utils.LayerUtils.createFramesFromSpritesheet(spritesheet, 4);
|
||||||
|
|
||||||
// expect 4 frames of 1x2
|
// expect 4 frames of 1x2
|
||||||
expect(frames.length).toBe(4);
|
expect(frames.length).toBe(4);
|
||||||
|
56
test/js/utils/LayerUtilsTest.js
Normal file
56
test/js/utils/LayerUtilsTest.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
describe("LayerUtils test", function() {
|
||||||
|
|
||||||
|
var B = '#000000';
|
||||||
|
var R = '#ff0000';
|
||||||
|
var T = Constants.TRANSPARENT_COLOR;
|
||||||
|
var frameEqualsGrid = test.testutils.frameEqualsGrid;
|
||||||
|
var imageEqualsGrid = test.testutils.imageEqualsGrid;
|
||||||
|
|
||||||
|
var frame1 = pskl.model.Frame.fromPixelGrid([
|
||||||
|
[B, T],
|
||||||
|
[T, B]
|
||||||
|
]);
|
||||||
|
|
||||||
|
var frame2 = pskl.model.Frame.fromPixelGrid([
|
||||||
|
[T, R],
|
||||||
|
[R, T]
|
||||||
|
]);
|
||||||
|
|
||||||
|
beforeEach(function() {});
|
||||||
|
afterEach(function() {});
|
||||||
|
|
||||||
|
it("flattens a frame", function() {
|
||||||
|
// when
|
||||||
|
var l1 = new pskl.model.Layer('l1');
|
||||||
|
l1.addFrame(frame1);
|
||||||
|
var l2 = new pskl.model.Layer('l2');
|
||||||
|
l2.addFrame(frame2);
|
||||||
|
|
||||||
|
// then
|
||||||
|
var flattened = pskl.utils.LayerUtils.flattenFrameAt([l1, l2], 0);
|
||||||
|
|
||||||
|
//verify
|
||||||
|
imageEqualsGrid(flattened, [
|
||||||
|
[B, R],
|
||||||
|
[R, B]
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("flattens a frame with opacity", function() {
|
||||||
|
// when
|
||||||
|
var l1 = new pskl.model.Layer('l1');
|
||||||
|
l1.addFrame(frame1);
|
||||||
|
var l2 = new pskl.model.Layer('l2');
|
||||||
|
l2.setOpacity(0.5);
|
||||||
|
l2.addFrame(frame2);
|
||||||
|
|
||||||
|
// then
|
||||||
|
var flattened = pskl.utils.LayerUtils.flattenFrameAt([l1, l2], 0, true);
|
||||||
|
|
||||||
|
//verify
|
||||||
|
imageEqualsGrid(flattened, [
|
||||||
|
[B, 'rgba(255,0,0,0.5)'],
|
||||||
|
['rgba(255,0,0,0.5)', B]
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user