Issue #414: part8: Support transparency for GIF export

Transparent layers are rendered properly in GIFs.

As soon as a layer as some opacity (ie not 0 or 1) the GIF will be
rendered with preserveColors set to false. This could be improved,
preserveColors could still be applied if the flattended picture has
only opaque pixels, for a color count lower than the GIF limit.

Other topic to handle : we are creating way to many canvas element.

A simple GIF rendering of a 50 frames animation with 10 layers creates
1000 canvas elements before creating the GIF. Should consider adding some
pooling in the CanvasUtils.createCanvas helper.
This commit is contained in:
Julian Descottes 2016-03-11 02:01:13 +01:00 committed by juliandescottes
parent 76a29bf51a
commit add97baf54
2 changed files with 25 additions and 10 deletions

View File

@ -105,7 +105,11 @@
ns.GifExportController.prototype.renderAsImageDataAnimatedGIF = function(zoom, fps, cb) { ns.GifExportController.prototype.renderAsImageDataAnimatedGIF = function(zoom, fps, cb) {
var currentColors = pskl.app.currentColorsService.getCurrentColors(); var currentColors = pskl.app.currentColorsService.getCurrentColors();
var preserveColors = currentColors.length < MAX_GIF_COLORS; var hasTransparency = this.piskelController.getLayers().some(function (l) {
var opacity = l.getOpacity();
return opacity > 0 && opacity < 1;
});
var preserveColors = !hasTransparency && currentColors.length < MAX_GIF_COLORS;
var transparentColor, transparent; var transparentColor, transparent;
// transparency only supported if preserveColors is true, see Issue #357 // transparency only supported if preserveColors is true, see Issue #357
@ -117,23 +121,30 @@
transparent = null; transparent = null;
} }
var width = this.piskelController.getWidth();
var height = this.piskelController.getHeight();
var gif = new window.GIF({ var gif = new window.GIF({
workers: 5, workers: 5,
quality: 1, quality: 1,
width: this.piskelController.getWidth() * zoom, width: width * zoom,
height: this.piskelController.getHeight() * zoom, height: height * zoom,
preserveColors : preserveColors, preserveColors : preserveColors,
transparent : transparent transparent : transparent
}); });
for (var i = 0 ; i < this.piskelController.getFrameCount() ; i++) { // Create a background canvas that will be filled with the transparent color before each render.
var frame = this.piskelController.getMergedFrameAt(i); var background = pskl.utils.CanvasUtils.createCanvas(width, height);
var canvasRenderer = new pskl.rendering.CanvasRenderer(frame, zoom); var context = background.getContext('2d');
if (preserveColors) { context.fillStyle = transparentColor;
} for (var i = 0 ; i < this.piskelController.getFrameCount() ; i++) {
canvasRenderer.drawTransparentAs(transparentColor); var render = this.piskelController.renderFrameAt(i, true);
var canvas = canvasRenderer.render(); context.clearRect(0, 0, width, height);
context.fillRect(0, 0, width, height);
context.drawImage(render, 0, 0, width, height);
var canvas = pskl.utils.ImageResizer.scale(background, zoom);
gif.addFrame(canvas.getContext('2d'), { gif.addFrame(canvas.getContext('2d'), {
delay: 1000 / fps delay: 1000 / fps
}); });

View File

@ -2,6 +2,10 @@
var ns = $.namespace('pskl.utils'); var ns = $.namespace('pskl.utils');
ns.ImageResizer = { ns.ImageResizer = {
scale : function (image, factor, smoothingEnabled) {
return ns.ImageResizer.resize(image, image.width * factor, image.height * factor, smoothingEnabled);
},
resize : function (image, targetWidth, targetHeight, smoothingEnabled) { resize : function (image, targetWidth, targetHeight, smoothingEnabled) {
var canvas = pskl.utils.CanvasUtils.createCanvas(targetWidth, targetHeight); var canvas = pskl.utils.CanvasUtils.createCanvas(targetWidth, targetHeight);
var context = canvas.getContext('2d'); var context = canvas.getContext('2d');