From add97baf54ffb12fabc6467439bda755bd1df10b Mon Sep 17 00:00:00 2001 From: Julian Descottes Date: Fri, 11 Mar 2016 02:01:13 +0100 Subject: [PATCH] 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. --- .../exportimage/GifExportController.js | 31 +++++++++++++------ src/js/utils/ImageResizer.js | 4 +++ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/js/controller/settings/exportimage/GifExportController.js b/src/js/controller/settings/exportimage/GifExportController.js index de3ebcce..cb4c061a 100644 --- a/src/js/controller/settings/exportimage/GifExportController.js +++ b/src/js/controller/settings/exportimage/GifExportController.js @@ -105,7 +105,11 @@ ns.GifExportController.prototype.renderAsImageDataAnimatedGIF = function(zoom, fps, cb) { 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; // transparency only supported if preserveColors is true, see Issue #357 @@ -117,23 +121,30 @@ transparent = null; } + var width = this.piskelController.getWidth(); + var height = this.piskelController.getHeight(); + var gif = new window.GIF({ workers: 5, quality: 1, - width: this.piskelController.getWidth() * zoom, - height: this.piskelController.getHeight() * zoom, + width: width * zoom, + height: height * zoom, preserveColors : preserveColors, transparent : transparent }); - for (var i = 0 ; i < this.piskelController.getFrameCount() ; i++) { - var frame = this.piskelController.getMergedFrameAt(i); - var canvasRenderer = new pskl.rendering.CanvasRenderer(frame, zoom); - if (preserveColors) { + // Create a background canvas that will be filled with the transparent color before each render. + var background = pskl.utils.CanvasUtils.createCanvas(width, height); + var context = background.getContext('2d'); + context.fillStyle = transparentColor; - } - canvasRenderer.drawTransparentAs(transparentColor); - var canvas = canvasRenderer.render(); + for (var i = 0 ; i < this.piskelController.getFrameCount() ; i++) { + var render = this.piskelController.renderFrameAt(i, true); + 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'), { delay: 1000 / fps }); diff --git a/src/js/utils/ImageResizer.js b/src/js/utils/ImageResizer.js index f9343514..6f0564f8 100644 --- a/src/js/utils/ImageResizer.js +++ b/src/js/utils/ImageResizer.js @@ -2,6 +2,10 @@ var ns = $.namespace('pskl.utils'); 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) { var canvas = pskl.utils.CanvasUtils.createCanvas(targetWidth, targetHeight); var context = canvas.getContext('2d');