diff --git a/docs/gif/css/common.css b/docs/gif/css/common.css
index d6936ec1e..6851a54e2 100644
--- a/docs/gif/css/common.css
+++ b/docs/gif/css/common.css
@@ -2,9 +2,48 @@ html, body, .container {
height: 100%;
}
-.artplayer {
- width: 800px;
+.container {
+ display: flex;
+ flex-direction: column;
+}
+
+.part-top {
+ display: flex;
+ height: 70%;
+}
+
+.part-bottom {
+ position: relative;
+ height: 30%;
+}
+
+.part-left {
+ width: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.part-right {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.part-left .artplayer {
+ width: 500px;
height: 500px;
- margin: 20px auto 0;
background: #fff;
+}
+
+.part-right .preview {
+ width: 500px;
+ height: 500px;
+ background: #f3f3f3;
+}
+
+.part-right .preview img {
+ width: 100%;
+ max-width: 100%;
}
\ No newline at end of file
diff --git a/docs/gif/index.html b/docs/gif/index.html
index 40157bab1..ac6a7c013 100644
--- a/docs/gif/index.html
+++ b/docs/gif/index.html
@@ -17,7 +17,15 @@
diff --git a/docs/gif/js/common.js b/docs/gif/js/common.js
index 53d4c62a4..75e6bb828 100644
--- a/docs/gif/js/common.js
+++ b/docs/gif/js/common.js
@@ -2,7 +2,8 @@
(function() {
consola.creat({
- target: 'body',
+ target: '.part-bottom',
+ size: '100%',
zIndex: 99,
});
@@ -30,5 +31,10 @@
art.on('firstCanplay', () => {
var preview = document.querySelector('.art-control-preview');
art.plugins.localPreview.attach(preview);
+ art.plugins.artplayerPluginGif.create({}, image => {
+ var animatedImage = document.createElement('img');
+ animatedImage.src = image;
+ document.querySelector('.preview').appendChild(animatedImage);
+ });
});
})();
diff --git a/docs/uncompiled/artplayer-plugin-gif.js b/docs/uncompiled/artplayer-plugin-gif.js
index 974fc4738..51a6e480b 100644
--- a/docs/uncompiled/artplayer-plugin-gif.js
+++ b/docs/uncompiled/artplayer-plugin-gif.js
@@ -1,2697 +1,2749 @@
(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
- typeof define === 'function' && define.amd ? define(['exports'], factory) :
- (factory((global['artplayer-plugin-gif'] = {})));
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
+ (factory((global['artplayer-plugin-gif'] = {})));
}(this, (function (exports) { 'use strict';
- function createCommonjsModule(fn, module) {
- return module = { exports: {} }, fn(module, module.exports), module.exports;
- }
-
- var gifshot = createCommonjsModule(function (module, exports) {
-
- (function (window, document, navigator, undefined) {
- /*
- utils.js
- ========
- */
-
- /* Copyright 2017 Yahoo Inc.
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
- */
-
- var utils = {
- URL: window.URL || window.webkitURL || window.mozURL || window.msURL,
- getUserMedia: function () {
- var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
- return getUserMedia ? getUserMedia.bind(navigator) : getUserMedia;
- }(),
- requestAnimFrame: window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame,
- requestTimeout: function requestTimeout(callback, delay) {
- callback = callback || utils.noop;
- delay = delay || 0;
-
- if (!utils.requestAnimFrame) {
- return setTimeout(callback, delay);
- }
-
- var start = new Date().getTime();
- var handle = new Object();
- var requestAnimFrame = utils.requestAnimFrame;
-
- var loop = function loop() {
- var current = new Date().getTime();
- var delta = current - start;
- delta >= delay ? callback.call() : handle.value = requestAnimFrame(loop);
- };
-
- handle.value = requestAnimFrame(loop);
- return handle;
- },
- Blob: window.Blob || window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder,
- btoa: function () {
- var btoa = window.btoa || function (input) {
- var output = '';
- var i = 0;
- var l = input.length;
- var key = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
- var chr1 = void 0;
- var chr2 = void 0;
- var chr3 = void 0;
- var enc1 = void 0;
- var enc2 = void 0;
- var enc3 = void 0;
- var enc4 = void 0;
-
- while (i < l) {
- chr1 = input.charCodeAt(i++);
- chr2 = input.charCodeAt(i++);
- chr3 = input.charCodeAt(i++);
- enc1 = chr1 >> 2;
- enc2 = (chr1 & 3) << 4 | chr2 >> 4;
- enc3 = (chr2 & 15) << 2 | chr3 >> 6;
- enc4 = chr3 & 63;
-
- if (isNaN(chr2)) {
- enc3 = enc4 = 64;
- } else if (isNaN(chr3)) {
- enc4 = 64;
- }
-
- output = output + key.charAt(enc1) + key.charAt(enc2) + key.charAt(enc3) + key.charAt(enc4);
- }
-
- return output;
- };
-
- return btoa ? btoa.bind(window) : utils.noop;
- }(),
- isObject: function isObject(obj) {
- return obj && Object.prototype.toString.call(obj) === '[object Object]';
- },
- isEmptyObject: function isEmptyObject(obj) {
- return utils.isObject(obj) && !Object.keys(obj).length;
- },
- isArray: function isArray(arr) {
- return arr && Array.isArray(arr);
- },
- isFunction: function isFunction(func) {
- return func && typeof func === 'function';
- },
- isElement: function isElement(elem) {
- return elem && elem.nodeType === 1;
- },
- isString: function isString(value) {
- return typeof value === 'string' || Object.prototype.toString.call(value) === '[object String]';
- },
- isSupported: {
- canvas: function canvas() {
- var el = document.createElement('canvas');
- return el && el.getContext && el.getContext('2d');
- },
- webworkers: function webworkers() {
- return window.Worker;
- },
- blob: function blob() {
- return utils.Blob;
- },
- Uint8Array: function Uint8Array() {
- return window.Uint8Array;
- },
- Uint32Array: function Uint32Array() {
- return window.Uint32Array;
- },
- videoCodecs: function () {
- var testEl = document.createElement('video');
- var supportObj = {
- 'mp4': false,
- 'h264': false,
- 'ogv': false,
- 'ogg': false,
- 'webm': false
- };
-
- try {
- if (testEl && testEl.canPlayType) {
- // Check for MPEG-4 support
- supportObj.mp4 = testEl.canPlayType('video/mp4; codecs="mp4v.20.8"') !== ''; // Check for h264 support
-
- supportObj.h264 = (testEl.canPlayType('video/mp4; codecs="avc1.42E01E"') || testEl.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"')) !== ''; // Check for Ogv support
-
- supportObj.ogv = testEl.canPlayType('video/ogg; codecs="theora"') !== ''; // Check for Ogg support
-
- supportObj.ogg = testEl.canPlayType('video/ogg; codecs="theora"') !== ''; // Check for Webm support
-
- supportObj.webm = testEl.canPlayType('video/webm; codecs="vp8, vorbis"') !== -1;
- }
- } catch (e) {}
-
- return supportObj;
- }()
- },
- noop: function noop() {},
- each: function each(collection, callback) {
- var x = void 0;
- var len = void 0;
-
- if (utils.isArray(collection)) {
- x = -1;
- len = collection.length;
-
- while (++x < len) {
- if (callback(x, collection[x]) === false) {
- break;
- }
- }
- } else if (utils.isObject(collection)) {
- for (x in collection) {
- if (collection.hasOwnProperty(x)) {
- if (callback(x, collection[x]) === false) {
- break;
- }
- }
- }
- }
- },
- mergeOptions: function mergeOptions(defaultOptions, userOptions) {
- if (!utils.isObject(defaultOptions) || !utils.isObject(userOptions) || !Object.keys) {
- return;
- }
-
- var newObj = {};
- utils.each(defaultOptions, function (key, val) {
- newObj[key] = defaultOptions[key];
- });
- utils.each(userOptions, function (key, val) {
- var currentUserOption = userOptions[key];
-
- if (!utils.isObject(currentUserOption)) {
- newObj[key] = currentUserOption;
- } else {
- if (!defaultOptions[key]) {
- newObj[key] = currentUserOption;
- } else {
- newObj[key] = utils.mergeOptions(defaultOptions[key], currentUserOption);
- }
- }
- });
- return newObj;
- },
- setCSSAttr: function setCSSAttr(elem, attr, val) {
- if (!utils.isElement(elem)) {
- return;
- }
-
- if (utils.isString(attr) && utils.isString(val)) {
- elem.style[attr] = val;
- } else if (utils.isObject(attr)) {
- utils.each(attr, function (key, val) {
- elem.style[key] = val;
- });
- }
- },
- removeElement: function removeElement(node) {
- if (!utils.isElement(node)) {
- return;
- }
-
- if (node.parentNode) {
- node.parentNode.removeChild(node);
- }
- },
- createWebWorker: function createWebWorker(content) {
- if (!utils.isString(content)) {
- return {};
- }
-
- try {
- var blob = new utils.Blob([content], {
- 'type': 'text/javascript'
- });
- var objectUrl = utils.URL.createObjectURL(blob);
- var worker = new Worker(objectUrl);
- return {
- 'objectUrl': objectUrl,
- 'worker': worker
- };
- } catch (e) {
- return '' + e;
- }
- },
- getExtension: function getExtension(src) {
- return src.substr(src.lastIndexOf('.') + 1, src.length);
- },
- getFontSize: function getFontSize() {
- var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
-
- if (!document.body || options.resizeFont === false) {
- return options.fontSize;
- }
-
- var text = options.text;
- var containerWidth = options.gifWidth;
- var fontSize = parseInt(options.fontSize, 10);
- var minFontSize = parseInt(options.minFontSize, 10);
- var div = document.createElement('div');
- var span = document.createElement('span');
- div.setAttribute('width', containerWidth);
- div.appendChild(span);
- span.innerHTML = text;
- span.style.fontSize = fontSize + 'px';
- span.style.textIndent = '-9999px';
- span.style.visibility = 'hidden';
- document.body.appendChild(span);
-
- while (span.offsetWidth > containerWidth && fontSize >= minFontSize) {
- span.style.fontSize = --fontSize + 'px';
- }
-
- document.body.removeChild(span);
- return fontSize + 'px';
- },
- webWorkerError: false
- };
- var utils$2 = Object.freeze({
- default: utils
- });
- /*
- error.js
- ========
- */
-
- /* Copyright 2017 Yahoo Inc.
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
- */
- // Dependencies
-
- var error = {
- validate: function validate(skipObj) {
- skipObj = utils.isObject(skipObj) ? skipObj : {};
- var errorObj = {};
- utils.each(error.validators, function (indece, currentValidator) {
- var errorCode = currentValidator.errorCode;
-
- if (!skipObj[errorCode] && !currentValidator.condition) {
- errorObj = currentValidator;
- errorObj.error = true;
- return false;
- }
- });
- delete errorObj.condition;
- return errorObj;
- },
- isValid: function isValid(skipObj) {
- var errorObj = error.validate(skipObj);
- var isValid = errorObj.error !== true ? true : false;
- return isValid;
- },
- validators: [{
- condition: utils.isFunction(utils.getUserMedia),
- errorCode: 'getUserMedia',
- errorMsg: 'The getUserMedia API is not supported in your browser'
- }, {
- condition: utils.isSupported.canvas(),
- errorCode: 'canvas',
- errorMsg: 'Canvas elements are not supported in your browser'
- }, {
- condition: utils.isSupported.webworkers(),
- errorCode: 'webworkers',
- errorMsg: 'The Web Workers API is not supported in your browser'
- }, {
- condition: utils.isFunction(utils.URL),
- errorCode: 'window.URL',
- errorMsg: 'The window.URL API is not supported in your browser'
- }, {
- condition: utils.isSupported.blob(),
- errorCode: 'window.Blob',
- errorMsg: 'The window.Blob File API is not supported in your browser'
- }, {
- condition: utils.isSupported.Uint8Array(),
- errorCode: 'window.Uint8Array',
- errorMsg: 'The window.Uint8Array function constructor is not supported in your browser'
- }, {
- condition: utils.isSupported.Uint32Array(),
- errorCode: 'window.Uint32Array',
- errorMsg: 'The window.Uint32Array function constructor is not supported in your browser'
- }],
- messages: {
- videoCodecs: {
- errorCode: 'videocodec',
- errorMsg: 'The video codec you are trying to use is not supported in your browser'
- }
- }
- };
- var error$2 = Object.freeze({
- default: error
- });
- /*
- defaultOptions.js
- =================
- */
-
- /* Copyright 2017 Yahoo Inc.
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
- */
- // Helpers
-
- var noop = function noop() {};
-
- var defaultOptions = {
- sampleInterval: 10,
- numWorkers: 2,
- filter: '',
- gifWidth: 200,
- gifHeight: 200,
- interval: 0.1,
- numFrames: 10,
- frameDuration: 1,
- keepCameraOn: false,
- images: [],
- video: null,
- webcamVideoElement: null,
- cameraStream: null,
- text: '',
- fontWeight: 'normal',
- fontSize: '16px',
- minFontSize: '10px',
- resizeFont: false,
- fontFamily: 'sans-serif',
- fontColor: '#ffffff',
- textAlign: 'center',
- textBaseline: 'bottom',
- textXCoordinate: null,
- textYCoordinate: null,
- progressCallback: noop,
- completeCallback: noop,
- saveRenderingContexts: false,
- savedRenderingContexts: [],
- crossOrigin: 'Anonymous'
- };
- var defaultOptions$2 = Object.freeze({
- default: defaultOptions
- });
- /*
- isSupported.js
- ==============
- */
-
- /* Copyright 2017 Yahoo Inc.
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
- */
- // Dependencies
-
- function isSupported() {
- return error.isValid();
- }
- /*
- isWebCamGIFSupported.js
- =======================
- */
-
- /* Copyright 2017 Yahoo Inc.
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
- */
-
-
- function isWebCamGIFSupported() {
- return error.isValid();
- }
- /*
- isSupported.js
- ==============
- */
-
- /* Copyright 2017 Yahoo Inc.
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
- */
- // Dependencies
-
-
- function isSupported$1() {
- var options = {
- getUserMedia: true
- };
- return error.isValid(options);
- }
- /*
- isExistingVideoGIFSupported.js
- ==============================
- */
-
- /* Copyright 2017 Yahoo Inc.
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
- */
- // Dependencies
-
-
- function isExistingVideoGIFSupported(codecs) {
- var hasValidCodec = false;
-
- if (utils.isArray(codecs) && codecs.length) {
- utils.each(codecs, function (indece, currentCodec) {
- if (utils.isSupported.videoCodecs[currentCodec]) {
- hasValidCodec = true;
- }
- });
-
- if (!hasValidCodec) {
- return false;
- }
- } else if (utils.isString(codecs) && codecs.length) {
- if (!utils.isSupported.videoCodecs[codecs]) {
- return false;
- }
- }
-
- return error.isValid({
- 'getUserMedia': true
- });
- }
- /*
- NeuQuant.js
- ===========
- */
-
- /*
- * NeuQuant Neural-Net Quantization Algorithm
- * ------------------------------------------
- *
- * Copyright (c) 1994 Anthony Dekker
- *
- * NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. See
- * "Kohonen neural networks for optimal colour quantization" in "Network:
- * Computation in Neural Systems" Vol. 5 (1994) pp 351-367. for a discussion of
- * the algorithm.
- *
- * Any party obtaining a copy of these files from the author, directly or
- * indirectly, is granted, free of charge, a full and unrestricted irrevocable,
- * world-wide, paid up, royalty-free, nonexclusive right and license to deal in
- * this software and documentation files (the "Software"), including without
- * limitation the rights to use, copy, modify, merge, publish, distribute,
- * sublicense, and/or sell copies of the Software, and to permit persons who
- * receive copies from any such party to do so, with the only requirement being
- * that this copyright notice remain intact.
- */
-
- /*
- * This class handles Neural-Net quantization algorithm
- * @author Kevin Weiner (original Java version - kweiner@fmsware.com)
- * @author Thibault Imbert (AS3 version - bytearray.org)
- * @version 0.1 AS3 implementation
- * @version 0.2 JS->AS3 "translation" by antimatter15
- * @version 0.3 JS clean up + using modern JS idioms by sole - http://soledadpenades.com
- * Also implement fix in color conversion described at http://stackoverflow.com/questions/16371712/neuquant-js-javascript-color-quantization-hidden-bug-in-js-conversion
- */
-
-
- function NeuQuant() {
- var netsize = 256; // number of colours used
- // four primes near 500 - assume no image has a length so large
- // that it is divisible by all four primes
-
- var prime1 = 499;
- var prime2 = 491;
- var prime3 = 487;
- var prime4 = 503; // minimum size for input image
-
- var minpicturebytes = 3 * prime4; // Network Definitions
-
- var maxnetpos = netsize - 1;
- var netbiasshift = 4; // bias for colour values
-
- var ncycles = 100; // no. of learning cycles
- // defs for freq and bias
-
- var intbiasshift = 16; // bias for fractions
-
- var intbias = 1 << intbiasshift;
- var gammashift = 10; // gamma = 1024
- var betashift = 10;
- var beta = intbias >> betashift; // beta = 1/1024
-
- var betagamma = intbias << gammashift - betashift; // defs for decreasing radius factor
- // For 256 colors, radius starts at 32.0 biased by 6 bits
- // and decreases by a factor of 1/30 each cycle
-
- var initrad = netsize >> 3;
- var radiusbiasshift = 6;
- var radiusbias = 1 << radiusbiasshift;
- var initradius = initrad * radiusbias;
- var radiusdec = 30; // defs for decreasing alpha factor
- // Alpha starts at 1.0 biased by 10 bits
-
- var alphabiasshift = 10;
- var initalpha = 1 << alphabiasshift;
- var alphadec; // radbias and alpharadbias used for radpower calculation
-
- var radbiasshift = 8;
- var radbias = 1 << radbiasshift;
- var alpharadbshift = alphabiasshift + radbiasshift;
- var alpharadbias = 1 << alpharadbshift; // Input image
-
- var thepicture; // Height * Width * 3
-
- var lengthcount; // Sampling factor 1..30
-
- var samplefac; // The network itself
-
- var network;
- var netindex = []; // for network lookup - really 256
-
- var bias = []; // bias and freq arrays for learning
-
- var freq = [];
- var radpower = [];
-
- function NeuQuantConstructor(thepic, len, sample) {
- var i;
- var p;
- thepicture = thepic;
- lengthcount = len;
- samplefac = sample;
- network = new Array(netsize);
-
- for (i = 0; i < netsize; i++) {
- network[i] = new Array(4);
- p = network[i];
- p[0] = p[1] = p[2] = (i << netbiasshift + 8) / netsize | 0;
- freq[i] = intbias / netsize | 0; // 1 / netsize
-
- bias[i] = 0;
- }
- }
-
- function colorMap() {
- var map = [];
- var index = new Array(netsize);
-
- for (var i = 0; i < netsize; i++) {
- index[network[i][3]] = i;
- }
-
- var k = 0;
-
- for (var l = 0; l < netsize; l++) {
- var j = index[l];
- map[k++] = network[j][0];
- map[k++] = network[j][1];
- map[k++] = network[j][2];
- }
-
- return map;
- } // Insertion sort of network and building of netindex[0..255]
- // (to do after unbias)
-
-
- function inxbuild() {
- var i;
- var j;
- var smallpos;
- var smallval;
- var p;
- var q;
- var previouscol;
- var startpos;
- previouscol = 0;
- startpos = 0;
-
- for (i = 0; i < netsize; i++) {
- p = network[i];
- smallpos = i;
- smallval = p[1]; // index on g
- // find smallest in i..netsize-1
-
- for (j = i + 1; j < netsize; j++) {
- q = network[j];
-
- if (q[1] < smallval) {
- // index on g
- smallpos = j;
- smallval = q[1]; // index on g
- }
- }
-
- q = network[smallpos]; // swap p (i) and q (smallpos) entries
-
- if (i != smallpos) {
- j = q[0];
- q[0] = p[0];
- p[0] = j;
- j = q[1];
- q[1] = p[1];
- p[1] = j;
- j = q[2];
- q[2] = p[2];
- p[2] = j;
- j = q[3];
- q[3] = p[3];
- p[3] = j;
- } // smallval entry is now in position i
-
-
- if (smallval != previouscol) {
- netindex[previouscol] = startpos + i >> 1;
-
- for (j = previouscol + 1; j < smallval; j++) {
- netindex[j] = i;
- }
-
- previouscol = smallval;
- startpos = i;
- }
- }
-
- netindex[previouscol] = startpos + maxnetpos >> 1;
-
- for (j = previouscol + 1; j < 256; j++) {
- netindex[j] = maxnetpos; // really 256
- }
- } // Main Learning Loop
-
-
- function learn() {
- var i;
- var j;
- var b;
- var g;
- var r;
- var radius;
- var rad;
- var alpha;
- var step;
- var delta;
- var samplepixels;
- var p;
- var pix;
- var lim;
-
- if (lengthcount < minpicturebytes) {
- samplefac = 1;
- }
-
- alphadec = 30 + (samplefac - 1) / 3;
- p = thepicture;
- pix = 0;
- lim = lengthcount;
- samplepixels = lengthcount / (3 * samplefac);
- delta = samplepixels / ncycles | 0;
- alpha = initalpha;
- radius = initradius;
- rad = radius >> radiusbiasshift;
-
- if (rad <= 1) {
- rad = 0;
- }
-
- for (i = 0; i < rad; i++) {
- radpower[i] = alpha * ((rad * rad - i * i) * radbias / (rad * rad));
- }
-
- if (lengthcount < minpicturebytes) {
- step = 3;
- } else if (lengthcount % prime1 !== 0) {
- step = 3 * prime1;
- } else {
- if (lengthcount % prime2 !== 0) {
- step = 3 * prime2;
- } else {
- if (lengthcount % prime3 !== 0) {
- step = 3 * prime3;
- } else {
- step = 3 * prime4;
- }
- }
- }
-
- i = 0;
-
- while (i < samplepixels) {
- b = (p[pix + 0] & 0xff) << netbiasshift;
- g = (p[pix + 1] & 0xff) << netbiasshift;
- r = (p[pix + 2] & 0xff) << netbiasshift;
- j = contest(b, g, r);
- altersingle(alpha, j, b, g, r);
-
- if (rad !== 0) {
- // Alter neighbours
- alterneigh(rad, j, b, g, r);
- }
-
- pix += step;
-
- if (pix >= lim) {
- pix -= lengthcount;
- }
-
- i++;
-
- if (delta === 0) {
- delta = 1;
- }
-
- if (i % delta === 0) {
- alpha -= alpha / alphadec;
- radius -= radius / radiusdec;
- rad = radius >> radiusbiasshift;
-
- if (rad <= 1) {
- rad = 0;
- }
-
- for (j = 0; j < rad; j++) {
- radpower[j] = alpha * ((rad * rad - j * j) * radbias / (rad * rad));
- }
- }
- }
- } // Search for BGR values 0..255 (after net is unbiased) and return colour index
-
-
- function map(b, g, r) {
- var i;
- var j;
- var dist;
- var a;
- var bestd;
- var p;
- var best; // Biggest possible distance is 256 * 3
-
- bestd = 1000;
- best = -1;
- i = netindex[g]; // index on g
-
- j = i - 1; // start at netindex[g] and work outwards
-
- while (i < netsize || j >= 0) {
- if (i < netsize) {
- p = network[i];
- dist = p[1] - g; // inx key
-
- if (dist >= bestd) {
- i = netsize; // stop iter
- } else {
- i++;
-
- if (dist < 0) {
- dist = -dist;
- }
-
- a = p[0] - b;
-
- if (a < 0) {
- a = -a;
- }
-
- dist += a;
-
- if (dist < bestd) {
- a = p[2] - r;
-
- if (a < 0) {
- a = -a;
- }
-
- dist += a;
-
- if (dist < bestd) {
- bestd = dist;
- best = p[3];
- }
- }
- }
- }
-
- if (j >= 0) {
- p = network[j];
- dist = g - p[1]; // inx key - reverse dif
-
- if (dist >= bestd) {
- j = -1; // stop iter
- } else {
- j--;
-
- if (dist < 0) {
- dist = -dist;
- }
-
- a = p[0] - b;
-
- if (a < 0) {
- a = -a;
- }
-
- dist += a;
-
- if (dist < bestd) {
- a = p[2] - r;
-
- if (a < 0) {
- a = -a;
- }
-
- dist += a;
-
- if (dist < bestd) {
- bestd = dist;
- best = p[3];
- }
- }
- }
- }
- }
-
- return best;
- }
-
- function process() {
- learn();
- unbiasnet();
- inxbuild();
- return colorMap();
- } // Unbias network to give byte values 0..255 and record position i
- // to prepare for sort
-
-
- function unbiasnet() {
- var i;
-
- for (i = 0; i < netsize; i++) {
- network[i][0] >>= netbiasshift;
- network[i][1] >>= netbiasshift;
- network[i][2] >>= netbiasshift;
- network[i][3] = i; // record colour no
- }
- } // Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2))
- // in radpower[|i-j|]
-
-
- function alterneigh(rad, i, b, g, r) {
- var j;
- var k;
- var lo;
- var hi;
- var a;
- var m;
- var p;
- lo = i - rad;
-
- if (lo < -1) {
- lo = -1;
- }
-
- hi = i + rad;
-
- if (hi > netsize) {
- hi = netsize;
- }
-
- j = i + 1;
- k = i - 1;
- m = 1;
-
- while (j < hi || k > lo) {
- a = radpower[m++];
-
- if (j < hi) {
- p = network[j++];
-
- try {
- p[0] -= a * (p[0] - b) / alpharadbias | 0;
- p[1] -= a * (p[1] - g) / alpharadbias | 0;
- p[2] -= a * (p[2] - r) / alpharadbias | 0;
- } catch (e) {}
- }
-
- if (k > lo) {
- p = network[k--];
-
- try {
- p[0] -= a * (p[0] - b) / alpharadbias | 0;
- p[1] -= a * (p[1] - g) / alpharadbias | 0;
- p[2] -= a * (p[2] - r) / alpharadbias | 0;
- } catch (e) {}
- }
- }
- } // Move neuron i towards biased (b,g,r) by factor alpha
-
-
- function altersingle(alpha, i, b, g, r) {
- // alter hit neuron
- var n = network[i];
- var alphaMult = alpha / initalpha;
- n[0] -= alphaMult * (n[0] - b) | 0;
- n[1] -= alphaMult * (n[1] - g) | 0;
- n[2] -= alphaMult * (n[2] - r) | 0;
- } // Search for biased BGR values
-
-
- function contest(b, g, r) {
- // finds closest neuron (min dist) and updates freq
- // finds best neuron (min dist-bias) and returns position
- // for frequently chosen neurons, freq[i] is high and bias[i] is negative
- // bias[i] = gamma*((1/netsize)-freq[i])
- var i;
- var dist;
- var a;
- var biasdist;
- var betafreq;
- var bestpos;
- var bestbiaspos;
- var bestd;
- var bestbiasd;
- var n;
- bestd = ~(1 << 31);
- bestbiasd = bestd;
- bestpos = -1;
- bestbiaspos = bestpos;
-
- for (i = 0; i < netsize; i++) {
- n = network[i];
- dist = n[0] - b;
-
- if (dist < 0) {
- dist = -dist;
- }
-
- a = n[1] - g;
-
- if (a < 0) {
- a = -a;
- }
-
- dist += a;
- a = n[2] - r;
-
- if (a < 0) {
- a = -a;
- }
-
- dist += a;
-
- if (dist < bestd) {
- bestd = dist;
- bestpos = i;
- }
-
- biasdist = dist - (bias[i] >> intbiasshift - netbiasshift);
-
- if (biasdist < bestbiasd) {
- bestbiasd = biasdist;
- bestbiaspos = i;
- }
-
- betafreq = freq[i] >> betashift;
- freq[i] -= betafreq;
- bias[i] += betafreq << gammashift;
- }
-
- freq[bestpos] += beta;
- bias[bestpos] -= betagamma;
- return bestbiaspos;
- }
-
- NeuQuantConstructor.apply(this, arguments);
- var exports = {};
- exports.map = map;
- exports.process = process;
- return exports;
- }
- /*
- processFrameWorker.js
- =====================
- */
-
- /* Copyright 2017 Yahoo Inc.
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
- */
-
-
- function workerCode() {
- var self = this;
-
- try {
- self.onmessage = function (ev) {
- var data = ev.data || {};
- var response;
-
- if (data.gifshot) {
- response = workerMethods.run(data);
- postMessage(response);
- }
- };
- } catch (e) {}
-
- var workerMethods = {
- dataToRGB: function dataToRGB(data, width, height) {
- var length = width * height * 4;
- var i = 0;
- var rgb = [];
-
- while (i < length) {
- rgb.push(data[i++]);
- rgb.push(data[i++]);
- rgb.push(data[i++]);
- i++; // for the alpha channel which we don't care about
- }
-
- return rgb;
- },
- componentizedPaletteToArray: function componentizedPaletteToArray(paletteRGB) {
- paletteRGB = paletteRGB || [];
- var paletteArray = [];
-
- for (var i = 0; i < paletteRGB.length; i += 3) {
- var r = paletteRGB[i];
- var g = paletteRGB[i + 1];
- var b = paletteRGB[i + 2];
- paletteArray.push(r << 16 | g << 8 | b);
- }
-
- return paletteArray;
- },
- // This is the "traditional" Animated_GIF style of going from RGBA to indexed color frames
- 'processFrameWithQuantizer': function processFrameWithQuantizer(imageData, width, height, sampleInterval) {
- var rgbComponents = this.dataToRGB(imageData, width, height);
- var nq = new NeuQuant(rgbComponents, rgbComponents.length, sampleInterval);
- var paletteRGB = nq.process();
- var paletteArray = new Uint32Array(this.componentizedPaletteToArray(paletteRGB));
- var numberPixels = width * height;
- var indexedPixels = new Uint8Array(numberPixels);
- var k = 0;
-
- for (var i = 0; i < numberPixels; i++) {
- var r = rgbComponents[k++];
- var g = rgbComponents[k++];
- var b = rgbComponents[k++];
- indexedPixels[i] = nq.map(r, g, b);
- }
-
- return {
- pixels: indexedPixels,
- palette: paletteArray
- };
- },
- 'run': function run(frame) {
- frame = frame || {};
- var _frame = frame,
- height = _frame.height,
- palette = _frame.palette,
- sampleInterval = _frame.sampleInterval,
- width = _frame.width;
- var imageData = frame.data;
- return this.processFrameWithQuantizer(imageData, width, height, sampleInterval);
- }
- };
- return workerMethods;
- }
- /*
- gifWriter.js
- ============
- */
- // (c) Dean McNamee , 2013.
- //
- // https://github.com/deanm/omggif
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //
- // omggif is a JavaScript implementation of a GIF 89a encoder and decoder,
- // including animation and compression. It does not rely on any specific
- // underlying system, so should run in the browser, Node, or Plask.
-
-
- function gifWriter(buf, width, height, gopts) {
- var p = 0;
- gopts = gopts === undefined ? {} : gopts;
- var loop_count = gopts.loop === undefined ? null : gopts.loop;
- var global_palette = gopts.palette === undefined ? null : gopts.palette;
- if (width <= 0 || height <= 0 || width > 65535 || height > 65535) throw "Width/Height invalid.";
-
- function check_palette_and_num_colors(palette) {
- var num_colors = palette.length;
- if (num_colors < 2 || num_colors > 256 || num_colors & num_colors - 1) throw "Invalid code/color length, must be power of 2 and 2 .. 256.";
- return num_colors;
- } // - Header.
-
-
- buf[p++] = 0x47;
- buf[p++] = 0x49;
- buf[p++] = 0x46; // GIF
-
- buf[p++] = 0x38;
- buf[p++] = 0x39;
- buf[p++] = 0x61; // 89a
- // Handling of Global Color Table (palette) and background index.
-
- var gp_num_colors_pow2 = 0;
- var background = 0; // - Logical Screen Descriptor.
- // NOTE(deanm): w/h apparently ignored by implementations, but set anyway.
-
- buf[p++] = width & 0xff;
- buf[p++] = width >> 8 & 0xff;
- buf[p++] = height & 0xff;
- buf[p++] = height >> 8 & 0xff; // NOTE: Indicates 0-bpp original color resolution (unused?).
-
- buf[p++] = (global_palette !== null ? 0x80 : 0) | // Global Color Table Flag.
- gp_num_colors_pow2; // NOTE: No sort flag (unused?).
-
- buf[p++] = background; // Background Color Index.
-
- buf[p++] = 0; // Pixel aspect ratio (unused?).
-
- if (loop_count !== null) {
- // Netscape block for looping.
- if (loop_count < 0 || loop_count > 65535) throw "Loop count invalid."; // Extension code, label, and length.
-
- buf[p++] = 0x21;
- buf[p++] = 0xff;
- buf[p++] = 0x0b; // NETSCAPE2.0
-
- buf[p++] = 0x4e;
- buf[p++] = 0x45;
- buf[p++] = 0x54;
- buf[p++] = 0x53;
- buf[p++] = 0x43;
- buf[p++] = 0x41;
- buf[p++] = 0x50;
- buf[p++] = 0x45;
- buf[p++] = 0x32;
- buf[p++] = 0x2e;
- buf[p++] = 0x30; // Sub-block
-
- buf[p++] = 0x03;
- buf[p++] = 0x01;
- buf[p++] = loop_count & 0xff;
- buf[p++] = loop_count >> 8 & 0xff;
- buf[p++] = 0x00; // Terminator.
- }
-
- var ended = false;
-
- this.addFrame = function (x, y, w, h, indexed_pixels, opts) {
- if (ended === true) {
- --p;
- ended = false;
- } // Un-end.
-
-
- opts = opts === undefined ? {} : opts; // TODO(deanm): Bounds check x, y. Do they need to be within the virtual
- // canvas width/height, I imagine?
-
- if (x < 0 || y < 0 || x > 65535 || y > 65535) throw "x/y invalid.";
- if (w <= 0 || h <= 0 || w > 65535 || h > 65535) throw "Width/Height invalid.";
- if (indexed_pixels.length < w * h) throw "Not enough pixels for the frame size.";
- var using_local_palette = true;
- var palette = opts.palette;
-
- if (palette === undefined || palette === null) {
- using_local_palette = false;
- palette = global_palette;
- }
-
- if (palette === undefined || palette === null) throw "Must supply either a local or global palette.";
- var num_colors = check_palette_and_num_colors(palette); // Compute the min_code_size (power of 2), destroying num_colors.
-
- var min_code_size = 0;
-
- while (num_colors >>= 1) {
- ++min_code_size;
- }
-
- num_colors = 1 << min_code_size; // Now we can easily get it back.
-
- var delay = opts.delay === undefined ? 0 : opts.delay; // From the spec:
- // 0 - No disposal specified. The decoder is
- // not required to take any action.
- // 1 - Do not dispose. The graphic is to be left
- // in place.
- // 2 - Restore to background color. The area used by the
- // graphic must be restored to the background color.
- // 3 - Restore to previous. The decoder is required to
- // restore the area overwritten by the graphic with
- // what was there prior to rendering the graphic.
- // 4-7 - To be defined.
- // NOTE(deanm): Dispose background doesn't really work, apparently most
- // browsers ignore the background palette index and clear to transparency.
-
- var disposal = opts.disposal === undefined ? 0 : opts.disposal;
- if (disposal < 0 || disposal > 3) // 4-7 is reserved.
- throw "Disposal out of range.";
- var use_transparency = false;
- var transparent_index = 0;
-
- if (opts.transparent !== undefined && opts.transparent !== null) {
- use_transparency = true;
- transparent_index = opts.transparent;
- if (transparent_index < 0 || transparent_index >= num_colors) throw "Transparent color index.";
- }
-
- if (disposal !== 0 || use_transparency || delay !== 0) {
- // - Graphics Control Extension
- buf[p++] = 0x21;
- buf[p++] = 0xf9; // Extension / Label.
-
- buf[p++] = 4; // Byte size.
-
- buf[p++] = disposal << 2 | (use_transparency === true ? 1 : 0);
- buf[p++] = delay & 0xff;
- buf[p++] = delay >> 8 & 0xff;
- buf[p++] = transparent_index; // Transparent color index.
-
- buf[p++] = 0; // Block Terminator.
- } // - Image Descriptor
-
-
- buf[p++] = 0x2c; // Image Seperator.
-
- buf[p++] = x & 0xff;
- buf[p++] = x >> 8 & 0xff; // Left.
-
- buf[p++] = y & 0xff;
- buf[p++] = y >> 8 & 0xff; // Top.
-
- buf[p++] = w & 0xff;
- buf[p++] = w >> 8 & 0xff;
- buf[p++] = h & 0xff;
- buf[p++] = h >> 8 & 0xff; // NOTE: No sort flag (unused?).
- // TODO(deanm): Support interlace.
-
- buf[p++] = using_local_palette === true ? 0x80 | min_code_size - 1 : 0; // - Local Color Table
-
- if (using_local_palette === true) {
- for (var i = 0, il = palette.length; i < il; ++i) {
- var rgb = palette[i];
- buf[p++] = rgb >> 16 & 0xff;
- buf[p++] = rgb >> 8 & 0xff;
- buf[p++] = rgb & 0xff;
- }
- }
-
- p = GifWriterOutputLZWCodeStream(buf, p, min_code_size < 2 ? 2 : min_code_size, indexed_pixels);
- };
-
- this.end = function () {
- if (ended === false) {
- buf[p++] = 0x3b; // Trailer.
-
- ended = true;
- }
-
- return p;
- }; // Main compression routine, palette indexes -> LZW code stream.
- // |index_stream| must have at least one entry.
-
-
- function GifWriterOutputLZWCodeStream(buf, p, min_code_size, index_stream) {
- buf[p++] = min_code_size;
- var cur_subblock = p++; // Pointing at the length field.
-
- var clear_code = 1 << min_code_size;
- var code_mask = clear_code - 1;
- var eoi_code = clear_code + 1;
- var next_code = eoi_code + 1;
- var cur_code_size = min_code_size + 1; // Number of bits per code.
-
- var cur_shift = 0; // We have at most 12-bit codes, so we should have to hold a max of 19
- // bits here (and then we would write out).
-
- var cur = 0;
-
- function emit_bytes_to_buffer(bit_block_size) {
- while (cur_shift >= bit_block_size) {
- buf[p++] = cur & 0xff;
- cur >>= 8;
- cur_shift -= 8;
-
- if (p === cur_subblock + 256) {
- // Finished a subblock.
- buf[cur_subblock] = 255;
- cur_subblock = p++;
- }
- }
- }
-
- function emit_code(c) {
- cur |= c << cur_shift;
- cur_shift += cur_code_size;
- emit_bytes_to_buffer(8);
- } // I am not an expert on the topic, and I don't want to write a thesis.
- // However, it is good to outline here the basic algorithm and the few data
- // structures and optimizations here that make this implementation fast.
- // The basic idea behind LZW is to build a table of previously seen runs
- // addressed by a short id (herein called output code). All data is
- // referenced by a code, which represents one or more values from the
- // original input stream. All input bytes can be referenced as the same
- // value as an output code. So if you didn't want any compression, you
- // could more or less just output the original bytes as codes (there are
- // some details to this, but it is the idea). In order to achieve
- // compression, values greater then the input range (codes can be up to
- // 12-bit while input only 8-bit) represent a sequence of previously seen
- // inputs. The decompressor is able to build the same mapping while
- // decoding, so there is always a shared common knowledge between the
- // encoding and decoder, which is also important for "timing" aspects like
- // how to handle variable bit width code encoding.
- //
- // One obvious but very important consequence of the table system is there
- // is always a unique id (at most 12-bits) to map the runs. 'A' might be
- // 4, then 'AA' might be 10, 'AAA' 11, 'AAAA' 12, etc. This relationship
- // can be used for an effecient lookup strategy for the code mapping. We
- // need to know if a run has been seen before, and be able to map that run
- // to the output code. Since we start with known unique ids (input bytes),
- // and then from those build more unique ids (table entries), we can
- // continue this chain (almost like a linked list) to always have small
- // integer values that represent the current byte chains in the encoder.
- // This means instead of tracking the input bytes (AAAABCD) to know our
- // current state, we can track the table entry for AAAABC (it is guaranteed
- // to exist by the nature of the algorithm) and the next character D.
- // Therefor the tuple of (table_entry, byte) is guaranteed to also be
- // unique. This allows us to create a simple lookup key for mapping input
- // sequences to codes (table indices) without having to store or search
- // any of the code sequences. So if 'AAAA' has a table entry of 12, the
- // tuple of ('AAAA', K) for any input byte K will be unique, and can be our
- // key. This leads to a integer value at most 20-bits, which can always
- // fit in an SMI value and be used as a fast sparse array / object key.
- // Output code for the current contents of the index buffer.
-
-
- var ib_code = index_stream[0] & code_mask; // Load first input index.
-
- var code_table = {}; // Key'd on our 20-bit "tuple".
-
- emit_code(clear_code); // Spec says first code should be a clear code.
- // First index already loaded, process the rest of the stream.
-
- for (var i = 1, il = index_stream.length; i < il; ++i) {
- var k = index_stream[i] & code_mask;
- var cur_key = ib_code << 8 | k; // (prev, k) unique tuple.
-
- var cur_code = code_table[cur_key]; // buffer + k.
- // Check if we have to create a new code table entry.
-
- if (cur_code === undefined) {
- // We don't have buffer + k.
- // Emit index buffer (without k).
- // This is an inline version of emit_code, because this is the core
- // writing routine of the compressor (and V8 cannot inline emit_code
- // because it is a closure here in a different context). Additionally
- // we can call emit_byte_to_buffer less often, because we can have
- // 30-bits (from our 31-bit signed SMI), and we know our codes will only
- // be 12-bits, so can safely have 18-bits there without overflow.
- // emit_code(ib_code);
- cur |= ib_code << cur_shift;
- cur_shift += cur_code_size;
-
- while (cur_shift >= 8) {
- buf[p++] = cur & 0xff;
- cur >>= 8;
- cur_shift -= 8;
-
- if (p === cur_subblock + 256) {
- // Finished a subblock.
- buf[cur_subblock] = 255;
- cur_subblock = p++;
- }
- }
-
- if (next_code === 4096) {
- // Table full, need a clear.
- emit_code(clear_code);
- next_code = eoi_code + 1;
- cur_code_size = min_code_size + 1;
- code_table = {};
- } else {
- // Table not full, insert a new entry.
- // Increase our variable bit code sizes if necessary. This is a bit
- // tricky as it is based on "timing" between the encoding and
- // decoder. From the encoders perspective this should happen after
- // we've already emitted the index buffer and are about to create the
- // first table entry that would overflow our current code bit size.
- if (next_code >= 1 << cur_code_size) ++cur_code_size;
- code_table[cur_key] = next_code++; // Insert into code table.
- }
-
- ib_code = k; // Index buffer to single input k.
- } else {
- ib_code = cur_code; // Index buffer to sequence in code table.
- }
- }
-
- emit_code(ib_code); // There will still be something in the index buffer.
-
- emit_code(eoi_code); // End Of Information.
- // Flush / finalize the sub-blocks stream to the buffer.
-
- emit_bytes_to_buffer(1); // Finish the sub-blocks, writing out any unfinished lengths and
- // terminating with a sub-block of length 0. If we have already started
- // but not yet used a sub-block it can just become the terminator.
-
- if (cur_subblock + 1 === p) {
- // Started but unused.
- buf[cur_subblock] = 0;
- } else {
- // Started and used, write length and additional terminator block.
- buf[cur_subblock] = p - cur_subblock - 1;
- buf[p++] = 0;
- }
-
- return p;
- }
- }
- /*
- animatedGIF.js
- ==============
- */
-
- /* Copyright 2017 Yahoo Inc.
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
- */
- // Dependencies
- // Helpers
-
-
- var noop$2 = function noop() {};
-
- var AnimatedGIF = function AnimatedGIF(options) {
- this.canvas = null;
- this.ctx = null;
- this.repeat = 0;
- this.frames = [];
- this.numRenderedFrames = 0;
- this.onRenderCompleteCallback = noop$2;
- this.onRenderProgressCallback = noop$2;
- this.workers = [];
- this.availableWorkers = [];
- this.generatingGIF = false;
- this.options = options; // Constructs and initializes the the web workers appropriately
-
- this.initializeWebWorkers(options);
- };
-
- AnimatedGIF.prototype = {
- 'workerMethods': workerCode(),
- 'initializeWebWorkers': function initializeWebWorkers(options) {
- var self = this;
- var processFrameWorkerCode = NeuQuant.toString() + '(' + workerCode.toString() + '());';
- var webWorkerObj = void 0;
- var objectUrl = void 0;
- var webWorker = void 0;
- var numWorkers = void 0;
- var x = -1;
- var workerError = '';
- numWorkers = options.numWorkers;
-
- while (++x < numWorkers) {
- webWorkerObj = utils.createWebWorker(processFrameWorkerCode);
-
- if (utils.isObject(webWorkerObj)) {
- objectUrl = webWorkerObj.objectUrl;
- webWorker = webWorkerObj.worker;
- self.workers.push({
- worker: webWorker,
- objectUrl: objectUrl
- });
- self.availableWorkers.push(webWorker);
- } else {
- workerError = webWorkerObj;
- utils.webWorkerError = !!webWorkerObj;
- }
- }
-
- this.workerError = workerError;
- this.canvas = document.createElement('canvas');
- this.canvas.width = options.gifWidth;
- this.canvas.height = options.gifHeight;
- this.ctx = this.canvas.getContext('2d');
- this.frames = [];
- },
- // Return a worker for processing a frame
- getWorker: function getWorker() {
- return this.availableWorkers.pop();
- },
- // Restores a worker to the pool
- freeWorker: function freeWorker(worker) {
- this.availableWorkers.push(worker);
- },
- byteMap: function () {
- var byteMap = [];
-
- for (var i = 0; i < 256; i++) {
- byteMap[i] = String.fromCharCode(i);
- }
-
- return byteMap;
- }(),
- bufferToString: function bufferToString(buffer) {
- var numberValues = buffer.length;
- var str = '';
- var x = -1;
-
- while (++x < numberValues) {
- str += this.byteMap[buffer[x]];
- }
-
- return str;
- },
- onFrameFinished: function onFrameFinished(progressCallback) {
- // The GIF is not written until we're done with all the frames
- // because they might not be processed in the same order
- var self = this;
- var frames = self.frames;
- var options = self.options;
- var hasExistingImages = !!(options.images || []).length;
- var allDone = frames.every(function (frame) {
- return !frame.beingProcessed && frame.done;
- });
- self.numRenderedFrames++;
-
- if (hasExistingImages) {
- progressCallback(self.numRenderedFrames / frames.length);
- }
-
- self.onRenderProgressCallback(self.numRenderedFrames * 0.75 / frames.length);
-
- if (allDone) {
- if (!self.generatingGIF) {
- self.generateGIF(frames, self.onRenderCompleteCallback);
- }
- } else {
- utils.requestTimeout(function () {
- self.processNextFrame();
- }, 1);
- }
- },
- processFrame: function processFrame(position) {
- var AnimatedGifContext = this;
- var options = this.options;
- var _options = this.options,
- progressCallback = _options.progressCallback,
- sampleInterval = _options.sampleInterval;
- var frames = this.frames;
- var frame = void 0;
- var worker = void 0;
-
- var done = function done() {
- var ev = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
- var data = ev.data; // Delete original data, and free memory
-
- delete frame.data;
- frame.pixels = Array.prototype.slice.call(data.pixels);
- frame.palette = Array.prototype.slice.call(data.palette);
- frame.done = true;
- frame.beingProcessed = false;
- AnimatedGifContext.freeWorker(worker);
- AnimatedGifContext.onFrameFinished(progressCallback);
- };
-
- frame = frames[position];
-
- if (frame.beingProcessed || frame.done) {
- this.onFrameFinished();
- return;
- }
-
- frame.sampleInterval = sampleInterval;
- frame.beingProcessed = true;
- frame.gifshot = true;
- worker = this.getWorker();
-
- if (worker) {
- // Process the frame in a web worker
- worker.onmessage = done;
- worker.postMessage(frame);
- } else {
- // Process the frame in the current thread
- done({
- 'data': AnimatedGifContext.workerMethods.run(frame)
- });
- }
- },
- startRendering: function startRendering(completeCallback) {
- this.onRenderCompleteCallback = completeCallback;
-
- for (var i = 0; i < this.options.numWorkers && i < this.frames.length; i++) {
- this.processFrame(i);
- }
- },
- processNextFrame: function processNextFrame() {
- var position = -1;
-
- for (var i = 0; i < this.frames.length; i++) {
- var frame = this.frames[i];
-
- if (!frame.done && !frame.beingProcessed) {
- position = i;
- break;
- }
- }
-
- if (position >= 0) {
- this.processFrame(position);
- }
- },
- // Takes the already processed data in frames and feeds it to a new
- // GifWriter instance in order to get the binary GIF file
- generateGIF: function generateGIF(frames, callback) {
- // TODO: Weird: using a simple JS array instead of a typed array,
- // the files are WAY smaller o_o. Patches/explanations welcome!
- var buffer = []; // new Uint8Array(width * height * frames.length * 5);
-
- var gifOptions = {
- loop: this.repeat
- };
- var options = this.options;
- var interval = options.interval;
- var frameDuration = options.frameDuration;
- var existingImages = options.images;
- var hasExistingImages = !!existingImages.length;
- var height = options.gifHeight;
- var width = options.gifWidth;
- var gifWriter$$1 = new gifWriter(buffer, width, height, gifOptions);
- var onRenderProgressCallback = this.onRenderProgressCallback;
- var delay = hasExistingImages ? interval * 100 : 0;
- var bufferToString = void 0;
- var gif = void 0;
- this.generatingGIF = true;
- utils.each(frames, function (iterator, frame) {
- var framePalette = frame.palette;
- onRenderProgressCallback(0.75 + 0.25 * frame.position * 1.0 / frames.length);
-
- for (var i = 0; i < frameDuration; i++) {
- gifWriter$$1.addFrame(0, 0, width, height, frame.pixels, {
- palette: framePalette,
- delay: delay
- });
- }
- });
- gifWriter$$1.end();
- onRenderProgressCallback(1.0);
- this.frames = [];
- this.generatingGIF = false;
-
- if (utils.isFunction(callback)) {
- bufferToString = this.bufferToString(buffer);
- gif = 'data:image/gif;base64,' + utils.btoa(bufferToString);
- callback(gif);
- }
- },
- // From GIF: 0 = loop forever, null = not looping, n > 0 = loop n times and stop
- setRepeat: function setRepeat(r) {
- this.repeat = r;
- },
- addFrame: function addFrame(element, gifshotOptions) {
- gifshotOptions = utils.isObject(gifshotOptions) ? gifshotOptions : {};
- var self = this;
- var ctx = self.ctx;
- var options = self.options;
- var width = options.gifWidth;
- var height = options.gifHeight;
- var fontSize = utils.getFontSize(gifshotOptions);
- var _gifshotOptions = gifshotOptions,
- filter = _gifshotOptions.filter,
- fontColor = _gifshotOptions.fontColor,
- fontFamily = _gifshotOptions.fontFamily,
- fontWeight = _gifshotOptions.fontWeight,
- gifHeight = _gifshotOptions.gifHeight,
- gifWidth = _gifshotOptions.gifWidth,
- text = _gifshotOptions.text,
- textAlign = _gifshotOptions.textAlign,
- textBaseline = _gifshotOptions.textBaseline;
- var textXCoordinate = gifshotOptions.textXCoordinate ? gifshotOptions.textXCoordinate : textAlign === 'left' ? 1 : textAlign === 'right' ? width : width / 2;
- var textYCoordinate = gifshotOptions.textYCoordinate ? gifshotOptions.textYCoordinate : textBaseline === 'top' ? 1 : textBaseline === 'center' ? height / 2 : height;
- var font = fontWeight + ' ' + fontSize + ' ' + fontFamily;
- var imageData = void 0;
-
- try {
- ctx.filter = filter;
- ctx.drawImage(element, 0, 0, width, height);
-
- if (text) {
- ctx.font = font;
- ctx.fillStyle = fontColor;
- ctx.textAlign = textAlign;
- ctx.textBaseline = textBaseline;
- ctx.fillText(text, textXCoordinate, textYCoordinate);
- }
-
- imageData = ctx.getImageData(0, 0, width, height);
- self.addFrameImageData(imageData);
- } catch (e) {
- return '' + e;
- }
- },
- addFrameImageData: function addFrameImageData() {
- var imageData = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
- var frames = this.frames;
- var imageDataArray = imageData.data;
- this.frames.push({
- 'data': imageDataArray,
- 'width': imageData.width,
- 'height': imageData.height,
- 'palette': null,
- 'dithering': null,
- 'done': false,
- 'beingProcessed': false,
- 'position': frames.length
- });
- },
- onRenderProgress: function onRenderProgress(callback) {
- this.onRenderProgressCallback = callback;
- },
- isRendering: function isRendering() {
- return this.generatingGIF;
- },
- getBase64GIF: function getBase64GIF(completeCallback) {
- var self = this;
-
- var onRenderComplete = function onRenderComplete(gif) {
- self.destroyWorkers();
- utils.requestTimeout(function () {
- completeCallback(gif);
- }, 0);
- };
-
- self.startRendering(onRenderComplete);
- },
- destroyWorkers: function destroyWorkers() {
- if (this.workerError) {
- return;
- }
-
- var workers = this.workers; // Explicitly ask web workers to die so they are explicitly GC'ed
-
- utils.each(workers, function (iterator, workerObj) {
- var worker = workerObj.worker;
- var objectUrl = workerObj.objectUrl;
- worker.terminate();
- utils.URL.revokeObjectURL(objectUrl);
- });
- }
- };
- /*
- getBase64GIF.js
- ===============
- */
-
- /* Copyright 2017 Yahoo Inc.
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
- */
-
- function getBase64GIF(animatedGifInstance, callback) {
- // This is asynchronous, rendered with WebWorkers
- animatedGifInstance.getBase64GIF(function (image) {
- callback({
- error: false,
- errorCode: '',
- errorMsg: '',
- image: image
- });
- });
- }
- /*
- existingImages.js
- =================
- */
-
- /* Copyright 2017 Yahoo Inc.
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
- */
-
-
- function existingImages() {
- var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
- var callback = obj.callback,
- images = obj.images,
- options = obj.options;
- var imagesLength = obj.imagesLength;
- var skipObj = {
- 'getUserMedia': true,
- 'window.URL': true
- };
- var errorObj = error.validate(skipObj);
- var loadedImages = [];
- var loadedImagesLength = 0;
- var tempImage = void 0;
- var ag = void 0;
-
- if (errorObj.error) {
- return callback(errorObj);
- } // change workerPath to point to where Animated_GIF.worker.js is
-
-
- ag = new AnimatedGIF(options);
- utils.each(images, function (index, image) {
- var currentImage = image;
-
- if (image.src) {
- currentImage = currentImage.src;
- }
-
- if (utils.isElement(currentImage)) {
- if (options.crossOrigin) {
- currentImage.crossOrigin = options.crossOrigin;
- }
-
- loadedImages[index] = currentImage;
- loadedImagesLength += 1;
-
- if (loadedImagesLength === imagesLength) {
- addLoadedImagesToGif();
- }
- } else if (utils.isString(currentImage)) {
- tempImage = new Image();
-
- if (options.crossOrigin) {
- tempImage.crossOrigin = options.crossOrigin;
- }
-
- (function (tempImage) {
- if (image.text) {
- tempImage.text = image.text;
- }
-
- tempImage.onerror = function (e) {
- var obj = void 0;
- --imagesLength; // skips over images that error out
-
- if (imagesLength === 0) {
- obj = {};
- obj.error = 'None of the requested images was capable of being retrieved';
- return callback(obj);
- }
- };
-
- tempImage.onload = function (e) {
- if (image.text) {
- loadedImages[index] = {
- img: tempImage,
- text: tempImage.text
- };
- } else {
- loadedImages[index] = tempImage;
- }
-
- loadedImagesLength += 1;
-
- if (loadedImagesLength === imagesLength) {
- addLoadedImagesToGif();
- }
-
- utils.removeElement(tempImage);
- };
-
- tempImage.src = currentImage;
- })(tempImage);
-
- utils.setCSSAttr(tempImage, {
- position: 'fixed',
- opacity: '0'
- });
- document.body.appendChild(tempImage);
- }
- });
-
- function addLoadedImagesToGif() {
- utils.each(loadedImages, function (index, loadedImage) {
- if (loadedImage) {
- if (loadedImage.text) {
- ag.addFrame(loadedImage.img, options, loadedImage.text);
- } else {
- ag.addFrame(loadedImage, options);
- }
- }
- });
- getBase64GIF(ag, callback);
- }
- }
- /*
- screenShot.js
- =============
- */
-
- /* Copyright 2017 Yahoo Inc.
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
- */
- // Dependencies
- // Helpers
-
-
- var noop$3 = function noop() {};
-
- var screenShot = {
- getGIF: function getGIF() {
- var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
- var callback = arguments[1];
- callback = utils.isFunction(callback) ? callback : noop$3;
- var canvas = document.createElement('canvas');
- var context = void 0;
- var existingImages = options.images;
- var hasExistingImages = !!existingImages.length;
- var cameraStream = options.cameraStream,
- crop = options.crop,
- filter = options.filter,
- fontColor = options.fontColor,
- fontFamily = options.fontFamily,
- fontWeight = options.fontWeight,
- keepCameraOn = options.keepCameraOn,
- numWorkers = options.numWorkers,
- progressCallback = options.progressCallback,
- saveRenderingContexts = options.saveRenderingContexts,
- savedRenderingContexts = options.savedRenderingContexts,
- text = options.text,
- textAlign = options.textAlign,
- textBaseline = options.textBaseline,
- videoElement = options.videoElement,
- videoHeight = options.videoHeight,
- videoWidth = options.videoWidth,
- webcamVideoElement = options.webcamVideoElement;
- var gifWidth = Number(options.gifWidth);
- var gifHeight = Number(options.gifHeight);
- var interval = Number(options.interval);
- var sampleInterval = Number(options.sampleInterval);
- var waitBetweenFrames = hasExistingImages ? 0 : interval * 1000;
- var renderingContextsToSave = [];
- var numFrames = savedRenderingContexts.length ? savedRenderingContexts.length : options.numFrames;
- var pendingFrames = numFrames;
- var ag = new AnimatedGIF(options);
- var fontSize = utils.getFontSize(options);
- var textXCoordinate = options.textXCoordinate ? options.textXCoordinate : textAlign === 'left' ? 1 : textAlign === 'right' ? gifWidth : gifWidth / 2;
- var textYCoordinate = options.textYCoordinate ? options.textYCoordinate : textBaseline === 'top' ? 1 : textBaseline === 'center' ? gifHeight / 2 : gifHeight;
- var font = fontWeight + ' ' + fontSize + ' ' + fontFamily;
- var sourceX = crop ? Math.floor(crop.scaledWidth / 2) : 0;
- var sourceWidth = crop ? videoWidth - crop.scaledWidth : 0;
- var sourceY = crop ? Math.floor(crop.scaledHeight / 2) : 0;
- var sourceHeight = crop ? videoHeight - crop.scaledHeight : 0;
-
- var captureFrames = function captureSingleFrame() {
- var framesLeft = pendingFrames - 1;
-
- if (savedRenderingContexts.length) {
- context.putImageData(savedRenderingContexts[numFrames - pendingFrames], 0, 0);
- finishCapture();
- } else {
- drawVideo();
- }
-
- function drawVideo() {
- try {
- // Makes sure the canvas video heights/widths are in bounds
- if (sourceWidth > videoWidth) {
- sourceWidth = videoWidth;
- }
-
- if (sourceHeight > videoHeight) {
- sourceHeight = videoHeight;
- }
-
- if (sourceX < 0) {
- sourceX = 0;
- }
-
- if (sourceY < 0) {
- sourceY = 0;
- }
-
- context.filter = filter;
- context.drawImage(videoElement, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, gifWidth, gifHeight);
- finishCapture();
- } catch (e) {
- // There is a Firefox bug that sometimes throws NS_ERROR_NOT_AVAILABLE and
- // and IndexSizeError errors when drawing a video element to the canvas
- if (e.name === 'NS_ERROR_NOT_AVAILABLE') {
- // Wait 100ms before trying again
- utils.requestTimeout(drawVideo, 100);
- } else {
- throw e;
- }
- }
- }
-
- function finishCapture() {
- var imageData = void 0;
-
- if (saveRenderingContexts) {
- renderingContextsToSave.push(context.getImageData(0, 0, gifWidth, gifHeight));
- } // If there is text to display, make sure to display it on the canvas after the image is drawn
-
-
- if (text) {
- context.font = font;
- context.fillStyle = fontColor;
- context.textAlign = textAlign;
- context.textBaseline = textBaseline;
- context.fillText(text, textXCoordinate, textYCoordinate);
- }
-
- imageData = context.getImageData(0, 0, gifWidth, gifHeight);
- ag.addFrameImageData(imageData);
- pendingFrames = framesLeft; // Call back with an r value indicating how far along we are in capture
-
- progressCallback((numFrames - pendingFrames) / numFrames);
-
- if (framesLeft > 0) {
- // test
- utils.requestTimeout(captureSingleFrame, waitBetweenFrames);
- }
-
- if (!pendingFrames) {
- ag.getBase64GIF(function (image) {
- callback({
- 'error': false,
- 'errorCode': '',
- 'errorMsg': '',
- 'image': image,
- 'cameraStream': cameraStream,
- 'videoElement': videoElement,
- 'webcamVideoElement': webcamVideoElement,
- 'savedRenderingContexts': renderingContextsToSave,
- 'keepCameraOn': keepCameraOn
- });
- });
- }
- }
- };
-
- numFrames = numFrames !== undefined ? numFrames : 10;
- interval = interval !== undefined ? interval : 0.1; // In seconds
-
- canvas.width = gifWidth;
- canvas.height = gifHeight;
- context = canvas.getContext('2d');
-
- (function capture() {
- if (!savedRenderingContexts.length && videoElement.currentTime === 0) {
- utils.requestTimeout(capture, 100);
- return;
- }
-
- captureFrames();
- })();
- },
- getCropDimensions: function getCropDimensions() {
- var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
- var width = obj.videoWidth;
- var height = obj.videoHeight;
- var gifWidth = obj.gifWidth;
- var gifHeight = obj.gifHeight;
- var result = {
- width: 0,
- height: 0,
- scaledWidth: 0,
- scaledHeight: 0
- };
-
- if (width > height) {
- result.width = Math.round(width * (gifHeight / height)) - gifWidth;
- result.scaledWidth = Math.round(result.width * (height / gifHeight));
- } else {
- result.height = Math.round(height * (gifWidth / width)) - gifHeight;
- result.scaledHeight = Math.round(result.height * (width / gifWidth));
- }
-
- return result;
- }
- };
- /*
- videoStream.js
- ==============
- */
-
- /* Copyright 2017 Yahoo Inc.
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
- */
- // Dependencies
-
- var videoStream = {
- loadedData: false,
- defaultVideoDimensions: {
- width: 640,
- height: 480
- },
- findVideoSize: function findVideoSizeMethod(obj) {
- findVideoSizeMethod.attempts = findVideoSizeMethod.attempts || 0;
- var cameraStream = obj.cameraStream,
- completedCallback = obj.completedCallback,
- videoElement = obj.videoElement;
-
- if (!videoElement) {
- return;
- }
-
- if (videoElement.videoWidth > 0 && videoElement.videoHeight > 0) {
- videoElement.removeEventListener('loadeddata', videoStream.findVideoSize);
- completedCallback({
- videoElement: videoElement,
- cameraStream: cameraStream,
- videoWidth: videoElement.videoWidth,
- videoHeight: videoElement.videoHeight
- });
- } else {
- if (findVideoSizeMethod.attempts < 10) {
- findVideoSizeMethod.attempts += 1;
- utils.requestTimeout(function () {
- videoStream.findVideoSize(obj);
- }, 400);
- } else {
- completedCallback({
- videoElement: videoElement,
- cameraStream: cameraStream,
- videoWidth: videoStream.defaultVideoDimensions.width,
- videoHeight: videoStream.defaultVideoDimensions.height
- });
- }
- }
- },
- onStreamingTimeout: function onStreamingTimeout(callback) {
- if (utils.isFunction(callback)) {
- callback({
- error: true,
- errorCode: 'getUserMedia',
- errorMsg: 'There was an issue with the getUserMedia API - Timed out while trying to start streaming',
- image: null,
- cameraStream: {}
- });
- }
- },
- stream: function stream(obj) {
- var existingVideo = utils.isArray(obj.existingVideo) ? obj.existingVideo[0] : obj.existingVideo;
- var cameraStream = obj.cameraStream,
- completedCallback = obj.completedCallback,
- streamedCallback = obj.streamedCallback,
- videoElement = obj.videoElement;
-
- if (utils.isFunction(streamedCallback)) {
- streamedCallback();
- }
-
- if (existingVideo) {
- if (utils.isString(existingVideo)) {
- videoElement.src = existingVideo;
- videoElement.innerHTML = '';
- } else if (existingVideo instanceof Blob) {
- try {
- videoElement.src = utils.URL.createObjectURL(existingVideo);
- } catch (e) {}
-
- videoElement.innerHTML = '';
- }
- } else if (videoElement.mozSrcObject) {
- videoElement.mozSrcObject = cameraStream;
- } else if (utils.URL) {
- try {
- videoElement.srcObject = cameraStream;
- videoElement.src = utils.URL.createObjectURL(cameraStream);
- } catch (e) {
- videoElement.srcObject = cameraStream;
- }
- }
-
- videoElement.play();
- utils.requestTimeout(function checkLoadedData() {
- checkLoadedData.count = checkLoadedData.count || 0;
-
- if (videoStream.loadedData === true) {
- videoStream.findVideoSize({
- videoElement: videoElement,
- cameraStream: cameraStream,
- completedCallback: completedCallback
- });
- videoStream.loadedData = false;
- } else {
- checkLoadedData.count += 1;
-
- if (checkLoadedData.count > 10) {
- videoStream.findVideoSize({
- videoElement: videoElement,
- cameraStream: cameraStream,
- completedCallback: completedCallback
- });
- } else {
- checkLoadedData();
- }
- }
- }, 0);
- },
- startStreaming: function startStreaming(obj) {
- var errorCallback = utils.isFunction(obj.error) ? obj.error : utils.noop;
- var streamedCallback = utils.isFunction(obj.streamed) ? obj.streamed : utils.noop;
- var completedCallback = utils.isFunction(obj.completed) ? obj.completed : utils.noop;
- var crossOrigin = obj.crossOrigin,
- existingVideo = obj.existingVideo,
- lastCameraStream = obj.lastCameraStream,
- options = obj.options,
- webcamVideoElement = obj.webcamVideoElement;
- var videoElement = utils.isElement(existingVideo) ? existingVideo : webcamVideoElement ? webcamVideoElement : document.createElement('video');
-
- if (crossOrigin) {
- videoElement.crossOrigin = options.crossOrigin;
- }
-
- videoElement.autoplay = true;
- videoElement.loop = true;
- videoElement.muted = true;
- videoElement.addEventListener('loadeddata', function (event) {
- videoStream.loadedData = true;
-
- if (options.offset) {
- videoElement.currentTime = options.offset;
- }
- });
-
- if (existingVideo) {
- videoStream.stream({
- videoElement: videoElement,
- existingVideo: existingVideo,
- completedCallback: completedCallback
- });
- } else if (lastCameraStream) {
- videoStream.stream({
- videoElement: videoElement,
- cameraStream: lastCameraStream,
- streamedCallback: streamedCallback,
- completedCallback: completedCallback
- });
- } else {
- utils.getUserMedia({
- video: true
- }, function (stream) {
- videoStream.stream({
- videoElement: videoElement,
- cameraStream: stream,
- streamedCallback: streamedCallback,
- completedCallback: completedCallback
- });
- }, errorCallback);
- }
- },
- startVideoStreaming: function startVideoStreaming(callback) {
- var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
- var timeoutLength = options.timeout !== undefined ? options.timeout : 0;
- var originalCallback = options.callback;
- var webcamVideoElement = options.webcamVideoElement;
- var noGetUserMediaSupportTimeout = void 0; // Some browsers apparently have support for video streaming because of the
- // presence of the getUserMedia function, but then do not answer our
- // calls for streaming.
- // So we'll set up this timeout and if nothing happens after a while, we'll
- // conclude that there's no actual getUserMedia support.
-
- if (timeoutLength > 0) {
- noGetUserMediaSupportTimeout = utils.requestTimeout(function () {
- videoStream.onStreamingTimeout(originalCallback);
- }, 10000);
- }
-
- videoStream.startStreaming({
- error: function error() {
- originalCallback({
- error: true,
- errorCode: 'getUserMedia',
- errorMsg: 'There was an issue with the getUserMedia API - the user probably denied permission',
- image: null,
- cameraStream: {}
- });
- },
- streamed: function streamed() {
- // The streaming started somehow, so we can assume there is getUserMedia support
- clearTimeout(noGetUserMediaSupportTimeout);
- },
- completed: function completed() {
- var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
- var cameraStream = obj.cameraStream,
- videoElement = obj.videoElement,
- videoHeight = obj.videoHeight,
- videoWidth = obj.videoWidth;
- callback({
- cameraStream: cameraStream,
- videoElement: videoElement,
- videoHeight: videoHeight,
- videoWidth: videoWidth
- });
- },
- lastCameraStream: options.lastCameraStream,
- webcamVideoElement: webcamVideoElement,
- crossOrigin: options.crossOrigin,
- options: options
- });
- },
- stopVideoStreaming: function stopVideoStreaming(obj) {
- obj = utils.isObject(obj) ? obj : {};
- var _obj = obj,
- keepCameraOn = _obj.keepCameraOn,
- videoElement = _obj.videoElement,
- webcamVideoElement = _obj.webcamVideoElement;
- var cameraStream = obj.cameraStream || {};
- var cameraStreamTracks = cameraStream.getTracks ? cameraStream.getTracks() || [] : [];
- var hasCameraStreamTracks = !!cameraStreamTracks.length;
- var firstCameraStreamTrack = cameraStreamTracks[0];
-
- if (!keepCameraOn && hasCameraStreamTracks) {
- if (utils.isFunction(firstCameraStreamTrack.stop)) {
- // Stops the camera stream
- firstCameraStreamTrack.stop();
- }
- }
-
- if (utils.isElement(videoElement) && !webcamVideoElement) {
- // Pauses the video, revokes the object URL (freeing up memory), and remove the video element
- videoElement.pause(); // Destroys the object url
-
- if (utils.isFunction(utils.URL.revokeObjectURL) && !utils.webWorkerError) {
- if (videoElement.src) {
- utils.URL.revokeObjectURL(videoElement.src);
- }
- } // Removes the video element from the DOM
-
-
- utils.removeElement(videoElement);
- }
- }
- };
- /*
- stopVideoStreaming.js
- =====================
- */
-
- /* Copyright 2017 Yahoo Inc.
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
- */
-
- function stopVideoStreaming(options) {
- options = utils.isObject(options) ? options : {};
- videoStream.stopVideoStreaming(options);
- }
- /*
- createAndGetGIF.js
- ==================
- */
-
- /* Copyright 2017 Yahoo Inc.
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
- */
- // Dependencies
-
-
- function createAndGetGIF(obj, callback) {
- var options = obj.options || {};
- var images = options.images,
- video = options.video;
- var gifWidth = Number(options.gifWidth);
- var gifHeight = Number(options.gifHeight);
- var numFrames = Number(options.numFrames);
- var cameraStream = obj.cameraStream,
- videoElement = obj.videoElement,
- videoWidth = obj.videoWidth,
- videoHeight = obj.videoHeight;
- var cropDimensions = screenShot.getCropDimensions({
- videoWidth: videoWidth,
- videoHeight: videoHeight,
- gifHeight: gifHeight,
- gifWidth: gifWidth
- });
- var completeCallback = callback;
- options.crop = cropDimensions;
- options.videoElement = videoElement;
- options.videoWidth = videoWidth;
- options.videoHeight = videoHeight;
- options.cameraStream = cameraStream;
-
- if (!utils.isElement(videoElement)) {
- return;
- }
-
- videoElement.width = gifWidth + cropDimensions.width;
- videoElement.height = gifHeight + cropDimensions.height;
-
- if (!options.webcamVideoElement) {
- utils.setCSSAttr(videoElement, {
- position: 'fixed',
- opacity: '0'
- });
- document.body.appendChild(videoElement);
- } // Firefox doesn't seem to obey autoplay if the element is not in the DOM when the content
- // is loaded, so we must manually trigger play after adding it, or the video will be frozen
-
-
- videoElement.play();
- screenShot.getGIF(options, function (obj) {
- if ((!images || !images.length) && (!video || !video.length)) {
- stopVideoStreaming(obj);
- }
-
- completeCallback(obj);
- });
- }
- /*
- existingVideo.js
- ================
- */
-
- /* Copyright 2017 Yahoo Inc.
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
- */
- // Dependencies
-
-
- function existingVideo() {
- var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
- var callback = obj.callback,
- existingVideo = obj.existingVideo,
- options = obj.options;
- var skipObj = {
- getUserMedia: true,
- 'window.URL': true
- };
- var errorObj = error.validate(skipObj);
- var videoType = void 0;
- var videoSrc = void 0;
-
- if (errorObj.error) {
- return callback(errorObj);
- }
-
- if (utils.isElement(existingVideo) && existingVideo.src) {
- videoSrc = existingVideo.src;
- videoType = utils.getExtension(videoSrc);
-
- if (!utils.isSupported.videoCodecs[videoType]) {
- return callback(error.messages.videoCodecs);
- }
- } else if (utils.isArray(existingVideo)) {
- utils.each(existingVideo, function (iterator, videoSrc) {
- if (videoSrc instanceof Blob) {
- videoType = videoSrc.type.substr(videoSrc.type.lastIndexOf('/') + 1, videoSrc.length);
- } else {
- videoType = videoSrc.substr(videoSrc.lastIndexOf('.') + 1, videoSrc.length);
- }
-
- if (utils.isSupported.videoCodecs[videoType]) {
- existingVideo = videoSrc;
- return false;
- }
- });
- }
-
- videoStream.startStreaming({
- completed: function completed(obj) {
- obj.options = options || {};
- createAndGetGIF(obj, callback);
- },
- existingVideo: existingVideo,
- crossOrigin: options.crossOrigin,
- options: options
- });
- }
- /*
- existingWebcam.js
- =================
- */
-
- /* Copyright 2017 Yahoo Inc.
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
- */
- // Dependencies
-
-
- function existingWebcam() {
- var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
- var callback = obj.callback,
- lastCameraStream = obj.lastCameraStream,
- options = obj.options,
- webcamVideoElement = obj.webcamVideoElement;
-
- if (!isWebCamGIFSupported()) {
- return callback(error.validate());
- }
-
- if (options.savedRenderingContexts.length) {
- screenShot.getGIF(options, function (obj) {
- callback(obj);
- });
- return;
- }
-
- videoStream.startVideoStreaming(function () {
- var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
- obj.options = options || {};
- createAndGetGIF(obj, callback);
- }, {
- lastCameraStream: lastCameraStream,
- callback: callback,
- webcamVideoElement: webcamVideoElement,
- crossOrigin: options.crossOrigin
- });
- }
- /*
- createGIF.js
- ============
- */
-
- /* Copyright 2017 Yahoo Inc.
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
- */
- // Dependencies
-
-
- function createGIF(userOptions, callback) {
- callback = utils.isFunction(userOptions) ? userOptions : callback;
- userOptions = utils.isObject(userOptions) ? userOptions : {};
-
- if (!utils.isFunction(callback)) {
- return;
- }
-
- var options = utils.mergeOptions(defaultOptions, userOptions) || {};
- var lastCameraStream = userOptions.cameraStream;
- var images = options.images;
- var imagesLength = images ? images.length : 0;
- var video = options.video;
- var webcamVideoElement = options.webcamVideoElement;
- options = utils.mergeOptions(options, {
- 'gifWidth': Math.floor(options.gifWidth),
- 'gifHeight': Math.floor(options.gifHeight)
- }); // If the user would like to create a GIF from an existing image(s)
-
- if (imagesLength) {
- existingImages({
- 'images': images,
- 'imagesLength': imagesLength,
- 'callback': callback,
- 'options': options
- });
- } else if (video) {
- // If the user would like to create a GIF from an existing HTML5 video
- existingVideo({
- 'existingVideo': video,
- callback: callback,
- options: options
- });
- } else {
- // If the user would like to create a GIF from a webcam stream
- existingWebcam({
- lastCameraStream: lastCameraStream,
- callback: callback,
- webcamVideoElement: webcamVideoElement,
- options: options
- });
- }
- }
- /*
- takeSnapShot.js
- ===============
- */
-
- /* Copyright 2017 Yahoo Inc.
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
- */
-
-
- function takeSnapShot(userOptions, callback) {
- callback = utils.isFunction(userOptions) ? userOptions : callback;
- userOptions = utils.isObject(userOptions) ? userOptions : {};
-
- if (!utils.isFunction(callback)) {
- return;
- }
-
- var mergedOptions = utils.mergeOptions(defaultOptions, userOptions);
- var options = utils.mergeOptions(mergedOptions, {
- 'interval': .1,
- 'numFrames': 1,
- 'gifWidth': Math.floor(mergedOptions.gifWidth),
- 'gifHeight': Math.floor(mergedOptions.gifHeight)
- });
- createGIF(options, callback);
- }
- /*
- API.js
- ======
- */
-
- /* Copyright 2017 Yahoo Inc.
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
- */
- // Dependencies
-
-
- var API = {
- 'utils': utils$2,
- 'error': error$2,
- 'defaultOptions': defaultOptions$2,
- 'createGIF': createGIF,
- 'takeSnapShot': takeSnapShot,
- 'stopVideoStreaming': stopVideoStreaming,
- 'isSupported': isSupported,
- 'isWebCamGIFSupported': isWebCamGIFSupported,
- 'isExistingVideoGIFSupported': isExistingVideoGIFSupported,
- 'isExistingImagesGIFSupported': isSupported$1,
- 'VERSION': '0.4.5'
- };
- /*
- index.js
- ========
- */
-
- /* Copyright 2017 Yahoo Inc.
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
- */
- // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, and plain browser loading
-
- if (typeof undefined === 'function' && undefined.amd) {
- undefined([], function () {
- return API;
- });
- } else {
- module.exports = API;
- }
- })(typeof window !== "undefined" ? window : {}, typeof document !== "undefined" ? document : {
- createElement: function createElement() {}
- }, typeof window !== "undefined" ? window.navigator : {});
- });
-
- function artplayerPluginGif(art) {
- console.log(gifshot);
- return {
- name: 'artplayerPluginGif'
- };
- }
-
- window.artplayerPluginGif = artplayerPluginGif;
-
- exports.default = artplayerPluginGif;
-
- Object.defineProperty(exports, '__esModule', { value: true });
+ function _defineProperty(obj, key, value) {
+ if (key in obj) {
+ Object.defineProperty(obj, key, {
+ value: value,
+ enumerable: true,
+ configurable: true,
+ writable: true
+ });
+ } else {
+ obj[key] = value;
+ }
+
+ return obj;
+ }
+
+ var defineProperty = _defineProperty;
+
+ function _objectSpread(target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i] != null ? arguments[i] : {};
+ var ownKeys = Object.keys(source);
+
+ if (typeof Object.getOwnPropertySymbols === 'function') {
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) {
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
+ }));
+ }
+
+ ownKeys.forEach(function (key) {
+ defineProperty(target, key, source[key]);
+ });
+ }
+
+ return target;
+ }
+
+ var objectSpread = _objectSpread;
+
+ function createCommonjsModule(fn, module) {
+ return module = { exports: {} }, fn(module, module.exports), module.exports;
+ }
+
+ var gifshot = createCommonjsModule(function (module, exports) {
+
+ (function (window, document, navigator, undefined) {
+ /*
+ utils.js
+ ========
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+
+ var utils = {
+ URL: window.URL || window.webkitURL || window.mozURL || window.msURL,
+ getUserMedia: function () {
+ var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
+ return getUserMedia ? getUserMedia.bind(navigator) : getUserMedia;
+ }(),
+ requestAnimFrame: window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame,
+ requestTimeout: function requestTimeout(callback, delay) {
+ callback = callback || utils.noop;
+ delay = delay || 0;
+
+ if (!utils.requestAnimFrame) {
+ return setTimeout(callback, delay);
+ }
+
+ var start = new Date().getTime();
+ var handle = new Object();
+ var requestAnimFrame = utils.requestAnimFrame;
+
+ var loop = function loop() {
+ var current = new Date().getTime();
+ var delta = current - start;
+ delta >= delay ? callback.call() : handle.value = requestAnimFrame(loop);
+ };
+
+ handle.value = requestAnimFrame(loop);
+ return handle;
+ },
+ Blob: window.Blob || window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder,
+ btoa: function () {
+ var btoa = window.btoa || function (input) {
+ var output = '';
+ var i = 0;
+ var l = input.length;
+ var key = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
+ var chr1 = void 0;
+ var chr2 = void 0;
+ var chr3 = void 0;
+ var enc1 = void 0;
+ var enc2 = void 0;
+ var enc3 = void 0;
+ var enc4 = void 0;
+
+ while (i < l) {
+ chr1 = input.charCodeAt(i++);
+ chr2 = input.charCodeAt(i++);
+ chr3 = input.charCodeAt(i++);
+ enc1 = chr1 >> 2;
+ enc2 = (chr1 & 3) << 4 | chr2 >> 4;
+ enc3 = (chr2 & 15) << 2 | chr3 >> 6;
+ enc4 = chr3 & 63;
+
+ if (isNaN(chr2)) {
+ enc3 = enc4 = 64;
+ } else if (isNaN(chr3)) {
+ enc4 = 64;
+ }
+
+ output = output + key.charAt(enc1) + key.charAt(enc2) + key.charAt(enc3) + key.charAt(enc4);
+ }
+
+ return output;
+ };
+
+ return btoa ? btoa.bind(window) : utils.noop;
+ }(),
+ isObject: function isObject(obj) {
+ return obj && Object.prototype.toString.call(obj) === '[object Object]';
+ },
+ isEmptyObject: function isEmptyObject(obj) {
+ return utils.isObject(obj) && !Object.keys(obj).length;
+ },
+ isArray: function isArray(arr) {
+ return arr && Array.isArray(arr);
+ },
+ isFunction: function isFunction(func) {
+ return func && typeof func === 'function';
+ },
+ isElement: function isElement(elem) {
+ return elem && elem.nodeType === 1;
+ },
+ isString: function isString(value) {
+ return typeof value === 'string' || Object.prototype.toString.call(value) === '[object String]';
+ },
+ isSupported: {
+ canvas: function canvas() {
+ var el = document.createElement('canvas');
+ return el && el.getContext && el.getContext('2d');
+ },
+ webworkers: function webworkers() {
+ return window.Worker;
+ },
+ blob: function blob() {
+ return utils.Blob;
+ },
+ Uint8Array: function Uint8Array() {
+ return window.Uint8Array;
+ },
+ Uint32Array: function Uint32Array() {
+ return window.Uint32Array;
+ },
+ videoCodecs: function () {
+ var testEl = document.createElement('video');
+ var supportObj = {
+ 'mp4': false,
+ 'h264': false,
+ 'ogv': false,
+ 'ogg': false,
+ 'webm': false
+ };
+
+ try {
+ if (testEl && testEl.canPlayType) {
+ // Check for MPEG-4 support
+ supportObj.mp4 = testEl.canPlayType('video/mp4; codecs="mp4v.20.8"') !== ''; // Check for h264 support
+
+ supportObj.h264 = (testEl.canPlayType('video/mp4; codecs="avc1.42E01E"') || testEl.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"')) !== ''; // Check for Ogv support
+
+ supportObj.ogv = testEl.canPlayType('video/ogg; codecs="theora"') !== ''; // Check for Ogg support
+
+ supportObj.ogg = testEl.canPlayType('video/ogg; codecs="theora"') !== ''; // Check for Webm support
+
+ supportObj.webm = testEl.canPlayType('video/webm; codecs="vp8, vorbis"') !== -1;
+ }
+ } catch (e) {}
+
+ return supportObj;
+ }()
+ },
+ noop: function noop() {},
+ each: function each(collection, callback) {
+ var x = void 0;
+ var len = void 0;
+
+ if (utils.isArray(collection)) {
+ x = -1;
+ len = collection.length;
+
+ while (++x < len) {
+ if (callback(x, collection[x]) === false) {
+ break;
+ }
+ }
+ } else if (utils.isObject(collection)) {
+ for (x in collection) {
+ if (collection.hasOwnProperty(x)) {
+ if (callback(x, collection[x]) === false) {
+ break;
+ }
+ }
+ }
+ }
+ },
+ mergeOptions: function mergeOptions(defaultOptions, userOptions) {
+ if (!utils.isObject(defaultOptions) || !utils.isObject(userOptions) || !Object.keys) {
+ return;
+ }
+
+ var newObj = {};
+ utils.each(defaultOptions, function (key, val) {
+ newObj[key] = defaultOptions[key];
+ });
+ utils.each(userOptions, function (key, val) {
+ var currentUserOption = userOptions[key];
+
+ if (!utils.isObject(currentUserOption)) {
+ newObj[key] = currentUserOption;
+ } else {
+ if (!defaultOptions[key]) {
+ newObj[key] = currentUserOption;
+ } else {
+ newObj[key] = utils.mergeOptions(defaultOptions[key], currentUserOption);
+ }
+ }
+ });
+ return newObj;
+ },
+ setCSSAttr: function setCSSAttr(elem, attr, val) {
+ if (!utils.isElement(elem)) {
+ return;
+ }
+
+ if (utils.isString(attr) && utils.isString(val)) {
+ elem.style[attr] = val;
+ } else if (utils.isObject(attr)) {
+ utils.each(attr, function (key, val) {
+ elem.style[key] = val;
+ });
+ }
+ },
+ removeElement: function removeElement(node) {
+ if (!utils.isElement(node)) {
+ return;
+ }
+
+ if (node.parentNode) {
+ node.parentNode.removeChild(node);
+ }
+ },
+ createWebWorker: function createWebWorker(content) {
+ if (!utils.isString(content)) {
+ return {};
+ }
+
+ try {
+ var blob = new utils.Blob([content], {
+ 'type': 'text/javascript'
+ });
+ var objectUrl = utils.URL.createObjectURL(blob);
+ var worker = new Worker(objectUrl);
+ return {
+ 'objectUrl': objectUrl,
+ 'worker': worker
+ };
+ } catch (e) {
+ return '' + e;
+ }
+ },
+ getExtension: function getExtension(src) {
+ return src.substr(src.lastIndexOf('.') + 1, src.length);
+ },
+ getFontSize: function getFontSize() {
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+
+ if (!document.body || options.resizeFont === false) {
+ return options.fontSize;
+ }
+
+ var text = options.text;
+ var containerWidth = options.gifWidth;
+ var fontSize = parseInt(options.fontSize, 10);
+ var minFontSize = parseInt(options.minFontSize, 10);
+ var div = document.createElement('div');
+ var span = document.createElement('span');
+ div.setAttribute('width', containerWidth);
+ div.appendChild(span);
+ span.innerHTML = text;
+ span.style.fontSize = fontSize + 'px';
+ span.style.textIndent = '-9999px';
+ span.style.visibility = 'hidden';
+ document.body.appendChild(span);
+
+ while (span.offsetWidth > containerWidth && fontSize >= minFontSize) {
+ span.style.fontSize = --fontSize + 'px';
+ }
+
+ document.body.removeChild(span);
+ return fontSize + 'px';
+ },
+ webWorkerError: false
+ };
+ var utils$2 = Object.freeze({
+ default: utils
+ });
+ /*
+ error.js
+ ========
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+
+ var error = {
+ validate: function validate(skipObj) {
+ skipObj = utils.isObject(skipObj) ? skipObj : {};
+ var errorObj = {};
+ utils.each(error.validators, function (indece, currentValidator) {
+ var errorCode = currentValidator.errorCode;
+
+ if (!skipObj[errorCode] && !currentValidator.condition) {
+ errorObj = currentValidator;
+ errorObj.error = true;
+ return false;
+ }
+ });
+ delete errorObj.condition;
+ return errorObj;
+ },
+ isValid: function isValid(skipObj) {
+ var errorObj = error.validate(skipObj);
+ var isValid = errorObj.error !== true ? true : false;
+ return isValid;
+ },
+ validators: [{
+ condition: utils.isFunction(utils.getUserMedia),
+ errorCode: 'getUserMedia',
+ errorMsg: 'The getUserMedia API is not supported in your browser'
+ }, {
+ condition: utils.isSupported.canvas(),
+ errorCode: 'canvas',
+ errorMsg: 'Canvas elements are not supported in your browser'
+ }, {
+ condition: utils.isSupported.webworkers(),
+ errorCode: 'webworkers',
+ errorMsg: 'The Web Workers API is not supported in your browser'
+ }, {
+ condition: utils.isFunction(utils.URL),
+ errorCode: 'window.URL',
+ errorMsg: 'The window.URL API is not supported in your browser'
+ }, {
+ condition: utils.isSupported.blob(),
+ errorCode: 'window.Blob',
+ errorMsg: 'The window.Blob File API is not supported in your browser'
+ }, {
+ condition: utils.isSupported.Uint8Array(),
+ errorCode: 'window.Uint8Array',
+ errorMsg: 'The window.Uint8Array function constructor is not supported in your browser'
+ }, {
+ condition: utils.isSupported.Uint32Array(),
+ errorCode: 'window.Uint32Array',
+ errorMsg: 'The window.Uint32Array function constructor is not supported in your browser'
+ }],
+ messages: {
+ videoCodecs: {
+ errorCode: 'videocodec',
+ errorMsg: 'The video codec you are trying to use is not supported in your browser'
+ }
+ }
+ };
+ var error$2 = Object.freeze({
+ default: error
+ });
+ /*
+ defaultOptions.js
+ =================
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Helpers
+
+ var noop = function noop() {};
+
+ var defaultOptions = {
+ sampleInterval: 10,
+ numWorkers: 2,
+ filter: '',
+ gifWidth: 200,
+ gifHeight: 200,
+ interval: 0.1,
+ numFrames: 10,
+ frameDuration: 1,
+ keepCameraOn: false,
+ images: [],
+ video: null,
+ webcamVideoElement: null,
+ cameraStream: null,
+ text: '',
+ fontWeight: 'normal',
+ fontSize: '16px',
+ minFontSize: '10px',
+ resizeFont: false,
+ fontFamily: 'sans-serif',
+ fontColor: '#ffffff',
+ textAlign: 'center',
+ textBaseline: 'bottom',
+ textXCoordinate: null,
+ textYCoordinate: null,
+ progressCallback: noop,
+ completeCallback: noop,
+ saveRenderingContexts: false,
+ savedRenderingContexts: [],
+ crossOrigin: 'Anonymous'
+ };
+ var defaultOptions$2 = Object.freeze({
+ default: defaultOptions
+ });
+ /*
+ isSupported.js
+ ==============
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+
+ function isSupported() {
+ return error.isValid();
+ }
+ /*
+ isWebCamGIFSupported.js
+ =======================
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+
+
+ function isWebCamGIFSupported() {
+ return error.isValid();
+ }
+ /*
+ isSupported.js
+ ==============
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+
+
+ function isSupported$1() {
+ var options = {
+ getUserMedia: true
+ };
+ return error.isValid(options);
+ }
+ /*
+ isExistingVideoGIFSupported.js
+ ==============================
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+
+
+ function isExistingVideoGIFSupported(codecs) {
+ var hasValidCodec = false;
+
+ if (utils.isArray(codecs) && codecs.length) {
+ utils.each(codecs, function (indece, currentCodec) {
+ if (utils.isSupported.videoCodecs[currentCodec]) {
+ hasValidCodec = true;
+ }
+ });
+
+ if (!hasValidCodec) {
+ return false;
+ }
+ } else if (utils.isString(codecs) && codecs.length) {
+ if (!utils.isSupported.videoCodecs[codecs]) {
+ return false;
+ }
+ }
+
+ return error.isValid({
+ 'getUserMedia': true
+ });
+ }
+ /*
+ NeuQuant.js
+ ===========
+ */
+
+ /*
+ * NeuQuant Neural-Net Quantization Algorithm
+ * ------------------------------------------
+ *
+ * Copyright (c) 1994 Anthony Dekker
+ *
+ * NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. See
+ * "Kohonen neural networks for optimal colour quantization" in "Network:
+ * Computation in Neural Systems" Vol. 5 (1994) pp 351-367. for a discussion of
+ * the algorithm.
+ *
+ * Any party obtaining a copy of these files from the author, directly or
+ * indirectly, is granted, free of charge, a full and unrestricted irrevocable,
+ * world-wide, paid up, royalty-free, nonexclusive right and license to deal in
+ * this software and documentation files (the "Software"), including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons who
+ * receive copies from any such party to do so, with the only requirement being
+ * that this copyright notice remain intact.
+ */
+
+ /*
+ * This class handles Neural-Net quantization algorithm
+ * @author Kevin Weiner (original Java version - kweiner@fmsware.com)
+ * @author Thibault Imbert (AS3 version - bytearray.org)
+ * @version 0.1 AS3 implementation
+ * @version 0.2 JS->AS3 "translation" by antimatter15
+ * @version 0.3 JS clean up + using modern JS idioms by sole - http://soledadpenades.com
+ * Also implement fix in color conversion described at http://stackoverflow.com/questions/16371712/neuquant-js-javascript-color-quantization-hidden-bug-in-js-conversion
+ */
+
+
+ function NeuQuant() {
+ var netsize = 256; // number of colours used
+ // four primes near 500 - assume no image has a length so large
+ // that it is divisible by all four primes
+
+ var prime1 = 499;
+ var prime2 = 491;
+ var prime3 = 487;
+ var prime4 = 503; // minimum size for input image
+
+ var minpicturebytes = 3 * prime4; // Network Definitions
+
+ var maxnetpos = netsize - 1;
+ var netbiasshift = 4; // bias for colour values
+
+ var ncycles = 100; // no. of learning cycles
+ // defs for freq and bias
+
+ var intbiasshift = 16; // bias for fractions
+
+ var intbias = 1 << intbiasshift;
+ var gammashift = 10; // gamma = 1024
+ var betashift = 10;
+ var beta = intbias >> betashift; // beta = 1/1024
+
+ var betagamma = intbias << gammashift - betashift; // defs for decreasing radius factor
+ // For 256 colors, radius starts at 32.0 biased by 6 bits
+ // and decreases by a factor of 1/30 each cycle
+
+ var initrad = netsize >> 3;
+ var radiusbiasshift = 6;
+ var radiusbias = 1 << radiusbiasshift;
+ var initradius = initrad * radiusbias;
+ var radiusdec = 30; // defs for decreasing alpha factor
+ // Alpha starts at 1.0 biased by 10 bits
+
+ var alphabiasshift = 10;
+ var initalpha = 1 << alphabiasshift;
+ var alphadec; // radbias and alpharadbias used for radpower calculation
+
+ var radbiasshift = 8;
+ var radbias = 1 << radbiasshift;
+ var alpharadbshift = alphabiasshift + radbiasshift;
+ var alpharadbias = 1 << alpharadbshift; // Input image
+
+ var thepicture; // Height * Width * 3
+
+ var lengthcount; // Sampling factor 1..30
+
+ var samplefac; // The network itself
+
+ var network;
+ var netindex = []; // for network lookup - really 256
+
+ var bias = []; // bias and freq arrays for learning
+
+ var freq = [];
+ var radpower = [];
+
+ function NeuQuantConstructor(thepic, len, sample) {
+ var i;
+ var p;
+ thepicture = thepic;
+ lengthcount = len;
+ samplefac = sample;
+ network = new Array(netsize);
+
+ for (i = 0; i < netsize; i++) {
+ network[i] = new Array(4);
+ p = network[i];
+ p[0] = p[1] = p[2] = (i << netbiasshift + 8) / netsize | 0;
+ freq[i] = intbias / netsize | 0; // 1 / netsize
+
+ bias[i] = 0;
+ }
+ }
+
+ function colorMap() {
+ var map = [];
+ var index = new Array(netsize);
+
+ for (var i = 0; i < netsize; i++) {
+ index[network[i][3]] = i;
+ }
+
+ var k = 0;
+
+ for (var l = 0; l < netsize; l++) {
+ var j = index[l];
+ map[k++] = network[j][0];
+ map[k++] = network[j][1];
+ map[k++] = network[j][2];
+ }
+
+ return map;
+ } // Insertion sort of network and building of netindex[0..255]
+ // (to do after unbias)
+
+
+ function inxbuild() {
+ var i;
+ var j;
+ var smallpos;
+ var smallval;
+ var p;
+ var q;
+ var previouscol;
+ var startpos;
+ previouscol = 0;
+ startpos = 0;
+
+ for (i = 0; i < netsize; i++) {
+ p = network[i];
+ smallpos = i;
+ smallval = p[1]; // index on g
+ // find smallest in i..netsize-1
+
+ for (j = i + 1; j < netsize; j++) {
+ q = network[j];
+
+ if (q[1] < smallval) {
+ // index on g
+ smallpos = j;
+ smallval = q[1]; // index on g
+ }
+ }
+
+ q = network[smallpos]; // swap p (i) and q (smallpos) entries
+
+ if (i != smallpos) {
+ j = q[0];
+ q[0] = p[0];
+ p[0] = j;
+ j = q[1];
+ q[1] = p[1];
+ p[1] = j;
+ j = q[2];
+ q[2] = p[2];
+ p[2] = j;
+ j = q[3];
+ q[3] = p[3];
+ p[3] = j;
+ } // smallval entry is now in position i
+
+
+ if (smallval != previouscol) {
+ netindex[previouscol] = startpos + i >> 1;
+
+ for (j = previouscol + 1; j < smallval; j++) {
+ netindex[j] = i;
+ }
+
+ previouscol = smallval;
+ startpos = i;
+ }
+ }
+
+ netindex[previouscol] = startpos + maxnetpos >> 1;
+
+ for (j = previouscol + 1; j < 256; j++) {
+ netindex[j] = maxnetpos; // really 256
+ }
+ } // Main Learning Loop
+
+
+ function learn() {
+ var i;
+ var j;
+ var b;
+ var g;
+ var r;
+ var radius;
+ var rad;
+ var alpha;
+ var step;
+ var delta;
+ var samplepixels;
+ var p;
+ var pix;
+ var lim;
+
+ if (lengthcount < minpicturebytes) {
+ samplefac = 1;
+ }
+
+ alphadec = 30 + (samplefac - 1) / 3;
+ p = thepicture;
+ pix = 0;
+ lim = lengthcount;
+ samplepixels = lengthcount / (3 * samplefac);
+ delta = samplepixels / ncycles | 0;
+ alpha = initalpha;
+ radius = initradius;
+ rad = radius >> radiusbiasshift;
+
+ if (rad <= 1) {
+ rad = 0;
+ }
+
+ for (i = 0; i < rad; i++) {
+ radpower[i] = alpha * ((rad * rad - i * i) * radbias / (rad * rad));
+ }
+
+ if (lengthcount < minpicturebytes) {
+ step = 3;
+ } else if (lengthcount % prime1 !== 0) {
+ step = 3 * prime1;
+ } else {
+ if (lengthcount % prime2 !== 0) {
+ step = 3 * prime2;
+ } else {
+ if (lengthcount % prime3 !== 0) {
+ step = 3 * prime3;
+ } else {
+ step = 3 * prime4;
+ }
+ }
+ }
+
+ i = 0;
+
+ while (i < samplepixels) {
+ b = (p[pix + 0] & 0xff) << netbiasshift;
+ g = (p[pix + 1] & 0xff) << netbiasshift;
+ r = (p[pix + 2] & 0xff) << netbiasshift;
+ j = contest(b, g, r);
+ altersingle(alpha, j, b, g, r);
+
+ if (rad !== 0) {
+ // Alter neighbours
+ alterneigh(rad, j, b, g, r);
+ }
+
+ pix += step;
+
+ if (pix >= lim) {
+ pix -= lengthcount;
+ }
+
+ i++;
+
+ if (delta === 0) {
+ delta = 1;
+ }
+
+ if (i % delta === 0) {
+ alpha -= alpha / alphadec;
+ radius -= radius / radiusdec;
+ rad = radius >> radiusbiasshift;
+
+ if (rad <= 1) {
+ rad = 0;
+ }
+
+ for (j = 0; j < rad; j++) {
+ radpower[j] = alpha * ((rad * rad - j * j) * radbias / (rad * rad));
+ }
+ }
+ }
+ } // Search for BGR values 0..255 (after net is unbiased) and return colour index
+
+
+ function map(b, g, r) {
+ var i;
+ var j;
+ var dist;
+ var a;
+ var bestd;
+ var p;
+ var best; // Biggest possible distance is 256 * 3
+
+ bestd = 1000;
+ best = -1;
+ i = netindex[g]; // index on g
+
+ j = i - 1; // start at netindex[g] and work outwards
+
+ while (i < netsize || j >= 0) {
+ if (i < netsize) {
+ p = network[i];
+ dist = p[1] - g; // inx key
+
+ if (dist >= bestd) {
+ i = netsize; // stop iter
+ } else {
+ i++;
+
+ if (dist < 0) {
+ dist = -dist;
+ }
+
+ a = p[0] - b;
+
+ if (a < 0) {
+ a = -a;
+ }
+
+ dist += a;
+
+ if (dist < bestd) {
+ a = p[2] - r;
+
+ if (a < 0) {
+ a = -a;
+ }
+
+ dist += a;
+
+ if (dist < bestd) {
+ bestd = dist;
+ best = p[3];
+ }
+ }
+ }
+ }
+
+ if (j >= 0) {
+ p = network[j];
+ dist = g - p[1]; // inx key - reverse dif
+
+ if (dist >= bestd) {
+ j = -1; // stop iter
+ } else {
+ j--;
+
+ if (dist < 0) {
+ dist = -dist;
+ }
+
+ a = p[0] - b;
+
+ if (a < 0) {
+ a = -a;
+ }
+
+ dist += a;
+
+ if (dist < bestd) {
+ a = p[2] - r;
+
+ if (a < 0) {
+ a = -a;
+ }
+
+ dist += a;
+
+ if (dist < bestd) {
+ bestd = dist;
+ best = p[3];
+ }
+ }
+ }
+ }
+ }
+
+ return best;
+ }
+
+ function process() {
+ learn();
+ unbiasnet();
+ inxbuild();
+ return colorMap();
+ } // Unbias network to give byte values 0..255 and record position i
+ // to prepare for sort
+
+
+ function unbiasnet() {
+ var i;
+
+ for (i = 0; i < netsize; i++) {
+ network[i][0] >>= netbiasshift;
+ network[i][1] >>= netbiasshift;
+ network[i][2] >>= netbiasshift;
+ network[i][3] = i; // record colour no
+ }
+ } // Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2))
+ // in radpower[|i-j|]
+
+
+ function alterneigh(rad, i, b, g, r) {
+ var j;
+ var k;
+ var lo;
+ var hi;
+ var a;
+ var m;
+ var p;
+ lo = i - rad;
+
+ if (lo < -1) {
+ lo = -1;
+ }
+
+ hi = i + rad;
+
+ if (hi > netsize) {
+ hi = netsize;
+ }
+
+ j = i + 1;
+ k = i - 1;
+ m = 1;
+
+ while (j < hi || k > lo) {
+ a = radpower[m++];
+
+ if (j < hi) {
+ p = network[j++];
+
+ try {
+ p[0] -= a * (p[0] - b) / alpharadbias | 0;
+ p[1] -= a * (p[1] - g) / alpharadbias | 0;
+ p[2] -= a * (p[2] - r) / alpharadbias | 0;
+ } catch (e) {}
+ }
+
+ if (k > lo) {
+ p = network[k--];
+
+ try {
+ p[0] -= a * (p[0] - b) / alpharadbias | 0;
+ p[1] -= a * (p[1] - g) / alpharadbias | 0;
+ p[2] -= a * (p[2] - r) / alpharadbias | 0;
+ } catch (e) {}
+ }
+ }
+ } // Move neuron i towards biased (b,g,r) by factor alpha
+
+
+ function altersingle(alpha, i, b, g, r) {
+ // alter hit neuron
+ var n = network[i];
+ var alphaMult = alpha / initalpha;
+ n[0] -= alphaMult * (n[0] - b) | 0;
+ n[1] -= alphaMult * (n[1] - g) | 0;
+ n[2] -= alphaMult * (n[2] - r) | 0;
+ } // Search for biased BGR values
+
+
+ function contest(b, g, r) {
+ // finds closest neuron (min dist) and updates freq
+ // finds best neuron (min dist-bias) and returns position
+ // for frequently chosen neurons, freq[i] is high and bias[i] is negative
+ // bias[i] = gamma*((1/netsize)-freq[i])
+ var i;
+ var dist;
+ var a;
+ var biasdist;
+ var betafreq;
+ var bestpos;
+ var bestbiaspos;
+ var bestd;
+ var bestbiasd;
+ var n;
+ bestd = ~(1 << 31);
+ bestbiasd = bestd;
+ bestpos = -1;
+ bestbiaspos = bestpos;
+
+ for (i = 0; i < netsize; i++) {
+ n = network[i];
+ dist = n[0] - b;
+
+ if (dist < 0) {
+ dist = -dist;
+ }
+
+ a = n[1] - g;
+
+ if (a < 0) {
+ a = -a;
+ }
+
+ dist += a;
+ a = n[2] - r;
+
+ if (a < 0) {
+ a = -a;
+ }
+
+ dist += a;
+
+ if (dist < bestd) {
+ bestd = dist;
+ bestpos = i;
+ }
+
+ biasdist = dist - (bias[i] >> intbiasshift - netbiasshift);
+
+ if (biasdist < bestbiasd) {
+ bestbiasd = biasdist;
+ bestbiaspos = i;
+ }
+
+ betafreq = freq[i] >> betashift;
+ freq[i] -= betafreq;
+ bias[i] += betafreq << gammashift;
+ }
+
+ freq[bestpos] += beta;
+ bias[bestpos] -= betagamma;
+ return bestbiaspos;
+ }
+
+ NeuQuantConstructor.apply(this, arguments);
+ var exports = {};
+ exports.map = map;
+ exports.process = process;
+ return exports;
+ }
+ /*
+ processFrameWorker.js
+ =====================
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+
+
+ function workerCode() {
+ var self = this;
+
+ try {
+ self.onmessage = function (ev) {
+ var data = ev.data || {};
+ var response;
+
+ if (data.gifshot) {
+ response = workerMethods.run(data);
+ postMessage(response);
+ }
+ };
+ } catch (e) {}
+
+ var workerMethods = {
+ dataToRGB: function dataToRGB(data, width, height) {
+ var length = width * height * 4;
+ var i = 0;
+ var rgb = [];
+
+ while (i < length) {
+ rgb.push(data[i++]);
+ rgb.push(data[i++]);
+ rgb.push(data[i++]);
+ i++; // for the alpha channel which we don't care about
+ }
+
+ return rgb;
+ },
+ componentizedPaletteToArray: function componentizedPaletteToArray(paletteRGB) {
+ paletteRGB = paletteRGB || [];
+ var paletteArray = [];
+
+ for (var i = 0; i < paletteRGB.length; i += 3) {
+ var r = paletteRGB[i];
+ var g = paletteRGB[i + 1];
+ var b = paletteRGB[i + 2];
+ paletteArray.push(r << 16 | g << 8 | b);
+ }
+
+ return paletteArray;
+ },
+ // This is the "traditional" Animated_GIF style of going from RGBA to indexed color frames
+ 'processFrameWithQuantizer': function processFrameWithQuantizer(imageData, width, height, sampleInterval) {
+ var rgbComponents = this.dataToRGB(imageData, width, height);
+ var nq = new NeuQuant(rgbComponents, rgbComponents.length, sampleInterval);
+ var paletteRGB = nq.process();
+ var paletteArray = new Uint32Array(this.componentizedPaletteToArray(paletteRGB));
+ var numberPixels = width * height;
+ var indexedPixels = new Uint8Array(numberPixels);
+ var k = 0;
+
+ for (var i = 0; i < numberPixels; i++) {
+ var r = rgbComponents[k++];
+ var g = rgbComponents[k++];
+ var b = rgbComponents[k++];
+ indexedPixels[i] = nq.map(r, g, b);
+ }
+
+ return {
+ pixels: indexedPixels,
+ palette: paletteArray
+ };
+ },
+ 'run': function run(frame) {
+ frame = frame || {};
+ var _frame = frame,
+ height = _frame.height,
+ palette = _frame.palette,
+ sampleInterval = _frame.sampleInterval,
+ width = _frame.width;
+ var imageData = frame.data;
+ return this.processFrameWithQuantizer(imageData, width, height, sampleInterval);
+ }
+ };
+ return workerMethods;
+ }
+ /*
+ gifWriter.js
+ ============
+ */
+ // (c) Dean McNamee , 2013.
+ //
+ // https://github.com/deanm/omggif
+ //
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
+ // of this software and associated documentation files (the "Software"), to
+ // deal in the Software without restriction, including without limitation the
+ // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ // sell copies of the Software, and to permit persons to whom the Software is
+ // furnished to do so, subject to the following conditions:
+ //
+ // The above copyright notice and this permission notice shall be included in
+ // all copies or substantial portions of the Software.
+ //
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ // IN THE SOFTWARE.
+ //
+ // omggif is a JavaScript implementation of a GIF 89a encoder and decoder,
+ // including animation and compression. It does not rely on any specific
+ // underlying system, so should run in the browser, Node, or Plask.
+
+
+ function gifWriter(buf, width, height, gopts) {
+ var p = 0;
+ gopts = gopts === undefined ? {} : gopts;
+ var loop_count = gopts.loop === undefined ? null : gopts.loop;
+ var global_palette = gopts.palette === undefined ? null : gopts.palette;
+ if (width <= 0 || height <= 0 || width > 65535 || height > 65535) throw "Width/Height invalid.";
+
+ function check_palette_and_num_colors(palette) {
+ var num_colors = palette.length;
+ if (num_colors < 2 || num_colors > 256 || num_colors & num_colors - 1) throw "Invalid code/color length, must be power of 2 and 2 .. 256.";
+ return num_colors;
+ } // - Header.
+
+
+ buf[p++] = 0x47;
+ buf[p++] = 0x49;
+ buf[p++] = 0x46; // GIF
+
+ buf[p++] = 0x38;
+ buf[p++] = 0x39;
+ buf[p++] = 0x61; // 89a
+ // Handling of Global Color Table (palette) and background index.
+
+ var gp_num_colors_pow2 = 0;
+ var background = 0; // - Logical Screen Descriptor.
+ // NOTE(deanm): w/h apparently ignored by implementations, but set anyway.
+
+ buf[p++] = width & 0xff;
+ buf[p++] = width >> 8 & 0xff;
+ buf[p++] = height & 0xff;
+ buf[p++] = height >> 8 & 0xff; // NOTE: Indicates 0-bpp original color resolution (unused?).
+
+ buf[p++] = (global_palette !== null ? 0x80 : 0) | // Global Color Table Flag.
+ gp_num_colors_pow2; // NOTE: No sort flag (unused?).
+
+ buf[p++] = background; // Background Color Index.
+
+ buf[p++] = 0; // Pixel aspect ratio (unused?).
+
+ if (loop_count !== null) {
+ // Netscape block for looping.
+ if (loop_count < 0 || loop_count > 65535) throw "Loop count invalid."; // Extension code, label, and length.
+
+ buf[p++] = 0x21;
+ buf[p++] = 0xff;
+ buf[p++] = 0x0b; // NETSCAPE2.0
+
+ buf[p++] = 0x4e;
+ buf[p++] = 0x45;
+ buf[p++] = 0x54;
+ buf[p++] = 0x53;
+ buf[p++] = 0x43;
+ buf[p++] = 0x41;
+ buf[p++] = 0x50;
+ buf[p++] = 0x45;
+ buf[p++] = 0x32;
+ buf[p++] = 0x2e;
+ buf[p++] = 0x30; // Sub-block
+
+ buf[p++] = 0x03;
+ buf[p++] = 0x01;
+ buf[p++] = loop_count & 0xff;
+ buf[p++] = loop_count >> 8 & 0xff;
+ buf[p++] = 0x00; // Terminator.
+ }
+
+ var ended = false;
+
+ this.addFrame = function (x, y, w, h, indexed_pixels, opts) {
+ if (ended === true) {
+ --p;
+ ended = false;
+ } // Un-end.
+
+
+ opts = opts === undefined ? {} : opts; // TODO(deanm): Bounds check x, y. Do they need to be within the virtual
+ // canvas width/height, I imagine?
+
+ if (x < 0 || y < 0 || x > 65535 || y > 65535) throw "x/y invalid.";
+ if (w <= 0 || h <= 0 || w > 65535 || h > 65535) throw "Width/Height invalid.";
+ if (indexed_pixels.length < w * h) throw "Not enough pixels for the frame size.";
+ var using_local_palette = true;
+ var palette = opts.palette;
+
+ if (palette === undefined || palette === null) {
+ using_local_palette = false;
+ palette = global_palette;
+ }
+
+ if (palette === undefined || palette === null) throw "Must supply either a local or global palette.";
+ var num_colors = check_palette_and_num_colors(palette); // Compute the min_code_size (power of 2), destroying num_colors.
+
+ var min_code_size = 0;
+
+ while (num_colors >>= 1) {
+ ++min_code_size;
+ }
+
+ num_colors = 1 << min_code_size; // Now we can easily get it back.
+
+ var delay = opts.delay === undefined ? 0 : opts.delay; // From the spec:
+ // 0 - No disposal specified. The decoder is
+ // not required to take any action.
+ // 1 - Do not dispose. The graphic is to be left
+ // in place.
+ // 2 - Restore to background color. The area used by the
+ // graphic must be restored to the background color.
+ // 3 - Restore to previous. The decoder is required to
+ // restore the area overwritten by the graphic with
+ // what was there prior to rendering the graphic.
+ // 4-7 - To be defined.
+ // NOTE(deanm): Dispose background doesn't really work, apparently most
+ // browsers ignore the background palette index and clear to transparency.
+
+ var disposal = opts.disposal === undefined ? 0 : opts.disposal;
+ if (disposal < 0 || disposal > 3) // 4-7 is reserved.
+ throw "Disposal out of range.";
+ var use_transparency = false;
+ var transparent_index = 0;
+
+ if (opts.transparent !== undefined && opts.transparent !== null) {
+ use_transparency = true;
+ transparent_index = opts.transparent;
+ if (transparent_index < 0 || transparent_index >= num_colors) throw "Transparent color index.";
+ }
+
+ if (disposal !== 0 || use_transparency || delay !== 0) {
+ // - Graphics Control Extension
+ buf[p++] = 0x21;
+ buf[p++] = 0xf9; // Extension / Label.
+
+ buf[p++] = 4; // Byte size.
+
+ buf[p++] = disposal << 2 | (use_transparency === true ? 1 : 0);
+ buf[p++] = delay & 0xff;
+ buf[p++] = delay >> 8 & 0xff;
+ buf[p++] = transparent_index; // Transparent color index.
+
+ buf[p++] = 0; // Block Terminator.
+ } // - Image Descriptor
+
+
+ buf[p++] = 0x2c; // Image Seperator.
+
+ buf[p++] = x & 0xff;
+ buf[p++] = x >> 8 & 0xff; // Left.
+
+ buf[p++] = y & 0xff;
+ buf[p++] = y >> 8 & 0xff; // Top.
+
+ buf[p++] = w & 0xff;
+ buf[p++] = w >> 8 & 0xff;
+ buf[p++] = h & 0xff;
+ buf[p++] = h >> 8 & 0xff; // NOTE: No sort flag (unused?).
+ // TODO(deanm): Support interlace.
+
+ buf[p++] = using_local_palette === true ? 0x80 | min_code_size - 1 : 0; // - Local Color Table
+
+ if (using_local_palette === true) {
+ for (var i = 0, il = palette.length; i < il; ++i) {
+ var rgb = palette[i];
+ buf[p++] = rgb >> 16 & 0xff;
+ buf[p++] = rgb >> 8 & 0xff;
+ buf[p++] = rgb & 0xff;
+ }
+ }
+
+ p = GifWriterOutputLZWCodeStream(buf, p, min_code_size < 2 ? 2 : min_code_size, indexed_pixels);
+ };
+
+ this.end = function () {
+ if (ended === false) {
+ buf[p++] = 0x3b; // Trailer.
+
+ ended = true;
+ }
+
+ return p;
+ }; // Main compression routine, palette indexes -> LZW code stream.
+ // |index_stream| must have at least one entry.
+
+
+ function GifWriterOutputLZWCodeStream(buf, p, min_code_size, index_stream) {
+ buf[p++] = min_code_size;
+ var cur_subblock = p++; // Pointing at the length field.
+
+ var clear_code = 1 << min_code_size;
+ var code_mask = clear_code - 1;
+ var eoi_code = clear_code + 1;
+ var next_code = eoi_code + 1;
+ var cur_code_size = min_code_size + 1; // Number of bits per code.
+
+ var cur_shift = 0; // We have at most 12-bit codes, so we should have to hold a max of 19
+ // bits here (and then we would write out).
+
+ var cur = 0;
+
+ function emit_bytes_to_buffer(bit_block_size) {
+ while (cur_shift >= bit_block_size) {
+ buf[p++] = cur & 0xff;
+ cur >>= 8;
+ cur_shift -= 8;
+
+ if (p === cur_subblock + 256) {
+ // Finished a subblock.
+ buf[cur_subblock] = 255;
+ cur_subblock = p++;
+ }
+ }
+ }
+
+ function emit_code(c) {
+ cur |= c << cur_shift;
+ cur_shift += cur_code_size;
+ emit_bytes_to_buffer(8);
+ } // I am not an expert on the topic, and I don't want to write a thesis.
+ // However, it is good to outline here the basic algorithm and the few data
+ // structures and optimizations here that make this implementation fast.
+ // The basic idea behind LZW is to build a table of previously seen runs
+ // addressed by a short id (herein called output code). All data is
+ // referenced by a code, which represents one or more values from the
+ // original input stream. All input bytes can be referenced as the same
+ // value as an output code. So if you didn't want any compression, you
+ // could more or less just output the original bytes as codes (there are
+ // some details to this, but it is the idea). In order to achieve
+ // compression, values greater then the input range (codes can be up to
+ // 12-bit while input only 8-bit) represent a sequence of previously seen
+ // inputs. The decompressor is able to build the same mapping while
+ // decoding, so there is always a shared common knowledge between the
+ // encoding and decoder, which is also important for "timing" aspects like
+ // how to handle variable bit width code encoding.
+ //
+ // One obvious but very important consequence of the table system is there
+ // is always a unique id (at most 12-bits) to map the runs. 'A' might be
+ // 4, then 'AA' might be 10, 'AAA' 11, 'AAAA' 12, etc. This relationship
+ // can be used for an effecient lookup strategy for the code mapping. We
+ // need to know if a run has been seen before, and be able to map that run
+ // to the output code. Since we start with known unique ids (input bytes),
+ // and then from those build more unique ids (table entries), we can
+ // continue this chain (almost like a linked list) to always have small
+ // integer values that represent the current byte chains in the encoder.
+ // This means instead of tracking the input bytes (AAAABCD) to know our
+ // current state, we can track the table entry for AAAABC (it is guaranteed
+ // to exist by the nature of the algorithm) and the next character D.
+ // Therefor the tuple of (table_entry, byte) is guaranteed to also be
+ // unique. This allows us to create a simple lookup key for mapping input
+ // sequences to codes (table indices) without having to store or search
+ // any of the code sequences. So if 'AAAA' has a table entry of 12, the
+ // tuple of ('AAAA', K) for any input byte K will be unique, and can be our
+ // key. This leads to a integer value at most 20-bits, which can always
+ // fit in an SMI value and be used as a fast sparse array / object key.
+ // Output code for the current contents of the index buffer.
+
+
+ var ib_code = index_stream[0] & code_mask; // Load first input index.
+
+ var code_table = {}; // Key'd on our 20-bit "tuple".
+
+ emit_code(clear_code); // Spec says first code should be a clear code.
+ // First index already loaded, process the rest of the stream.
+
+ for (var i = 1, il = index_stream.length; i < il; ++i) {
+ var k = index_stream[i] & code_mask;
+ var cur_key = ib_code << 8 | k; // (prev, k) unique tuple.
+
+ var cur_code = code_table[cur_key]; // buffer + k.
+ // Check if we have to create a new code table entry.
+
+ if (cur_code === undefined) {
+ // We don't have buffer + k.
+ // Emit index buffer (without k).
+ // This is an inline version of emit_code, because this is the core
+ // writing routine of the compressor (and V8 cannot inline emit_code
+ // because it is a closure here in a different context). Additionally
+ // we can call emit_byte_to_buffer less often, because we can have
+ // 30-bits (from our 31-bit signed SMI), and we know our codes will only
+ // be 12-bits, so can safely have 18-bits there without overflow.
+ // emit_code(ib_code);
+ cur |= ib_code << cur_shift;
+ cur_shift += cur_code_size;
+
+ while (cur_shift >= 8) {
+ buf[p++] = cur & 0xff;
+ cur >>= 8;
+ cur_shift -= 8;
+
+ if (p === cur_subblock + 256) {
+ // Finished a subblock.
+ buf[cur_subblock] = 255;
+ cur_subblock = p++;
+ }
+ }
+
+ if (next_code === 4096) {
+ // Table full, need a clear.
+ emit_code(clear_code);
+ next_code = eoi_code + 1;
+ cur_code_size = min_code_size + 1;
+ code_table = {};
+ } else {
+ // Table not full, insert a new entry.
+ // Increase our variable bit code sizes if necessary. This is a bit
+ // tricky as it is based on "timing" between the encoding and
+ // decoder. From the encoders perspective this should happen after
+ // we've already emitted the index buffer and are about to create the
+ // first table entry that would overflow our current code bit size.
+ if (next_code >= 1 << cur_code_size) ++cur_code_size;
+ code_table[cur_key] = next_code++; // Insert into code table.
+ }
+
+ ib_code = k; // Index buffer to single input k.
+ } else {
+ ib_code = cur_code; // Index buffer to sequence in code table.
+ }
+ }
+
+ emit_code(ib_code); // There will still be something in the index buffer.
+
+ emit_code(eoi_code); // End Of Information.
+ // Flush / finalize the sub-blocks stream to the buffer.
+
+ emit_bytes_to_buffer(1); // Finish the sub-blocks, writing out any unfinished lengths and
+ // terminating with a sub-block of length 0. If we have already started
+ // but not yet used a sub-block it can just become the terminator.
+
+ if (cur_subblock + 1 === p) {
+ // Started but unused.
+ buf[cur_subblock] = 0;
+ } else {
+ // Started and used, write length and additional terminator block.
+ buf[cur_subblock] = p - cur_subblock - 1;
+ buf[p++] = 0;
+ }
+
+ return p;
+ }
+ }
+ /*
+ animatedGIF.js
+ ==============
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+ // Helpers
+
+
+ var noop$2 = function noop() {};
+
+ var AnimatedGIF = function AnimatedGIF(options) {
+ this.canvas = null;
+ this.ctx = null;
+ this.repeat = 0;
+ this.frames = [];
+ this.numRenderedFrames = 0;
+ this.onRenderCompleteCallback = noop$2;
+ this.onRenderProgressCallback = noop$2;
+ this.workers = [];
+ this.availableWorkers = [];
+ this.generatingGIF = false;
+ this.options = options; // Constructs and initializes the the web workers appropriately
+
+ this.initializeWebWorkers(options);
+ };
+
+ AnimatedGIF.prototype = {
+ 'workerMethods': workerCode(),
+ 'initializeWebWorkers': function initializeWebWorkers(options) {
+ var self = this;
+ var processFrameWorkerCode = NeuQuant.toString() + '(' + workerCode.toString() + '());';
+ var webWorkerObj = void 0;
+ var objectUrl = void 0;
+ var webWorker = void 0;
+ var numWorkers = void 0;
+ var x = -1;
+ var workerError = '';
+ numWorkers = options.numWorkers;
+
+ while (++x < numWorkers) {
+ webWorkerObj = utils.createWebWorker(processFrameWorkerCode);
+
+ if (utils.isObject(webWorkerObj)) {
+ objectUrl = webWorkerObj.objectUrl;
+ webWorker = webWorkerObj.worker;
+ self.workers.push({
+ worker: webWorker,
+ objectUrl: objectUrl
+ });
+ self.availableWorkers.push(webWorker);
+ } else {
+ workerError = webWorkerObj;
+ utils.webWorkerError = !!webWorkerObj;
+ }
+ }
+
+ this.workerError = workerError;
+ this.canvas = document.createElement('canvas');
+ this.canvas.width = options.gifWidth;
+ this.canvas.height = options.gifHeight;
+ this.ctx = this.canvas.getContext('2d');
+ this.frames = [];
+ },
+ // Return a worker for processing a frame
+ getWorker: function getWorker() {
+ return this.availableWorkers.pop();
+ },
+ // Restores a worker to the pool
+ freeWorker: function freeWorker(worker) {
+ this.availableWorkers.push(worker);
+ },
+ byteMap: function () {
+ var byteMap = [];
+
+ for (var i = 0; i < 256; i++) {
+ byteMap[i] = String.fromCharCode(i);
+ }
+
+ return byteMap;
+ }(),
+ bufferToString: function bufferToString(buffer) {
+ var numberValues = buffer.length;
+ var str = '';
+ var x = -1;
+
+ while (++x < numberValues) {
+ str += this.byteMap[buffer[x]];
+ }
+
+ return str;
+ },
+ onFrameFinished: function onFrameFinished(progressCallback) {
+ // The GIF is not written until we're done with all the frames
+ // because they might not be processed in the same order
+ var self = this;
+ var frames = self.frames;
+ var options = self.options;
+ var hasExistingImages = !!(options.images || []).length;
+ var allDone = frames.every(function (frame) {
+ return !frame.beingProcessed && frame.done;
+ });
+ self.numRenderedFrames++;
+
+ if (hasExistingImages) {
+ progressCallback(self.numRenderedFrames / frames.length);
+ }
+
+ self.onRenderProgressCallback(self.numRenderedFrames * 0.75 / frames.length);
+
+ if (allDone) {
+ if (!self.generatingGIF) {
+ self.generateGIF(frames, self.onRenderCompleteCallback);
+ }
+ } else {
+ utils.requestTimeout(function () {
+ self.processNextFrame();
+ }, 1);
+ }
+ },
+ processFrame: function processFrame(position) {
+ var AnimatedGifContext = this;
+ var options = this.options;
+ var _options = this.options,
+ progressCallback = _options.progressCallback,
+ sampleInterval = _options.sampleInterval;
+ var frames = this.frames;
+ var frame = void 0;
+ var worker = void 0;
+
+ var done = function done() {
+ var ev = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ var data = ev.data; // Delete original data, and free memory
+
+ delete frame.data;
+ frame.pixels = Array.prototype.slice.call(data.pixels);
+ frame.palette = Array.prototype.slice.call(data.palette);
+ frame.done = true;
+ frame.beingProcessed = false;
+ AnimatedGifContext.freeWorker(worker);
+ AnimatedGifContext.onFrameFinished(progressCallback);
+ };
+
+ frame = frames[position];
+
+ if (frame.beingProcessed || frame.done) {
+ this.onFrameFinished();
+ return;
+ }
+
+ frame.sampleInterval = sampleInterval;
+ frame.beingProcessed = true;
+ frame.gifshot = true;
+ worker = this.getWorker();
+
+ if (worker) {
+ // Process the frame in a web worker
+ worker.onmessage = done;
+ worker.postMessage(frame);
+ } else {
+ // Process the frame in the current thread
+ done({
+ 'data': AnimatedGifContext.workerMethods.run(frame)
+ });
+ }
+ },
+ startRendering: function startRendering(completeCallback) {
+ this.onRenderCompleteCallback = completeCallback;
+
+ for (var i = 0; i < this.options.numWorkers && i < this.frames.length; i++) {
+ this.processFrame(i);
+ }
+ },
+ processNextFrame: function processNextFrame() {
+ var position = -1;
+
+ for (var i = 0; i < this.frames.length; i++) {
+ var frame = this.frames[i];
+
+ if (!frame.done && !frame.beingProcessed) {
+ position = i;
+ break;
+ }
+ }
+
+ if (position >= 0) {
+ this.processFrame(position);
+ }
+ },
+ // Takes the already processed data in frames and feeds it to a new
+ // GifWriter instance in order to get the binary GIF file
+ generateGIF: function generateGIF(frames, callback) {
+ // TODO: Weird: using a simple JS array instead of a typed array,
+ // the files are WAY smaller o_o. Patches/explanations welcome!
+ var buffer = []; // new Uint8Array(width * height * frames.length * 5);
+
+ var gifOptions = {
+ loop: this.repeat
+ };
+ var options = this.options;
+ var interval = options.interval;
+ var frameDuration = options.frameDuration;
+ var existingImages = options.images;
+ var hasExistingImages = !!existingImages.length;
+ var height = options.gifHeight;
+ var width = options.gifWidth;
+ var gifWriter$$1 = new gifWriter(buffer, width, height, gifOptions);
+ var onRenderProgressCallback = this.onRenderProgressCallback;
+ var delay = hasExistingImages ? interval * 100 : 0;
+ var bufferToString = void 0;
+ var gif = void 0;
+ this.generatingGIF = true;
+ utils.each(frames, function (iterator, frame) {
+ var framePalette = frame.palette;
+ onRenderProgressCallback(0.75 + 0.25 * frame.position * 1.0 / frames.length);
+
+ for (var i = 0; i < frameDuration; i++) {
+ gifWriter$$1.addFrame(0, 0, width, height, frame.pixels, {
+ palette: framePalette,
+ delay: delay
+ });
+ }
+ });
+ gifWriter$$1.end();
+ onRenderProgressCallback(1.0);
+ this.frames = [];
+ this.generatingGIF = false;
+
+ if (utils.isFunction(callback)) {
+ bufferToString = this.bufferToString(buffer);
+ gif = 'data:image/gif;base64,' + utils.btoa(bufferToString);
+ callback(gif);
+ }
+ },
+ // From GIF: 0 = loop forever, null = not looping, n > 0 = loop n times and stop
+ setRepeat: function setRepeat(r) {
+ this.repeat = r;
+ },
+ addFrame: function addFrame(element, gifshotOptions) {
+ gifshotOptions = utils.isObject(gifshotOptions) ? gifshotOptions : {};
+ var self = this;
+ var ctx = self.ctx;
+ var options = self.options;
+ var width = options.gifWidth;
+ var height = options.gifHeight;
+ var fontSize = utils.getFontSize(gifshotOptions);
+ var _gifshotOptions = gifshotOptions,
+ filter = _gifshotOptions.filter,
+ fontColor = _gifshotOptions.fontColor,
+ fontFamily = _gifshotOptions.fontFamily,
+ fontWeight = _gifshotOptions.fontWeight,
+ gifHeight = _gifshotOptions.gifHeight,
+ gifWidth = _gifshotOptions.gifWidth,
+ text = _gifshotOptions.text,
+ textAlign = _gifshotOptions.textAlign,
+ textBaseline = _gifshotOptions.textBaseline;
+ var textXCoordinate = gifshotOptions.textXCoordinate ? gifshotOptions.textXCoordinate : textAlign === 'left' ? 1 : textAlign === 'right' ? width : width / 2;
+ var textYCoordinate = gifshotOptions.textYCoordinate ? gifshotOptions.textYCoordinate : textBaseline === 'top' ? 1 : textBaseline === 'center' ? height / 2 : height;
+ var font = fontWeight + ' ' + fontSize + ' ' + fontFamily;
+ var imageData = void 0;
+
+ try {
+ ctx.filter = filter;
+ ctx.drawImage(element, 0, 0, width, height);
+
+ if (text) {
+ ctx.font = font;
+ ctx.fillStyle = fontColor;
+ ctx.textAlign = textAlign;
+ ctx.textBaseline = textBaseline;
+ ctx.fillText(text, textXCoordinate, textYCoordinate);
+ }
+
+ imageData = ctx.getImageData(0, 0, width, height);
+ self.addFrameImageData(imageData);
+ } catch (e) {
+ return '' + e;
+ }
+ },
+ addFrameImageData: function addFrameImageData() {
+ var imageData = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ var frames = this.frames;
+ var imageDataArray = imageData.data;
+ this.frames.push({
+ 'data': imageDataArray,
+ 'width': imageData.width,
+ 'height': imageData.height,
+ 'palette': null,
+ 'dithering': null,
+ 'done': false,
+ 'beingProcessed': false,
+ 'position': frames.length
+ });
+ },
+ onRenderProgress: function onRenderProgress(callback) {
+ this.onRenderProgressCallback = callback;
+ },
+ isRendering: function isRendering() {
+ return this.generatingGIF;
+ },
+ getBase64GIF: function getBase64GIF(completeCallback) {
+ var self = this;
+
+ var onRenderComplete = function onRenderComplete(gif) {
+ self.destroyWorkers();
+ utils.requestTimeout(function () {
+ completeCallback(gif);
+ }, 0);
+ };
+
+ self.startRendering(onRenderComplete);
+ },
+ destroyWorkers: function destroyWorkers() {
+ if (this.workerError) {
+ return;
+ }
+
+ var workers = this.workers; // Explicitly ask web workers to die so they are explicitly GC'ed
+
+ utils.each(workers, function (iterator, workerObj) {
+ var worker = workerObj.worker;
+ var objectUrl = workerObj.objectUrl;
+ worker.terminate();
+ utils.URL.revokeObjectURL(objectUrl);
+ });
+ }
+ };
+ /*
+ getBase64GIF.js
+ ===============
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+
+ function getBase64GIF(animatedGifInstance, callback) {
+ // This is asynchronous, rendered with WebWorkers
+ animatedGifInstance.getBase64GIF(function (image) {
+ callback({
+ error: false,
+ errorCode: '',
+ errorMsg: '',
+ image: image
+ });
+ });
+ }
+ /*
+ existingImages.js
+ =================
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+
+
+ function existingImages() {
+ var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ var callback = obj.callback,
+ images = obj.images,
+ options = obj.options;
+ var imagesLength = obj.imagesLength;
+ var skipObj = {
+ 'getUserMedia': true,
+ 'window.URL': true
+ };
+ var errorObj = error.validate(skipObj);
+ var loadedImages = [];
+ var loadedImagesLength = 0;
+ var tempImage = void 0;
+ var ag = void 0;
+
+ if (errorObj.error) {
+ return callback(errorObj);
+ } // change workerPath to point to where Animated_GIF.worker.js is
+
+
+ ag = new AnimatedGIF(options);
+ utils.each(images, function (index, image) {
+ var currentImage = image;
+
+ if (image.src) {
+ currentImage = currentImage.src;
+ }
+
+ if (utils.isElement(currentImage)) {
+ if (options.crossOrigin) {
+ currentImage.crossOrigin = options.crossOrigin;
+ }
+
+ loadedImages[index] = currentImage;
+ loadedImagesLength += 1;
+
+ if (loadedImagesLength === imagesLength) {
+ addLoadedImagesToGif();
+ }
+ } else if (utils.isString(currentImage)) {
+ tempImage = new Image();
+
+ if (options.crossOrigin) {
+ tempImage.crossOrigin = options.crossOrigin;
+ }
+
+ (function (tempImage) {
+ if (image.text) {
+ tempImage.text = image.text;
+ }
+
+ tempImage.onerror = function (e) {
+ var obj = void 0;
+ --imagesLength; // skips over images that error out
+
+ if (imagesLength === 0) {
+ obj = {};
+ obj.error = 'None of the requested images was capable of being retrieved';
+ return callback(obj);
+ }
+ };
+
+ tempImage.onload = function (e) {
+ if (image.text) {
+ loadedImages[index] = {
+ img: tempImage,
+ text: tempImage.text
+ };
+ } else {
+ loadedImages[index] = tempImage;
+ }
+
+ loadedImagesLength += 1;
+
+ if (loadedImagesLength === imagesLength) {
+ addLoadedImagesToGif();
+ }
+
+ utils.removeElement(tempImage);
+ };
+
+ tempImage.src = currentImage;
+ })(tempImage);
+
+ utils.setCSSAttr(tempImage, {
+ position: 'fixed',
+ opacity: '0'
+ });
+ document.body.appendChild(tempImage);
+ }
+ });
+
+ function addLoadedImagesToGif() {
+ utils.each(loadedImages, function (index, loadedImage) {
+ if (loadedImage) {
+ if (loadedImage.text) {
+ ag.addFrame(loadedImage.img, options, loadedImage.text);
+ } else {
+ ag.addFrame(loadedImage, options);
+ }
+ }
+ });
+ getBase64GIF(ag, callback);
+ }
+ }
+ /*
+ screenShot.js
+ =============
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+ // Helpers
+
+
+ var noop$3 = function noop() {};
+
+ var screenShot = {
+ getGIF: function getGIF() {
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ var callback = arguments[1];
+ callback = utils.isFunction(callback) ? callback : noop$3;
+ var canvas = document.createElement('canvas');
+ var context = void 0;
+ var existingImages = options.images;
+ var hasExistingImages = !!existingImages.length;
+ var cameraStream = options.cameraStream,
+ crop = options.crop,
+ filter = options.filter,
+ fontColor = options.fontColor,
+ fontFamily = options.fontFamily,
+ fontWeight = options.fontWeight,
+ keepCameraOn = options.keepCameraOn,
+ numWorkers = options.numWorkers,
+ progressCallback = options.progressCallback,
+ saveRenderingContexts = options.saveRenderingContexts,
+ savedRenderingContexts = options.savedRenderingContexts,
+ text = options.text,
+ textAlign = options.textAlign,
+ textBaseline = options.textBaseline,
+ videoElement = options.videoElement,
+ videoHeight = options.videoHeight,
+ videoWidth = options.videoWidth,
+ webcamVideoElement = options.webcamVideoElement;
+ var gifWidth = Number(options.gifWidth);
+ var gifHeight = Number(options.gifHeight);
+ var interval = Number(options.interval);
+ var sampleInterval = Number(options.sampleInterval);
+ var waitBetweenFrames = hasExistingImages ? 0 : interval * 1000;
+ var renderingContextsToSave = [];
+ var numFrames = savedRenderingContexts.length ? savedRenderingContexts.length : options.numFrames;
+ var pendingFrames = numFrames;
+ var ag = new AnimatedGIF(options);
+ var fontSize = utils.getFontSize(options);
+ var textXCoordinate = options.textXCoordinate ? options.textXCoordinate : textAlign === 'left' ? 1 : textAlign === 'right' ? gifWidth : gifWidth / 2;
+ var textYCoordinate = options.textYCoordinate ? options.textYCoordinate : textBaseline === 'top' ? 1 : textBaseline === 'center' ? gifHeight / 2 : gifHeight;
+ var font = fontWeight + ' ' + fontSize + ' ' + fontFamily;
+ var sourceX = crop ? Math.floor(crop.scaledWidth / 2) : 0;
+ var sourceWidth = crop ? videoWidth - crop.scaledWidth : 0;
+ var sourceY = crop ? Math.floor(crop.scaledHeight / 2) : 0;
+ var sourceHeight = crop ? videoHeight - crop.scaledHeight : 0;
+
+ var captureFrames = function captureSingleFrame() {
+ var framesLeft = pendingFrames - 1;
+
+ if (savedRenderingContexts.length) {
+ context.putImageData(savedRenderingContexts[numFrames - pendingFrames], 0, 0);
+ finishCapture();
+ } else {
+ drawVideo();
+ }
+
+ function drawVideo() {
+ try {
+ // Makes sure the canvas video heights/widths are in bounds
+ if (sourceWidth > videoWidth) {
+ sourceWidth = videoWidth;
+ }
+
+ if (sourceHeight > videoHeight) {
+ sourceHeight = videoHeight;
+ }
+
+ if (sourceX < 0) {
+ sourceX = 0;
+ }
+
+ if (sourceY < 0) {
+ sourceY = 0;
+ }
+
+ context.filter = filter;
+ context.drawImage(videoElement, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, gifWidth, gifHeight);
+ finishCapture();
+ } catch (e) {
+ // There is a Firefox bug that sometimes throws NS_ERROR_NOT_AVAILABLE and
+ // and IndexSizeError errors when drawing a video element to the canvas
+ if (e.name === 'NS_ERROR_NOT_AVAILABLE') {
+ // Wait 100ms before trying again
+ utils.requestTimeout(drawVideo, 100);
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ function finishCapture() {
+ var imageData = void 0;
+
+ if (saveRenderingContexts) {
+ renderingContextsToSave.push(context.getImageData(0, 0, gifWidth, gifHeight));
+ } // If there is text to display, make sure to display it on the canvas after the image is drawn
+
+
+ if (text) {
+ context.font = font;
+ context.fillStyle = fontColor;
+ context.textAlign = textAlign;
+ context.textBaseline = textBaseline;
+ context.fillText(text, textXCoordinate, textYCoordinate);
+ }
+
+ imageData = context.getImageData(0, 0, gifWidth, gifHeight);
+ ag.addFrameImageData(imageData);
+ pendingFrames = framesLeft; // Call back with an r value indicating how far along we are in capture
+
+ progressCallback((numFrames - pendingFrames) / numFrames);
+
+ if (framesLeft > 0) {
+ // test
+ utils.requestTimeout(captureSingleFrame, waitBetweenFrames);
+ }
+
+ if (!pendingFrames) {
+ ag.getBase64GIF(function (image) {
+ callback({
+ 'error': false,
+ 'errorCode': '',
+ 'errorMsg': '',
+ 'image': image,
+ 'cameraStream': cameraStream,
+ 'videoElement': videoElement,
+ 'webcamVideoElement': webcamVideoElement,
+ 'savedRenderingContexts': renderingContextsToSave,
+ 'keepCameraOn': keepCameraOn
+ });
+ });
+ }
+ }
+ };
+
+ numFrames = numFrames !== undefined ? numFrames : 10;
+ interval = interval !== undefined ? interval : 0.1; // In seconds
+
+ canvas.width = gifWidth;
+ canvas.height = gifHeight;
+ context = canvas.getContext('2d');
+
+ (function capture() {
+ if (!savedRenderingContexts.length && videoElement.currentTime === 0) {
+ utils.requestTimeout(capture, 100);
+ return;
+ }
+
+ captureFrames();
+ })();
+ },
+ getCropDimensions: function getCropDimensions() {
+ var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ var width = obj.videoWidth;
+ var height = obj.videoHeight;
+ var gifWidth = obj.gifWidth;
+ var gifHeight = obj.gifHeight;
+ var result = {
+ width: 0,
+ height: 0,
+ scaledWidth: 0,
+ scaledHeight: 0
+ };
+
+ if (width > height) {
+ result.width = Math.round(width * (gifHeight / height)) - gifWidth;
+ result.scaledWidth = Math.round(result.width * (height / gifHeight));
+ } else {
+ result.height = Math.round(height * (gifWidth / width)) - gifHeight;
+ result.scaledHeight = Math.round(result.height * (width / gifWidth));
+ }
+
+ return result;
+ }
+ };
+ /*
+ videoStream.js
+ ==============
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+
+ var videoStream = {
+ loadedData: false,
+ defaultVideoDimensions: {
+ width: 640,
+ height: 480
+ },
+ findVideoSize: function findVideoSizeMethod(obj) {
+ findVideoSizeMethod.attempts = findVideoSizeMethod.attempts || 0;
+ var cameraStream = obj.cameraStream,
+ completedCallback = obj.completedCallback,
+ videoElement = obj.videoElement;
+
+ if (!videoElement) {
+ return;
+ }
+
+ if (videoElement.videoWidth > 0 && videoElement.videoHeight > 0) {
+ videoElement.removeEventListener('loadeddata', videoStream.findVideoSize);
+ completedCallback({
+ videoElement: videoElement,
+ cameraStream: cameraStream,
+ videoWidth: videoElement.videoWidth,
+ videoHeight: videoElement.videoHeight
+ });
+ } else {
+ if (findVideoSizeMethod.attempts < 10) {
+ findVideoSizeMethod.attempts += 1;
+ utils.requestTimeout(function () {
+ videoStream.findVideoSize(obj);
+ }, 400);
+ } else {
+ completedCallback({
+ videoElement: videoElement,
+ cameraStream: cameraStream,
+ videoWidth: videoStream.defaultVideoDimensions.width,
+ videoHeight: videoStream.defaultVideoDimensions.height
+ });
+ }
+ }
+ },
+ onStreamingTimeout: function onStreamingTimeout(callback) {
+ if (utils.isFunction(callback)) {
+ callback({
+ error: true,
+ errorCode: 'getUserMedia',
+ errorMsg: 'There was an issue with the getUserMedia API - Timed out while trying to start streaming',
+ image: null,
+ cameraStream: {}
+ });
+ }
+ },
+ stream: function stream(obj) {
+ var existingVideo = utils.isArray(obj.existingVideo) ? obj.existingVideo[0] : obj.existingVideo;
+ var cameraStream = obj.cameraStream,
+ completedCallback = obj.completedCallback,
+ streamedCallback = obj.streamedCallback,
+ videoElement = obj.videoElement;
+
+ if (utils.isFunction(streamedCallback)) {
+ streamedCallback();
+ }
+
+ if (existingVideo) {
+ if (utils.isString(existingVideo)) {
+ videoElement.src = existingVideo;
+ videoElement.innerHTML = '';
+ } else if (existingVideo instanceof Blob) {
+ try {
+ videoElement.src = utils.URL.createObjectURL(existingVideo);
+ } catch (e) {}
+
+ videoElement.innerHTML = '';
+ }
+ } else if (videoElement.mozSrcObject) {
+ videoElement.mozSrcObject = cameraStream;
+ } else if (utils.URL) {
+ try {
+ videoElement.srcObject = cameraStream;
+ videoElement.src = utils.URL.createObjectURL(cameraStream);
+ } catch (e) {
+ videoElement.srcObject = cameraStream;
+ }
+ }
+
+ videoElement.play();
+ utils.requestTimeout(function checkLoadedData() {
+ checkLoadedData.count = checkLoadedData.count || 0;
+
+ if (videoStream.loadedData === true) {
+ videoStream.findVideoSize({
+ videoElement: videoElement,
+ cameraStream: cameraStream,
+ completedCallback: completedCallback
+ });
+ videoStream.loadedData = false;
+ } else {
+ checkLoadedData.count += 1;
+
+ if (checkLoadedData.count > 10) {
+ videoStream.findVideoSize({
+ videoElement: videoElement,
+ cameraStream: cameraStream,
+ completedCallback: completedCallback
+ });
+ } else {
+ checkLoadedData();
+ }
+ }
+ }, 0);
+ },
+ startStreaming: function startStreaming(obj) {
+ var errorCallback = utils.isFunction(obj.error) ? obj.error : utils.noop;
+ var streamedCallback = utils.isFunction(obj.streamed) ? obj.streamed : utils.noop;
+ var completedCallback = utils.isFunction(obj.completed) ? obj.completed : utils.noop;
+ var crossOrigin = obj.crossOrigin,
+ existingVideo = obj.existingVideo,
+ lastCameraStream = obj.lastCameraStream,
+ options = obj.options,
+ webcamVideoElement = obj.webcamVideoElement;
+ var videoElement = utils.isElement(existingVideo) ? existingVideo : webcamVideoElement ? webcamVideoElement : document.createElement('video');
+
+ if (crossOrigin) {
+ videoElement.crossOrigin = options.crossOrigin;
+ }
+
+ videoElement.autoplay = true;
+ videoElement.loop = true;
+ videoElement.muted = true;
+ videoElement.addEventListener('loadeddata', function (event) {
+ videoStream.loadedData = true;
+
+ if (options.offset) {
+ videoElement.currentTime = options.offset;
+ }
+ });
+
+ if (existingVideo) {
+ videoStream.stream({
+ videoElement: videoElement,
+ existingVideo: existingVideo,
+ completedCallback: completedCallback
+ });
+ } else if (lastCameraStream) {
+ videoStream.stream({
+ videoElement: videoElement,
+ cameraStream: lastCameraStream,
+ streamedCallback: streamedCallback,
+ completedCallback: completedCallback
+ });
+ } else {
+ utils.getUserMedia({
+ video: true
+ }, function (stream) {
+ videoStream.stream({
+ videoElement: videoElement,
+ cameraStream: stream,
+ streamedCallback: streamedCallback,
+ completedCallback: completedCallback
+ });
+ }, errorCallback);
+ }
+ },
+ startVideoStreaming: function startVideoStreaming(callback) {
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+ var timeoutLength = options.timeout !== undefined ? options.timeout : 0;
+ var originalCallback = options.callback;
+ var webcamVideoElement = options.webcamVideoElement;
+ var noGetUserMediaSupportTimeout = void 0; // Some browsers apparently have support for video streaming because of the
+ // presence of the getUserMedia function, but then do not answer our
+ // calls for streaming.
+ // So we'll set up this timeout and if nothing happens after a while, we'll
+ // conclude that there's no actual getUserMedia support.
+
+ if (timeoutLength > 0) {
+ noGetUserMediaSupportTimeout = utils.requestTimeout(function () {
+ videoStream.onStreamingTimeout(originalCallback);
+ }, 10000);
+ }
+
+ videoStream.startStreaming({
+ error: function error() {
+ originalCallback({
+ error: true,
+ errorCode: 'getUserMedia',
+ errorMsg: 'There was an issue with the getUserMedia API - the user probably denied permission',
+ image: null,
+ cameraStream: {}
+ });
+ },
+ streamed: function streamed() {
+ // The streaming started somehow, so we can assume there is getUserMedia support
+ clearTimeout(noGetUserMediaSupportTimeout);
+ },
+ completed: function completed() {
+ var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ var cameraStream = obj.cameraStream,
+ videoElement = obj.videoElement,
+ videoHeight = obj.videoHeight,
+ videoWidth = obj.videoWidth;
+ callback({
+ cameraStream: cameraStream,
+ videoElement: videoElement,
+ videoHeight: videoHeight,
+ videoWidth: videoWidth
+ });
+ },
+ lastCameraStream: options.lastCameraStream,
+ webcamVideoElement: webcamVideoElement,
+ crossOrigin: options.crossOrigin,
+ options: options
+ });
+ },
+ stopVideoStreaming: function stopVideoStreaming(obj) {
+ obj = utils.isObject(obj) ? obj : {};
+ var _obj = obj,
+ keepCameraOn = _obj.keepCameraOn,
+ videoElement = _obj.videoElement,
+ webcamVideoElement = _obj.webcamVideoElement;
+ var cameraStream = obj.cameraStream || {};
+ var cameraStreamTracks = cameraStream.getTracks ? cameraStream.getTracks() || [] : [];
+ var hasCameraStreamTracks = !!cameraStreamTracks.length;
+ var firstCameraStreamTrack = cameraStreamTracks[0];
+
+ if (!keepCameraOn && hasCameraStreamTracks) {
+ if (utils.isFunction(firstCameraStreamTrack.stop)) {
+ // Stops the camera stream
+ firstCameraStreamTrack.stop();
+ }
+ }
+
+ if (utils.isElement(videoElement) && !webcamVideoElement) {
+ // Pauses the video, revokes the object URL (freeing up memory), and remove the video element
+ videoElement.pause(); // Destroys the object url
+
+ if (utils.isFunction(utils.URL.revokeObjectURL) && !utils.webWorkerError) {
+ if (videoElement.src) {
+ utils.URL.revokeObjectURL(videoElement.src);
+ }
+ } // Removes the video element from the DOM
+
+
+ utils.removeElement(videoElement);
+ }
+ }
+ };
+ /*
+ stopVideoStreaming.js
+ =====================
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+
+ function stopVideoStreaming(options) {
+ options = utils.isObject(options) ? options : {};
+ videoStream.stopVideoStreaming(options);
+ }
+ /*
+ createAndGetGIF.js
+ ==================
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+
+
+ function createAndGetGIF(obj, callback) {
+ var options = obj.options || {};
+ var images = options.images,
+ video = options.video;
+ var gifWidth = Number(options.gifWidth);
+ var gifHeight = Number(options.gifHeight);
+ var numFrames = Number(options.numFrames);
+ var cameraStream = obj.cameraStream,
+ videoElement = obj.videoElement,
+ videoWidth = obj.videoWidth,
+ videoHeight = obj.videoHeight;
+ var cropDimensions = screenShot.getCropDimensions({
+ videoWidth: videoWidth,
+ videoHeight: videoHeight,
+ gifHeight: gifHeight,
+ gifWidth: gifWidth
+ });
+ var completeCallback = callback;
+ options.crop = cropDimensions;
+ options.videoElement = videoElement;
+ options.videoWidth = videoWidth;
+ options.videoHeight = videoHeight;
+ options.cameraStream = cameraStream;
+
+ if (!utils.isElement(videoElement)) {
+ return;
+ }
+
+ videoElement.width = gifWidth + cropDimensions.width;
+ videoElement.height = gifHeight + cropDimensions.height;
+
+ if (!options.webcamVideoElement) {
+ utils.setCSSAttr(videoElement, {
+ position: 'fixed',
+ opacity: '0'
+ });
+ document.body.appendChild(videoElement);
+ } // Firefox doesn't seem to obey autoplay if the element is not in the DOM when the content
+ // is loaded, so we must manually trigger play after adding it, or the video will be frozen
+
+
+ videoElement.play();
+ screenShot.getGIF(options, function (obj) {
+ if ((!images || !images.length) && (!video || !video.length)) {
+ stopVideoStreaming(obj);
+ }
+
+ completeCallback(obj);
+ });
+ }
+ /*
+ existingVideo.js
+ ================
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+
+
+ function existingVideo() {
+ var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ var callback = obj.callback,
+ existingVideo = obj.existingVideo,
+ options = obj.options;
+ var skipObj = {
+ getUserMedia: true,
+ 'window.URL': true
+ };
+ var errorObj = error.validate(skipObj);
+ var videoType = void 0;
+ var videoSrc = void 0;
+
+ if (errorObj.error) {
+ return callback(errorObj);
+ }
+
+ if (utils.isElement(existingVideo) && existingVideo.src) {
+ videoSrc = existingVideo.src;
+ videoType = utils.getExtension(videoSrc);
+
+ if (!utils.isSupported.videoCodecs[videoType]) {
+ return callback(error.messages.videoCodecs);
+ }
+ } else if (utils.isArray(existingVideo)) {
+ utils.each(existingVideo, function (iterator, videoSrc) {
+ if (videoSrc instanceof Blob) {
+ videoType = videoSrc.type.substr(videoSrc.type.lastIndexOf('/') + 1, videoSrc.length);
+ } else {
+ videoType = videoSrc.substr(videoSrc.lastIndexOf('.') + 1, videoSrc.length);
+ }
+
+ if (utils.isSupported.videoCodecs[videoType]) {
+ existingVideo = videoSrc;
+ return false;
+ }
+ });
+ }
+
+ videoStream.startStreaming({
+ completed: function completed(obj) {
+ obj.options = options || {};
+ createAndGetGIF(obj, callback);
+ },
+ existingVideo: existingVideo,
+ crossOrigin: options.crossOrigin,
+ options: options
+ });
+ }
+ /*
+ existingWebcam.js
+ =================
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+
+
+ function existingWebcam() {
+ var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ var callback = obj.callback,
+ lastCameraStream = obj.lastCameraStream,
+ options = obj.options,
+ webcamVideoElement = obj.webcamVideoElement;
+
+ if (!isWebCamGIFSupported()) {
+ return callback(error.validate());
+ }
+
+ if (options.savedRenderingContexts.length) {
+ screenShot.getGIF(options, function (obj) {
+ callback(obj);
+ });
+ return;
+ }
+
+ videoStream.startVideoStreaming(function () {
+ var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ obj.options = options || {};
+ createAndGetGIF(obj, callback);
+ }, {
+ lastCameraStream: lastCameraStream,
+ callback: callback,
+ webcamVideoElement: webcamVideoElement,
+ crossOrigin: options.crossOrigin
+ });
+ }
+ /*
+ createGIF.js
+ ============
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+
+
+ function createGIF(userOptions, callback) {
+ callback = utils.isFunction(userOptions) ? userOptions : callback;
+ userOptions = utils.isObject(userOptions) ? userOptions : {};
+
+ if (!utils.isFunction(callback)) {
+ return;
+ }
+
+ var options = utils.mergeOptions(defaultOptions, userOptions) || {};
+ var lastCameraStream = userOptions.cameraStream;
+ var images = options.images;
+ var imagesLength = images ? images.length : 0;
+ var video = options.video;
+ var webcamVideoElement = options.webcamVideoElement;
+ options = utils.mergeOptions(options, {
+ 'gifWidth': Math.floor(options.gifWidth),
+ 'gifHeight': Math.floor(options.gifHeight)
+ }); // If the user would like to create a GIF from an existing image(s)
+
+ if (imagesLength) {
+ existingImages({
+ 'images': images,
+ 'imagesLength': imagesLength,
+ 'callback': callback,
+ 'options': options
+ });
+ } else if (video) {
+ // If the user would like to create a GIF from an existing HTML5 video
+ existingVideo({
+ 'existingVideo': video,
+ callback: callback,
+ options: options
+ });
+ } else {
+ // If the user would like to create a GIF from a webcam stream
+ existingWebcam({
+ lastCameraStream: lastCameraStream,
+ callback: callback,
+ webcamVideoElement: webcamVideoElement,
+ options: options
+ });
+ }
+ }
+ /*
+ takeSnapShot.js
+ ===============
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+
+
+ function takeSnapShot(userOptions, callback) {
+ callback = utils.isFunction(userOptions) ? userOptions : callback;
+ userOptions = utils.isObject(userOptions) ? userOptions : {};
+
+ if (!utils.isFunction(callback)) {
+ return;
+ }
+
+ var mergedOptions = utils.mergeOptions(defaultOptions, userOptions);
+ var options = utils.mergeOptions(mergedOptions, {
+ 'interval': .1,
+ 'numFrames': 1,
+ 'gifWidth': Math.floor(mergedOptions.gifWidth),
+ 'gifHeight': Math.floor(mergedOptions.gifHeight)
+ });
+ createGIF(options, callback);
+ }
+ /*
+ API.js
+ ======
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+
+
+ var API = {
+ 'utils': utils$2,
+ 'error': error$2,
+ 'defaultOptions': defaultOptions$2,
+ 'createGIF': createGIF,
+ 'takeSnapShot': takeSnapShot,
+ 'stopVideoStreaming': stopVideoStreaming,
+ 'isSupported': isSupported,
+ 'isWebCamGIFSupported': isWebCamGIFSupported,
+ 'isExistingVideoGIFSupported': isExistingVideoGIFSupported,
+ 'isExistingImagesGIFSupported': isSupported$1,
+ 'VERSION': '0.4.5'
+ };
+ /*
+ index.js
+ ========
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, and plain browser loading
+
+ if (typeof undefined === 'function' && undefined.amd) {
+ undefined([], function () {
+ return API;
+ });
+ } else {
+ module.exports = API;
+ }
+ })(typeof window !== "undefined" ? window : {}, typeof document !== "undefined" ? document : {
+ createElement: function createElement() {}
+ }, typeof window !== "undefined" ? window.navigator : {});
+ });
+
+ function artplayerPluginGif(art) {
+ return {
+ name: 'artplayerPluginGif',
+ create: function create() {
+ var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ var callback = arguments.length > 1 ? arguments[1] : undefined;
+ gifshot.createGIF(objectSpread({}, config, {
+ video: [art.template.$video.src]
+ }), function (obj) {
+ if (!obj.error) {
+ if (callback) {
+ callback(obj.image);
+ }
+ } else {
+ console.log(obj.error);
+ }
+ });
+ }
+ };
+ }
+
+ window.artplayerPluginGif = artplayerPluginGif;
+
+ exports.default = artplayerPluginGif;
+
+ Object.defineProperty(exports, '__esModule', { value: true });
})));
diff --git a/packages/artplayer-plugin-gif/src/index.js b/packages/artplayer-plugin-gif/src/index.js
index 74657325b..766cc4557 100644
--- a/packages/artplayer-plugin-gif/src/index.js
+++ b/packages/artplayer-plugin-gif/src/index.js
@@ -1,9 +1,25 @@
import gifshot from 'gifshot';
function artplayerPluginGif(art) {
- console.log(gifshot);
return {
name: 'artplayerPluginGif',
+ create(config = {}, callback) {
+ gifshot.createGIF(
+ {
+ ...config,
+ video: [art.template.$video.src],
+ },
+ obj => {
+ if (!obj.error) {
+ if (callback) {
+ callback(obj.image);
+ }
+ } else {
+ console.log(obj.error);
+ }
+ },
+ );
+ },
};
}