@loaders.gl/video 4.0.0-alpha.4 → 4.0.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/dist/bundle.d.ts +2 -0
  2. package/dist/bundle.d.ts.map +1 -0
  3. package/dist/bundle.js +2 -2
  4. package/dist/dist.min.js +2147 -0
  5. package/dist/es5/bundle.js +6 -0
  6. package/dist/es5/bundle.js.map +1 -0
  7. package/dist/es5/gif-builder.js +200 -0
  8. package/dist/es5/gif-builder.js.map +1 -0
  9. package/dist/es5/index.js +21 -0
  10. package/dist/es5/index.js.map +1 -0
  11. package/dist/es5/lib/gifshot/gifshot-loader.js +67 -0
  12. package/dist/es5/lib/gifshot/gifshot-loader.js.map +1 -0
  13. package/dist/es5/lib/gifshot/gifshot.js +1924 -0
  14. package/dist/es5/lib/gifshot/gifshot.js.map +1 -0
  15. package/dist/es5/lib/parsers/parse-video.js +31 -0
  16. package/dist/es5/lib/parsers/parse-video.js.map +1 -0
  17. package/dist/es5/lib/utils/assert.js +12 -0
  18. package/dist/es5/lib/utils/assert.js.map +1 -0
  19. package/dist/es5/video-loader.js +28 -0
  20. package/dist/es5/video-loader.js.map +1 -0
  21. package/dist/esm/bundle.js +4 -0
  22. package/dist/esm/bundle.js.map +1 -0
  23. package/dist/esm/gif-builder.js +106 -0
  24. package/dist/esm/gif-builder.js.map +1 -0
  25. package/dist/esm/index.js +3 -0
  26. package/dist/esm/index.js.map +1 -0
  27. package/{src → dist/esm}/lib/gifshot/gifshot-loader.js +4 -8
  28. package/dist/esm/lib/gifshot/gifshot-loader.js.map +1 -0
  29. package/dist/esm/lib/gifshot/gifshot.js +1915 -0
  30. package/dist/esm/lib/gifshot/gifshot.js.map +1 -0
  31. package/dist/esm/lib/parsers/parse-video.js +7 -0
  32. package/dist/esm/lib/parsers/parse-video.js.map +1 -0
  33. package/{src → dist/esm}/lib/utils/assert.js +1 -0
  34. package/dist/esm/lib/utils/assert.js.map +1 -0
  35. package/dist/esm/video-loader.js +19 -0
  36. package/dist/esm/video-loader.js.map +1 -0
  37. package/dist/gif-builder.d.ts +53 -0
  38. package/dist/gif-builder.d.ts.map +1 -0
  39. package/dist/gif-builder.js +136 -114
  40. package/dist/index.d.ts +3 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +10 -3
  43. package/dist/lib/gifshot/gifshot-loader.d.ts +2 -0
  44. package/dist/lib/gifshot/gifshot-loader.d.ts.map +1 -0
  45. package/dist/lib/gifshot/gifshot-loader.js +18 -15
  46. package/dist/lib/gifshot/gifshot.d.ts +72 -0
  47. package/dist/lib/gifshot/gifshot.d.ts.map +1 -0
  48. package/dist/lib/gifshot/gifshot.js +2439 -0
  49. package/dist/lib/parsers/parse-video.d.ts +2 -0
  50. package/dist/lib/parsers/parse-video.d.ts.map +1 -0
  51. package/dist/lib/parsers/parse-video.js +12 -6
  52. package/dist/lib/utils/assert.d.ts +2 -0
  53. package/dist/lib/utils/assert.d.ts.map +1 -0
  54. package/dist/lib/utils/assert.js +8 -5
  55. package/dist/video-loader.d.ts +17 -0
  56. package/dist/video-loader.d.ts.map +1 -0
  57. package/dist/video-loader.js +22 -14
  58. package/package.json +8 -8
  59. package/src/{gif-builder.js → gif-builder.ts} +6 -6
  60. package/src/lib/gifshot/gifshot.ts +2416 -0
  61. package/dist/bundle.js.map +0 -1
  62. package/dist/gif-builder.js.map +0 -1
  63. package/dist/index.js.map +0 -1
  64. package/dist/lib/gifshot/gifshot-loader.js.map +0 -1
  65. package/dist/lib/parsers/parse-video.js.map +0 -1
  66. package/dist/lib/utils/assert.js.map +0 -1
  67. package/dist/lib/utils/globals.js +0 -16
  68. package/dist/lib/utils/globals.js.map +0 -1
  69. package/dist/libs/gifshot.js +0 -2826
  70. package/dist/video-loader.js.map +0 -1
  71. package/src/lib/utils/globals.js +0 -25
  72. package/src/libs/gifshot.js +0 -2826
@@ -1,2826 +0,0 @@
1
- // @ts-nocheck
2
- /*Copyrights for code authored by Yahoo Inc. is licensed under the following terms:
3
- MIT License
4
- Copyright 2017 Yahoo Inc.
5
- 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:
6
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7
- 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.
8
- */
9
- ;(function(window, document, navigator, undefined) {
10
- "use strict";
11
-
12
- /*
13
- utils.js
14
- ========
15
- */
16
-
17
- /* Copyright 2017 Yahoo Inc.
18
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
19
- */
20
-
21
- var utils = {
22
- URL: window.URL || window.webkitURL || window.mozURL || window.msURL,
23
- getUserMedia: function () {
24
- var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
25
-
26
- return getUserMedia ? getUserMedia.bind(navigator) : getUserMedia;
27
- }(),
28
- requestAnimFrame: window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame,
29
- requestTimeout: function requestTimeout(callback, delay) {
30
- callback = callback || utils.noop;
31
- delay = delay || 0;
32
-
33
- if (!utils.requestAnimFrame) {
34
- return setTimeout(callback, delay);
35
- }
36
-
37
- var start = new Date().getTime();
38
- var handle = new Object();
39
- var requestAnimFrame = utils.requestAnimFrame;
40
-
41
- var loop = function loop() {
42
- var current = new Date().getTime();
43
- var delta = current - start;
44
-
45
- delta >= delay ? callback.call() : handle.value = requestAnimFrame(loop);
46
- };
47
-
48
- handle.value = requestAnimFrame(loop);
49
-
50
- return handle;
51
- },
52
- Blob: window.Blob || window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder,
53
- btoa: function () {
54
- var btoa = window.btoa || function (input) {
55
- var output = '';
56
- var i = 0;
57
- var l = input.length;
58
- var key = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
59
- var chr1 = void 0;
60
- var chr2 = void 0;
61
- var chr3 = void 0;
62
- var enc1 = void 0;
63
- var enc2 = void 0;
64
- var enc3 = void 0;
65
- var enc4 = void 0;
66
-
67
- while (i < l) {
68
- chr1 = input.charCodeAt(i++);
69
- chr2 = input.charCodeAt(i++);
70
- chr3 = input.charCodeAt(i++);
71
- enc1 = chr1 >> 2;
72
- enc2 = (chr1 & 3) << 4 | chr2 >> 4;
73
- enc3 = (chr2 & 15) << 2 | chr3 >> 6;
74
- enc4 = chr3 & 63;
75
-
76
- if (isNaN(chr2)) {
77
- enc3 = enc4 = 64;
78
- } else if (isNaN(chr3)) {
79
- enc4 = 64;
80
- }
81
-
82
- output = output + key.charAt(enc1) + key.charAt(enc2) + key.charAt(enc3) + key.charAt(enc4);
83
- }
84
-
85
- return output;
86
- };
87
-
88
- return btoa ? btoa.bind(window) : utils.noop;
89
- }(),
90
- isObject: function isObject(obj) {
91
- return obj && Object.prototype.toString.call(obj) === '[object Object]';
92
- },
93
- isEmptyObject: function isEmptyObject(obj) {
94
- return utils.isObject(obj) && !Object.keys(obj).length;
95
- },
96
- isArray: function isArray(arr) {
97
- return arr && Array.isArray(arr);
98
- },
99
- isFunction: function isFunction(func) {
100
- return func && typeof func === 'function';
101
- },
102
- isElement: function isElement(elem) {
103
- return elem && elem.nodeType === 1;
104
- },
105
- isString: function isString(value) {
106
- return typeof value === 'string' || Object.prototype.toString.call(value) === '[object String]';
107
- },
108
- isSupported: {
109
- canvas: function canvas() {
110
- var el = document.createElement('canvas');
111
-
112
- return el && el.getContext && el.getContext('2d');
113
- },
114
- webworkers: function webworkers() {
115
- return window.Worker;
116
- },
117
- blob: function blob() {
118
- return utils.Blob;
119
- },
120
- Uint8Array: function Uint8Array() {
121
- return window.Uint8Array;
122
- },
123
- Uint32Array: function Uint32Array() {
124
- return window.Uint32Array;
125
- },
126
- videoCodecs: function () {
127
- var testEl = document.createElement('video');
128
- var supportObj = {
129
- 'mp4': false,
130
- 'h264': false,
131
- 'ogv': false,
132
- 'ogg': false,
133
- 'webm': false
134
- };
135
-
136
- try {
137
- if (testEl && testEl.canPlayType) {
138
- // Check for MPEG-4 support
139
- supportObj.mp4 = testEl.canPlayType('video/mp4; codecs="mp4v.20.8"') !== '';
140
-
141
- // Check for h264 support
142
- supportObj.h264 = (testEl.canPlayType('video/mp4; codecs="avc1.42E01E"') || testEl.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"')) !== '';
143
-
144
- // Check for Ogv support
145
- supportObj.ogv = testEl.canPlayType('video/ogg; codecs="theora"') !== '';
146
-
147
- // Check for Ogg support
148
- supportObj.ogg = testEl.canPlayType('video/ogg; codecs="theora"') !== '';
149
-
150
- // Check for Webm support
151
- supportObj.webm = testEl.canPlayType('video/webm; codecs="vp8, vorbis"') !== -1;
152
- }
153
- } catch (e) {}
154
-
155
- return supportObj;
156
- }()
157
- },
158
- noop: function noop() {},
159
- each: function each(collection, callback) {
160
- var x = void 0;
161
- var len = void 0;
162
-
163
- if (utils.isArray(collection)) {
164
- x = -1;
165
- len = collection.length;
166
-
167
- while (++x < len) {
168
- if (callback(x, collection[x]) === false) {
169
- break;
170
- }
171
- }
172
- } else if (utils.isObject(collection)) {
173
- for (x in collection) {
174
- if (collection.hasOwnProperty(x)) {
175
- if (callback(x, collection[x]) === false) {
176
- break;
177
- }
178
- }
179
- }
180
- }
181
- },
182
- normalizeOptions: function normalizeOptions(defaultOptions, userOptions) {
183
- if (!utils.isObject(defaultOptions) || !utils.isObject(userOptions) || !Object.keys) {
184
- return;
185
- }
186
-
187
- var newObj = {};
188
-
189
- utils.each(defaultOptions, function (key, val) {
190
- newObj[key] = defaultOptions[key];
191
- });
192
-
193
- utils.each(userOptions, function (key, val) {
194
- var currentUserOption = userOptions[key];
195
-
196
- if (!utils.isObject(currentUserOption)) {
197
- newObj[key] = currentUserOption;
198
- } else {
199
- if (!defaultOptions[key]) {
200
- newObj[key] = currentUserOption;
201
- } else {
202
- newObj[key] = utils.normalizeOptions(defaultOptions[key], currentUserOption);
203
- }
204
- }
205
- });
206
-
207
- return newObj;
208
- },
209
- setCSSAttr: function setCSSAttr(elem, attr, val) {
210
- if (!utils.isElement(elem)) {
211
- return;
212
- }
213
-
214
- if (utils.isString(attr) && utils.isString(val)) {
215
- elem.style[attr] = val;
216
- } else if (utils.isObject(attr)) {
217
- utils.each(attr, function (key, val) {
218
- elem.style[key] = val;
219
- });
220
- }
221
- },
222
- removeElement: function removeElement(node) {
223
- if (!utils.isElement(node)) {
224
- return;
225
- }
226
- if (node.parentNode) {
227
- node.parentNode.removeChild(node);
228
- }
229
- },
230
- createWebWorker: function createWebWorker(content) {
231
- if (!utils.isString(content)) {
232
- return {};
233
- }
234
-
235
- try {
236
- var blob = new utils.Blob([content], {
237
- 'type': 'text/javascript'
238
- });
239
- var objectUrl = utils.URL.createObjectURL(blob);
240
- var worker = new Worker(objectUrl);
241
-
242
- return {
243
- 'objectUrl': objectUrl,
244
- 'worker': worker
245
- };
246
- } catch (e) {
247
- return '' + e;
248
- }
249
- },
250
- getExtension: function getExtension(src) {
251
- return src.substr(src.lastIndexOf('.') + 1, src.length);
252
- },
253
- getFontSize: function getFontSize() {
254
- var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
255
-
256
- if (!document.body || options.resizeFont === false) {
257
- return options.fontSize;
258
- }
259
-
260
- var text = options.text;
261
- var containerWidth = options.gifWidth;
262
- var fontSize = parseInt(options.fontSize, 10);
263
- var minFontSize = parseInt(options.minFontSize, 10);
264
- var div = document.createElement('div');
265
- var span = document.createElement('span');
266
-
267
- div.setAttribute('width', containerWidth);
268
- div.appendChild(span);
269
-
270
- span.innerHTML = text;
271
- span.style.fontSize = fontSize + 'px';
272
- span.style.textIndent = '-9999px';
273
- span.style.visibility = 'hidden';
274
-
275
- document.body.appendChild(span);
276
-
277
- while (span.offsetWidth > containerWidth && fontSize >= minFontSize) {
278
- span.style.fontSize = --fontSize + 'px';
279
- }
280
-
281
- document.body.removeChild(span);
282
-
283
- return fontSize + 'px';
284
- },
285
- webWorkerError: false
286
- };
287
-
288
-
289
-
290
- var utils$2 = Object.freeze({
291
- default: utils
292
- });
293
-
294
- /*
295
- error.js
296
- ========
297
- */
298
-
299
- /* Copyright 2017 Yahoo Inc.
300
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
301
- */
302
-
303
- // Dependencies
304
- var error = {
305
- validate: function validate(skipObj) {
306
- skipObj = utils.isObject(skipObj) ? skipObj : {};
307
-
308
- var errorObj = {};
309
-
310
- utils.each(error.validators, function (indece, currentValidator) {
311
- var errorCode = currentValidator.errorCode;
312
-
313
- if (!skipObj[errorCode] && !currentValidator.condition) {
314
- errorObj = currentValidator;
315
- errorObj.error = true;
316
-
317
- return false;
318
- }
319
- });
320
-
321
- delete errorObj.condition;
322
-
323
- return errorObj;
324
- },
325
- isValid: function isValid(skipObj) {
326
- var errorObj = error.validate(skipObj);
327
- var isValid = errorObj.error !== true ? true : false;
328
-
329
- return isValid;
330
- },
331
- validators: [{
332
- condition: utils.isFunction(utils.getUserMedia),
333
- errorCode: 'getUserMedia',
334
- errorMsg: 'The getUserMedia API is not supported in your browser'
335
- }, {
336
- condition: utils.isSupported.canvas(),
337
- errorCode: 'canvas',
338
- errorMsg: 'Canvas elements are not supported in your browser'
339
- }, {
340
- condition: utils.isSupported.webworkers(),
341
- errorCode: 'webworkers',
342
- errorMsg: 'The Web Workers API is not supported in your browser'
343
- }, {
344
- condition: utils.isFunction(utils.URL),
345
- errorCode: 'window.URL',
346
- errorMsg: 'The window.URL API is not supported in your browser'
347
- }, {
348
- condition: utils.isSupported.blob(),
349
- errorCode: 'window.Blob',
350
- errorMsg: 'The window.Blob File API is not supported in your browser'
351
- }, {
352
- condition: utils.isSupported.Uint8Array(),
353
- errorCode: 'window.Uint8Array',
354
- errorMsg: 'The window.Uint8Array function constructor is not supported in your browser'
355
- }, {
356
- condition: utils.isSupported.Uint32Array(),
357
- errorCode: 'window.Uint32Array',
358
- errorMsg: 'The window.Uint32Array function constructor is not supported in your browser'
359
- }],
360
- messages: {
361
- videoCodecs: {
362
- errorCode: 'videocodec',
363
- errorMsg: 'The video codec you are trying to use is not supported in your browser'
364
- }
365
- }
366
- };
367
-
368
-
369
-
370
- var error$2 = Object.freeze({
371
- default: error
372
- });
373
-
374
- /*
375
- defaultOptions.js
376
- =================
377
- */
378
-
379
- /* Copyright 2017 Yahoo Inc.
380
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
381
- */
382
-
383
- // Helpers
384
- var noop = function noop() {};
385
-
386
- var defaultOptions = {
387
- sampleInterval: 10,
388
- numWorkers: 2,
389
- filter: '',
390
- gifWidth: 200,
391
- gifHeight: 200,
392
- interval: 0.1,
393
- numFrames: 10,
394
- frameDuration: 1,
395
- keepCameraOn: false,
396
- images: [],
397
- video: null,
398
- webcamVideoElement: null,
399
- cameraStream: null,
400
- text: '',
401
- fontWeight: 'normal',
402
- fontSize: '16px',
403
- minFontSize: '10px',
404
- resizeFont: false,
405
- fontFamily: 'sans-serif',
406
- fontColor: '#ffffff',
407
- textAlign: 'center',
408
- textBaseline: 'bottom',
409
- textXCoordinate: null,
410
- textYCoordinate: null,
411
- progressCallback: noop,
412
- completeCallback: noop,
413
- saveRenderingContexts: false,
414
- savedRenderingContexts: [],
415
- crossOrigin: 'Anonymous'
416
- };
417
-
418
-
419
-
420
- var defaultOptions$2 = Object.freeze({
421
- default: defaultOptions
422
- });
423
-
424
- /*
425
- isSupported.js
426
- ==============
427
- */
428
-
429
- /* Copyright 2017 Yahoo Inc.
430
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
431
- */
432
-
433
- // Dependencies
434
- function isSupported() {
435
- return error.isValid();
436
- }
437
-
438
- /*
439
- isWebCamGIFSupported.js
440
- =======================
441
- */
442
-
443
- /* Copyright 2017 Yahoo Inc.
444
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
445
- */
446
-
447
- function isWebCamGIFSupported() {
448
- return error.isValid();
449
- }
450
-
451
- /*
452
- isSupported.js
453
- ==============
454
- */
455
-
456
- /* Copyright 2017 Yahoo Inc.
457
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
458
- */
459
-
460
- // Dependencies
461
- function isSupported$1() {
462
- var options = {
463
- getUserMedia: true
464
- };
465
-
466
- return error.isValid(options);
467
- }
468
-
469
- /*
470
- isExistingVideoGIFSupported.js
471
- ==============================
472
- */
473
-
474
- /* Copyright 2017 Yahoo Inc.
475
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
476
- */
477
-
478
- // Dependencies
479
- function isExistingVideoGIFSupported(codecs) {
480
- var hasValidCodec = false;
481
-
482
- if (utils.isArray(codecs) && codecs.length) {
483
- utils.each(codecs, function (indece, currentCodec) {
484
- if (utils.isSupported.videoCodecs[currentCodec]) {
485
- hasValidCodec = true;
486
- }
487
- });
488
-
489
- if (!hasValidCodec) {
490
- return false;
491
- }
492
- } else if (utils.isString(codecs) && codecs.length) {
493
- if (!utils.isSupported.videoCodecs[codecs]) {
494
- return false;
495
- }
496
- }
497
-
498
- return error.isValid({
499
- 'getUserMedia': true
500
- });
501
- }
502
-
503
- /*
504
- NeuQuant.js
505
- ===========
506
- */
507
-
508
- /*
509
- * NeuQuant Neural-Net Quantization Algorithm
510
- * ------------------------------------------
511
- *
512
- * Copyright (c) 1994 Anthony Dekker
513
- *
514
- * NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. See
515
- * "Kohonen neural networks for optimal colour quantization" in "Network:
516
- * Computation in Neural Systems" Vol. 5 (1994) pp 351-367. for a discussion of
517
- * the algorithm.
518
- *
519
- * Any party obtaining a copy of these files from the author, directly or
520
- * indirectly, is granted, free of charge, a full and unrestricted irrevocable,
521
- * world-wide, paid up, royalty-free, nonexclusive right and license to deal in
522
- * this software and documentation files (the "Software"), including without
523
- * limitation the rights to use, copy, modify, merge, publish, distribute,
524
- * sublicense, and/or sell copies of the Software, and to permit persons who
525
- * receive copies from any such party to do so, with the only requirement being
526
- * that this copyright notice remain intact.
527
- */
528
-
529
- /*
530
- * This class handles Neural-Net quantization algorithm
531
- * @author Kevin Weiner (original Java version - kweiner@fmsware.com)
532
- * @author Thibault Imbert (AS3 version - bytearray.org)
533
- * @version 0.1 AS3 implementation
534
- * @version 0.2 JS->AS3 "translation" by antimatter15
535
- * @version 0.3 JS clean up + using modern JS idioms by sole - http://soledadpenades.com
536
- * Also implement fix in color conversion described at http://stackoverflow.com/questions/16371712/neuquant-js-javascript-color-quantization-hidden-bug-in-js-conversion
537
- */
538
-
539
- function NeuQuant() {
540
- var netsize = 256; // number of colours used
541
-
542
- // four primes near 500 - assume no image has a length so large
543
- // that it is divisible by all four primes
544
- var prime1 = 499;
545
- var prime2 = 491;
546
- var prime3 = 487;
547
- var prime4 = 503;
548
-
549
- // minimum size for input image
550
- var minpicturebytes = 3 * prime4;
551
-
552
- // Network Definitions
553
-
554
- var maxnetpos = netsize - 1;
555
- var netbiasshift = 4; // bias for colour values
556
- var ncycles = 100; // no. of learning cycles
557
-
558
- // defs for freq and bias
559
- var intbiasshift = 16; // bias for fractions
560
- var intbias = 1 << intbiasshift;
561
- var gammashift = 10; // gamma = 1024
562
- var gamma = 1 << gammashift;
563
- var betashift = 10;
564
- var beta = intbias >> betashift; // beta = 1/1024
565
- var betagamma = intbias << gammashift - betashift;
566
-
567
- // defs for decreasing radius factor
568
- // For 256 colors, radius starts at 32.0 biased by 6 bits
569
- // and decreases by a factor of 1/30 each cycle
570
- var initrad = netsize >> 3;
571
- var radiusbiasshift = 6;
572
- var radiusbias = 1 << radiusbiasshift;
573
- var initradius = initrad * radiusbias;
574
- var radiusdec = 30;
575
-
576
- // defs for decreasing alpha factor
577
- // Alpha starts at 1.0 biased by 10 bits
578
- var alphabiasshift = 10;
579
- var initalpha = 1 << alphabiasshift;
580
- var alphadec;
581
-
582
- // radbias and alpharadbias used for radpower calculation
583
- var radbiasshift = 8;
584
- var radbias = 1 << radbiasshift;
585
- var alpharadbshift = alphabiasshift + radbiasshift;
586
- var alpharadbias = 1 << alpharadbshift;
587
-
588
- // Input image
589
- var thepicture;
590
- // Height * Width * 3
591
- var lengthcount;
592
- // Sampling factor 1..30
593
- var samplefac;
594
-
595
- // The network itself
596
- var network;
597
- var netindex = [];
598
-
599
- // for network lookup - really 256
600
- var bias = [];
601
-
602
- // bias and freq arrays for learning
603
- var freq = [];
604
- var radpower = [];
605
-
606
- function NeuQuantConstructor(thepic, len, sample) {
607
-
608
- var i;
609
- var p;
610
-
611
- thepicture = thepic;
612
- lengthcount = len;
613
- samplefac = sample;
614
-
615
- network = new Array(netsize);
616
-
617
- for (i = 0; i < netsize; i++) {
618
- network[i] = new Array(4);
619
- p = network[i];
620
- p[0] = p[1] = p[2] = (i << netbiasshift + 8) / netsize | 0;
621
- freq[i] = intbias / netsize | 0; // 1 / netsize
622
- bias[i] = 0;
623
- }
624
- }
625
-
626
- function colorMap() {
627
- var map = [];
628
- var index = new Array(netsize);
629
- for (var i = 0; i < netsize; i++) {
630
- index[network[i][3]] = i;
631
- }var k = 0;
632
- for (var l = 0; l < netsize; l++) {
633
- var j = index[l];
634
- map[k++] = network[j][0];
635
- map[k++] = network[j][1];
636
- map[k++] = network[j][2];
637
- }
638
- return map;
639
- }
640
-
641
- // Insertion sort of network and building of netindex[0..255]
642
- // (to do after unbias)
643
- function inxbuild() {
644
- var i;
645
- var j;
646
- var smallpos;
647
- var smallval;
648
- var p;
649
- var q;
650
- var previouscol;
651
- var startpos;
652
-
653
- previouscol = 0;
654
- startpos = 0;
655
-
656
- for (i = 0; i < netsize; i++) {
657
-
658
- p = network[i];
659
- smallpos = i;
660
- smallval = p[1]; // index on g
661
- // find smallest in i..netsize-1
662
- for (j = i + 1; j < netsize; j++) {
663
-
664
- q = network[j];
665
-
666
- if (q[1] < smallval) {
667
- // index on g
668
- smallpos = j;
669
- smallval = q[1]; // index on g
670
- }
671
- }
672
-
673
- q = network[smallpos];
674
-
675
- // swap p (i) and q (smallpos) entries
676
- if (i != smallpos) {
677
- j = q[0];
678
- q[0] = p[0];
679
- p[0] = j;
680
- j = q[1];
681
- q[1] = p[1];
682
- p[1] = j;
683
- j = q[2];
684
- q[2] = p[2];
685
- p[2] = j;
686
- j = q[3];
687
- q[3] = p[3];
688
- p[3] = j;
689
- }
690
-
691
- // smallval entry is now in position i
692
- if (smallval != previouscol) {
693
-
694
- netindex[previouscol] = startpos + i >> 1;
695
-
696
- for (j = previouscol + 1; j < smallval; j++) {
697
- netindex[j] = i;
698
- }
699
-
700
- previouscol = smallval;
701
- startpos = i;
702
- }
703
- }
704
-
705
- netindex[previouscol] = startpos + maxnetpos >> 1;
706
- for (j = previouscol + 1; j < 256; j++) {
707
- netindex[j] = maxnetpos; // really 256
708
- }
709
- }
710
-
711
- // Main Learning Loop
712
-
713
- function learn() {
714
- var i;
715
- var j;
716
- var b;
717
- var g;
718
- var r;
719
- var radius;
720
- var rad;
721
- var alpha;
722
- var step;
723
- var delta;
724
- var samplepixels;
725
- var p;
726
- var pix;
727
- var lim;
728
-
729
- if (lengthcount < minpicturebytes) {
730
- samplefac = 1;
731
- }
732
-
733
- alphadec = 30 + (samplefac - 1) / 3;
734
- p = thepicture;
735
- pix = 0;
736
- lim = lengthcount;
737
- samplepixels = lengthcount / (3 * samplefac);
738
- delta = samplepixels / ncycles | 0;
739
- alpha = initalpha;
740
- radius = initradius;
741
-
742
- rad = radius >> radiusbiasshift;
743
- if (rad <= 1) {
744
- rad = 0;
745
- }
746
-
747
- for (i = 0; i < rad; i++) {
748
- radpower[i] = alpha * ((rad * rad - i * i) * radbias / (rad * rad));
749
- }
750
-
751
- if (lengthcount < minpicturebytes) {
752
- step = 3;
753
- } else if (lengthcount % prime1 !== 0) {
754
- step = 3 * prime1;
755
- } else {
756
-
757
- if (lengthcount % prime2 !== 0) {
758
- step = 3 * prime2;
759
- } else {
760
- if (lengthcount % prime3 !== 0) {
761
- step = 3 * prime3;
762
- } else {
763
- step = 3 * prime4;
764
- }
765
- }
766
- }
767
-
768
- i = 0;
769
-
770
- while (i < samplepixels) {
771
-
772
- b = (p[pix + 0] & 0xff) << netbiasshift;
773
- g = (p[pix + 1] & 0xff) << netbiasshift;
774
- r = (p[pix + 2] & 0xff) << netbiasshift;
775
- j = contest(b, g, r);
776
-
777
- altersingle(alpha, j, b, g, r);
778
-
779
- if (rad !== 0) {
780
- // Alter neighbours
781
- alterneigh(rad, j, b, g, r);
782
- }
783
-
784
- pix += step;
785
-
786
- if (pix >= lim) {
787
- pix -= lengthcount;
788
- }
789
-
790
- i++;
791
-
792
- if (delta === 0) {
793
- delta = 1;
794
- }
795
-
796
- if (i % delta === 0) {
797
- alpha -= alpha / alphadec;
798
- radius -= radius / radiusdec;
799
- rad = radius >> radiusbiasshift;
800
-
801
- if (rad <= 1) {
802
- rad = 0;
803
- }
804
-
805
- for (j = 0; j < rad; j++) {
806
- radpower[j] = alpha * ((rad * rad - j * j) * radbias / (rad * rad));
807
- }
808
- }
809
- }
810
- }
811
-
812
- // Search for BGR values 0..255 (after net is unbiased) and return colour index
813
- function map(b, g, r) {
814
- var i;
815
- var j;
816
- var dist;
817
- var a;
818
- var bestd;
819
- var p;
820
- var best;
821
-
822
- // Biggest possible distance is 256 * 3
823
- bestd = 1000;
824
- best = -1;
825
- i = netindex[g]; // index on g
826
- j = i - 1; // start at netindex[g] and work outwards
827
-
828
- while (i < netsize || j >= 0) {
829
-
830
- if (i < netsize) {
831
-
832
- p = network[i];
833
-
834
- dist = p[1] - g; // inx key
835
-
836
- if (dist >= bestd) {
837
- i = netsize; // stop iter
838
- } else {
839
-
840
- i++;
841
-
842
- if (dist < 0) {
843
- dist = -dist;
844
- }
845
-
846
- a = p[0] - b;
847
-
848
- if (a < 0) {
849
- a = -a;
850
- }
851
-
852
- dist += a;
853
-
854
- if (dist < bestd) {
855
- a = p[2] - r;
856
-
857
- if (a < 0) {
858
- a = -a;
859
- }
860
-
861
- dist += a;
862
-
863
- if (dist < bestd) {
864
- bestd = dist;
865
- best = p[3];
866
- }
867
- }
868
- }
869
- }
870
-
871
- if (j >= 0) {
872
-
873
- p = network[j];
874
-
875
- dist = g - p[1]; // inx key - reverse dif
876
-
877
- if (dist >= bestd) {
878
- j = -1; // stop iter
879
- } else {
880
-
881
- j--;
882
- if (dist < 0) {
883
- dist = -dist;
884
- }
885
- a = p[0] - b;
886
- if (a < 0) {
887
- a = -a;
888
- }
889
- dist += a;
890
-
891
- if (dist < bestd) {
892
- a = p[2] - r;
893
- if (a < 0) {
894
- a = -a;
895
- }
896
- dist += a;
897
- if (dist < bestd) {
898
- bestd = dist;
899
- best = p[3];
900
- }
901
- }
902
- }
903
- }
904
- }
905
-
906
- return best;
907
- }
908
-
909
- function process() {
910
- learn();
911
- unbiasnet();
912
- inxbuild();
913
- return colorMap();
914
- }
915
-
916
- // Unbias network to give byte values 0..255 and record position i
917
- // to prepare for sort
918
- function unbiasnet() {
919
- var i;
920
- var j;
921
-
922
- for (i = 0; i < netsize; i++) {
923
- network[i][0] >>= netbiasshift;
924
- network[i][1] >>= netbiasshift;
925
- network[i][2] >>= netbiasshift;
926
- network[i][3] = i; // record colour no
927
- }
928
- }
929
-
930
- // Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2))
931
- // in radpower[|i-j|]
932
- function alterneigh(rad, i, b, g, r) {
933
-
934
- var j;
935
- var k;
936
- var lo;
937
- var hi;
938
- var a;
939
- var m;
940
-
941
- var p;
942
-
943
- lo = i - rad;
944
- if (lo < -1) {
945
- lo = -1;
946
- }
947
-
948
- hi = i + rad;
949
-
950
- if (hi > netsize) {
951
- hi = netsize;
952
- }
953
-
954
- j = i + 1;
955
- k = i - 1;
956
- m = 1;
957
-
958
- while (j < hi || k > lo) {
959
-
960
- a = radpower[m++];
961
-
962
- if (j < hi) {
963
-
964
- p = network[j++];
965
-
966
- try {
967
-
968
- p[0] -= a * (p[0] - b) / alpharadbias | 0;
969
- p[1] -= a * (p[1] - g) / alpharadbias | 0;
970
- p[2] -= a * (p[2] - r) / alpharadbias | 0;
971
- } catch (e) {}
972
- }
973
-
974
- if (k > lo) {
975
-
976
- p = network[k--];
977
-
978
- try {
979
-
980
- p[0] -= a * (p[0] - b) / alpharadbias | 0;
981
- p[1] -= a * (p[1] - g) / alpharadbias | 0;
982
- p[2] -= a * (p[2] - r) / alpharadbias | 0;
983
- } catch (e) {}
984
- }
985
- }
986
- }
987
-
988
- // Move neuron i towards biased (b,g,r) by factor alpha
989
- function altersingle(alpha, i, b, g, r) {
990
-
991
- // alter hit neuron
992
- var n = network[i];
993
- var alphaMult = alpha / initalpha;
994
- n[0] -= alphaMult * (n[0] - b) | 0;
995
- n[1] -= alphaMult * (n[1] - g) | 0;
996
- n[2] -= alphaMult * (n[2] - r) | 0;
997
- }
998
-
999
- // Search for biased BGR values
1000
- function contest(b, g, r) {
1001
-
1002
- // finds closest neuron (min dist) and updates freq
1003
- // finds best neuron (min dist-bias) and returns position
1004
- // for frequently chosen neurons, freq[i] is high and bias[i] is negative
1005
- // bias[i] = gamma*((1/netsize)-freq[i])
1006
-
1007
- var i;
1008
- var dist;
1009
- var a;
1010
- var biasdist;
1011
- var betafreq;
1012
- var bestpos;
1013
- var bestbiaspos;
1014
- var bestd;
1015
- var bestbiasd;
1016
- var n;
1017
-
1018
- bestd = ~(1 << 31);
1019
- bestbiasd = bestd;
1020
- bestpos = -1;
1021
- bestbiaspos = bestpos;
1022
-
1023
- for (i = 0; i < netsize; i++) {
1024
-
1025
- n = network[i];
1026
- dist = n[0] - b;
1027
-
1028
- if (dist < 0) {
1029
- dist = -dist;
1030
- }
1031
-
1032
- a = n[1] - g;
1033
-
1034
- if (a < 0) {
1035
- a = -a;
1036
- }
1037
-
1038
- dist += a;
1039
-
1040
- a = n[2] - r;
1041
-
1042
- if (a < 0) {
1043
- a = -a;
1044
- }
1045
-
1046
- dist += a;
1047
-
1048
- if (dist < bestd) {
1049
- bestd = dist;
1050
- bestpos = i;
1051
- }
1052
-
1053
- biasdist = dist - (bias[i] >> intbiasshift - netbiasshift);
1054
-
1055
- if (biasdist < bestbiasd) {
1056
- bestbiasd = biasdist;
1057
- bestbiaspos = i;
1058
- }
1059
-
1060
- betafreq = freq[i] >> betashift;
1061
- freq[i] -= betafreq;
1062
- bias[i] += betafreq << gammashift;
1063
- }
1064
-
1065
- freq[bestpos] += beta;
1066
- bias[bestpos] -= betagamma;
1067
- return bestbiaspos;
1068
- }
1069
-
1070
- NeuQuantConstructor.apply(this, arguments);
1071
-
1072
- var exports = {};
1073
- exports.map = map;
1074
- exports.process = process;
1075
-
1076
- return exports;
1077
- }
1078
-
1079
- /*
1080
- processFrameWorker.js
1081
- =====================
1082
- */
1083
-
1084
- /* Copyright 2017 Yahoo Inc.
1085
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
1086
- */
1087
-
1088
- function workerCode() {
1089
- var self = this;
1090
-
1091
- try {
1092
- self.onmessage = function (ev) {
1093
- var data = ev.data || {};
1094
- var response;
1095
-
1096
- if (data.gifshot) {
1097
- response = workerMethods.run(data);
1098
- postMessage(response);
1099
- }
1100
- };
1101
- } catch (e) {}
1102
-
1103
- var workerMethods = {
1104
- dataToRGB: function dataToRGB(data, width, height) {
1105
- var length = width * height * 4;
1106
- var i = 0;
1107
- var rgb = [];
1108
-
1109
- while (i < length) {
1110
- rgb.push(data[i++]);
1111
- rgb.push(data[i++]);
1112
- rgb.push(data[i++]);
1113
- i++; // for the alpha channel which we don't care about
1114
- }
1115
-
1116
- return rgb;
1117
- },
1118
- componentizedPaletteToArray: function componentizedPaletteToArray(paletteRGB) {
1119
- paletteRGB = paletteRGB || [];
1120
-
1121
- var paletteArray = [];
1122
-
1123
- for (var i = 0; i < paletteRGB.length; i += 3) {
1124
- var r = paletteRGB[i];
1125
- var g = paletteRGB[i + 1];
1126
- var b = paletteRGB[i + 2];
1127
-
1128
- paletteArray.push(r << 16 | g << 8 | b);
1129
- }
1130
-
1131
- return paletteArray;
1132
- },
1133
- // This is the "traditional" Animated_GIF style of going from RGBA to indexed color frames
1134
- 'processFrameWithQuantizer': function processFrameWithQuantizer(imageData, width, height, sampleInterval) {
1135
- var rgbComponents = this.dataToRGB(imageData, width, height);
1136
- var nq = new NeuQuant(rgbComponents, rgbComponents.length, sampleInterval);
1137
- var paletteRGB = nq.process();
1138
- var paletteArray = new Uint32Array(this.componentizedPaletteToArray(paletteRGB));
1139
- var numberPixels = width * height;
1140
- var indexedPixels = new Uint8Array(numberPixels);
1141
- var k = 0;
1142
-
1143
- for (var i = 0; i < numberPixels; i++) {
1144
- var r = rgbComponents[k++];
1145
- var g = rgbComponents[k++];
1146
- var b = rgbComponents[k++];
1147
-
1148
- indexedPixels[i] = nq.map(r, g, b);
1149
- }
1150
-
1151
- return {
1152
- pixels: indexedPixels,
1153
- palette: paletteArray
1154
- };
1155
- },
1156
- 'run': function run(frame) {
1157
- frame = frame || {};
1158
-
1159
- var _frame = frame,
1160
- height = _frame.height,
1161
- palette = _frame.palette,
1162
- sampleInterval = _frame.sampleInterval,
1163
- width = _frame.width;
1164
-
1165
- var imageData = frame.data;
1166
-
1167
- return this.processFrameWithQuantizer(imageData, width, height, sampleInterval);
1168
- }
1169
- };
1170
-
1171
- return workerMethods;
1172
- }
1173
-
1174
- /*
1175
- gifWriter.js
1176
- ============
1177
- */
1178
-
1179
- // (c) Dean McNamee <dean@gmail.com>, 2013.
1180
- //
1181
- // https://github.com/deanm/omggif
1182
- //
1183
- // Permission is hereby granted, free of charge, to any person obtaining a copy
1184
- // of this software and associated documentation files (the "Software"), to
1185
- // deal in the Software without restriction, including without limitation the
1186
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
1187
- // sell copies of the Software, and to permit persons to whom the Software is
1188
- // furnished to do so, subject to the following conditions:
1189
- //
1190
- // The above copyright notice and this permission notice shall be included in
1191
- // all copies or substantial portions of the Software.
1192
- //
1193
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1194
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1195
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1196
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1197
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
1198
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
1199
- // IN THE SOFTWARE.
1200
- //
1201
- // omggif is a JavaScript implementation of a GIF 89a encoder and decoder,
1202
- // including animation and compression. It does not rely on any specific
1203
- // underlying system, so should run in the browser, Node, or Plask.
1204
-
1205
- function gifWriter(buf, width, height, gopts) {
1206
- var p = 0;
1207
-
1208
- gopts = gopts === undefined ? {} : gopts;
1209
- var loop_count = gopts.loop === undefined ? null : gopts.loop;
1210
- var global_palette = gopts.palette === undefined ? null : gopts.palette;
1211
-
1212
- if (width <= 0 || height <= 0 || width > 65535 || height > 65535) throw "Width/Height invalid.";
1213
-
1214
- function check_palette_and_num_colors(palette) {
1215
- var num_colors = palette.length;
1216
-
1217
- 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.";
1218
- return num_colors;
1219
- }
1220
-
1221
- // - Header.
1222
- buf[p++] = 0x47;
1223
- buf[p++] = 0x49;
1224
- buf[p++] = 0x46; // GIF
1225
- buf[p++] = 0x38;
1226
- buf[p++] = 0x39;
1227
- buf[p++] = 0x61; // 89a
1228
-
1229
- // Handling of Global Color Table (palette) and background index.
1230
- var gp_num_colors_pow2 = 0;
1231
- var background = 0;
1232
-
1233
- // - Logical Screen Descriptor.
1234
- // NOTE(deanm): w/h apparently ignored by implementations, but set anyway.
1235
- buf[p++] = width & 0xff;
1236
- buf[p++] = width >> 8 & 0xff;
1237
- buf[p++] = height & 0xff;
1238
- buf[p++] = height >> 8 & 0xff;
1239
- // NOTE: Indicates 0-bpp original color resolution (unused?).
1240
- buf[p++] = (global_palette !== null ? 0x80 : 0) | // Global Color Table Flag.
1241
- gp_num_colors_pow2; // NOTE: No sort flag (unused?).
1242
- buf[p++] = background; // Background Color Index.
1243
- buf[p++] = 0; // Pixel aspect ratio (unused?).
1244
-
1245
- if (loop_count !== null) {
1246
- // Netscape block for looping.
1247
- if (loop_count < 0 || loop_count > 65535) throw "Loop count invalid.";
1248
-
1249
- // Extension code, label, and length.
1250
- buf[p++] = 0x21;
1251
- buf[p++] = 0xff;
1252
- buf[p++] = 0x0b;
1253
- // NETSCAPE2.0
1254
- buf[p++] = 0x4e;
1255
- buf[p++] = 0x45;
1256
- buf[p++] = 0x54;
1257
- buf[p++] = 0x53;
1258
- buf[p++] = 0x43;
1259
- buf[p++] = 0x41;
1260
- buf[p++] = 0x50;
1261
- buf[p++] = 0x45;
1262
- buf[p++] = 0x32;
1263
- buf[p++] = 0x2e;
1264
- buf[p++] = 0x30;
1265
- // Sub-block
1266
- buf[p++] = 0x03;
1267
- buf[p++] = 0x01;
1268
- buf[p++] = loop_count & 0xff;
1269
- buf[p++] = loop_count >> 8 & 0xff;
1270
- buf[p++] = 0x00; // Terminator.
1271
- }
1272
-
1273
- var ended = false;
1274
-
1275
- this.addFrame = function (x, y, w, h, indexed_pixels, opts) {
1276
- if (ended === true) {
1277
- --p;
1278
- ended = false;
1279
- } // Un-end.
1280
-
1281
- opts = opts === undefined ? {} : opts;
1282
-
1283
- // TODO(deanm): Bounds check x, y. Do they need to be within the virtual
1284
- // canvas width/height, I imagine?
1285
- if (x < 0 || y < 0 || x > 65535 || y > 65535) throw "x/y invalid.";
1286
-
1287
- if (w <= 0 || h <= 0 || w > 65535 || h > 65535) throw "Width/Height invalid.";
1288
-
1289
- if (indexed_pixels.length < w * h) throw "Not enough pixels for the frame size.";
1290
-
1291
- var using_local_palette = true;
1292
- var palette = opts.palette;
1293
- if (palette === undefined || palette === null) {
1294
- using_local_palette = false;
1295
- palette = global_palette;
1296
- }
1297
-
1298
- if (palette === undefined || palette === null) throw "Must supply either a local or global palette.";
1299
-
1300
- var num_colors = check_palette_and_num_colors(palette);
1301
-
1302
- // Compute the min_code_size (power of 2), destroying num_colors.
1303
- var min_code_size = 0;
1304
- while (num_colors >>= 1) {
1305
- ++min_code_size;
1306
- }num_colors = 1 << min_code_size; // Now we can easily get it back.
1307
-
1308
- var delay = opts.delay === undefined ? 0 : opts.delay;
1309
-
1310
- // From the spec:
1311
- // 0 - No disposal specified. The decoder is
1312
- // not required to take any action.
1313
- // 1 - Do not dispose. The graphic is to be left
1314
- // in place.
1315
- // 2 - Restore to background color. The area used by the
1316
- // graphic must be restored to the background color.
1317
- // 3 - Restore to previous. The decoder is required to
1318
- // restore the area overwritten by the graphic with
1319
- // what was there prior to rendering the graphic.
1320
- // 4-7 - To be defined.
1321
- // NOTE(deanm): Dispose background doesn't really work, apparently most
1322
- // browsers ignore the background palette index and clear to transparency.
1323
- var disposal = opts.disposal === undefined ? 0 : opts.disposal;
1324
- if (disposal < 0 || disposal > 3) // 4-7 is reserved.
1325
- throw "Disposal out of range.";
1326
-
1327
- var use_transparency = false;
1328
- var transparent_index = 0;
1329
- if (opts.transparent !== undefined && opts.transparent !== null) {
1330
- use_transparency = true;
1331
- transparent_index = opts.transparent;
1332
- if (transparent_index < 0 || transparent_index >= num_colors) throw "Transparent color index.";
1333
- }
1334
-
1335
- if (disposal !== 0 || use_transparency || delay !== 0) {
1336
- // - Graphics Control Extension
1337
- buf[p++] = 0x21;
1338
- buf[p++] = 0xf9; // Extension / Label.
1339
- buf[p++] = 4; // Byte size.
1340
-
1341
- buf[p++] = disposal << 2 | (use_transparency === true ? 1 : 0);
1342
- buf[p++] = delay & 0xff;
1343
- buf[p++] = delay >> 8 & 0xff;
1344
- buf[p++] = transparent_index; // Transparent color index.
1345
- buf[p++] = 0; // Block Terminator.
1346
- }
1347
-
1348
- // - Image Descriptor
1349
- buf[p++] = 0x2c; // Image Seperator.
1350
- buf[p++] = x & 0xff;
1351
- buf[p++] = x >> 8 & 0xff; // Left.
1352
- buf[p++] = y & 0xff;
1353
- buf[p++] = y >> 8 & 0xff; // Top.
1354
- buf[p++] = w & 0xff;
1355
- buf[p++] = w >> 8 & 0xff;
1356
- buf[p++] = h & 0xff;
1357
- buf[p++] = h >> 8 & 0xff;
1358
- // NOTE: No sort flag (unused?).
1359
- // TODO(deanm): Support interlace.
1360
- buf[p++] = using_local_palette === true ? 0x80 | min_code_size - 1 : 0;
1361
-
1362
- // - Local Color Table
1363
- if (using_local_palette === true) {
1364
- for (var i = 0, il = palette.length; i < il; ++i) {
1365
- var rgb = palette[i];
1366
- buf[p++] = rgb >> 16 & 0xff;
1367
- buf[p++] = rgb >> 8 & 0xff;
1368
- buf[p++] = rgb & 0xff;
1369
- }
1370
- }
1371
-
1372
- p = GifWriterOutputLZWCodeStream(buf, p, min_code_size < 2 ? 2 : min_code_size, indexed_pixels);
1373
- };
1374
-
1375
- this.end = function () {
1376
- if (ended === false) {
1377
- buf[p++] = 0x3b; // Trailer.
1378
- ended = true;
1379
- }
1380
- return p;
1381
- };
1382
-
1383
- // Main compression routine, palette indexes -> LZW code stream.
1384
- // |index_stream| must have at least one entry.
1385
- function GifWriterOutputLZWCodeStream(buf, p, min_code_size, index_stream) {
1386
- buf[p++] = min_code_size;
1387
- var cur_subblock = p++; // Pointing at the length field.
1388
-
1389
- var clear_code = 1 << min_code_size;
1390
- var code_mask = clear_code - 1;
1391
- var eoi_code = clear_code + 1;
1392
- var next_code = eoi_code + 1;
1393
-
1394
- var cur_code_size = min_code_size + 1; // Number of bits per code.
1395
- var cur_shift = 0;
1396
- // We have at most 12-bit codes, so we should have to hold a max of 19
1397
- // bits here (and then we would write out).
1398
- var cur = 0;
1399
-
1400
- function emit_bytes_to_buffer(bit_block_size) {
1401
- while (cur_shift >= bit_block_size) {
1402
- buf[p++] = cur & 0xff;
1403
- cur >>= 8;
1404
- cur_shift -= 8;
1405
- if (p === cur_subblock + 256) {
1406
- // Finished a subblock.
1407
- buf[cur_subblock] = 255;
1408
- cur_subblock = p++;
1409
- }
1410
- }
1411
- }
1412
-
1413
- function emit_code(c) {
1414
- cur |= c << cur_shift;
1415
- cur_shift += cur_code_size;
1416
- emit_bytes_to_buffer(8);
1417
- }
1418
-
1419
- // I am not an expert on the topic, and I don't want to write a thesis.
1420
- // However, it is good to outline here the basic algorithm and the few data
1421
- // structures and optimizations here that make this implementation fast.
1422
- // The basic idea behind LZW is to build a table of previously seen runs
1423
- // addressed by a short id (herein called output code). All data is
1424
- // referenced by a code, which represents one or more values from the
1425
- // original input stream. All input bytes can be referenced as the same
1426
- // value as an output code. So if you didn't want any compression, you
1427
- // could more or less just output the original bytes as codes (there are
1428
- // some details to this, but it is the idea). In order to achieve
1429
- // compression, values greater then the input range (codes can be up to
1430
- // 12-bit while input only 8-bit) represent a sequence of previously seen
1431
- // inputs. The decompressor is able to build the same mapping while
1432
- // decoding, so there is always a shared common knowledge between the
1433
- // encoding and decoder, which is also important for "timing" aspects like
1434
- // how to handle variable bit width code encoding.
1435
- //
1436
- // One obvious but very important consequence of the table system is there
1437
- // is always a unique id (at most 12-bits) to map the runs. 'A' might be
1438
- // 4, then 'AA' might be 10, 'AAA' 11, 'AAAA' 12, etc. This relationship
1439
- // can be used for an effecient lookup strategy for the code mapping. We
1440
- // need to know if a run has been seen before, and be able to map that run
1441
- // to the output code. Since we start with known unique ids (input bytes),
1442
- // and then from those build more unique ids (table entries), we can
1443
- // continue this chain (almost like a linked list) to always have small
1444
- // integer values that represent the current byte chains in the encoder.
1445
- // This means instead of tracking the input bytes (AAAABCD) to know our
1446
- // current state, we can track the table entry for AAAABC (it is guaranteed
1447
- // to exist by the nature of the algorithm) and the next character D.
1448
- // Therefor the tuple of (table_entry, byte) is guaranteed to also be
1449
- // unique. This allows us to create a simple lookup key for mapping input
1450
- // sequences to codes (table indices) without having to store or search
1451
- // any of the code sequences. So if 'AAAA' has a table entry of 12, the
1452
- // tuple of ('AAAA', K) for any input byte K will be unique, and can be our
1453
- // key. This leads to a integer value at most 20-bits, which can always
1454
- // fit in an SMI value and be used as a fast sparse array / object key.
1455
-
1456
- // Output code for the current contents of the index buffer.
1457
- var ib_code = index_stream[0] & code_mask; // Load first input index.
1458
- var code_table = {}; // Key'd on our 20-bit "tuple".
1459
-
1460
- emit_code(clear_code); // Spec says first code should be a clear code.
1461
-
1462
- // First index already loaded, process the rest of the stream.
1463
- for (var i = 1, il = index_stream.length; i < il; ++i) {
1464
- var k = index_stream[i] & code_mask;
1465
- var cur_key = ib_code << 8 | k; // (prev, k) unique tuple.
1466
- var cur_code = code_table[cur_key]; // buffer + k.
1467
-
1468
- // Check if we have to create a new code table entry.
1469
- if (cur_code === undefined) {
1470
- // We don't have buffer + k.
1471
- // Emit index buffer (without k).
1472
- // This is an inline version of emit_code, because this is the core
1473
- // writing routine of the compressor (and V8 cannot inline emit_code
1474
- // because it is a closure here in a different context). Additionally
1475
- // we can call emit_byte_to_buffer less often, because we can have
1476
- // 30-bits (from our 31-bit signed SMI), and we know our codes will only
1477
- // be 12-bits, so can safely have 18-bits there without overflow.
1478
- // emit_code(ib_code);
1479
- cur |= ib_code << cur_shift;
1480
- cur_shift += cur_code_size;
1481
- while (cur_shift >= 8) {
1482
- buf[p++] = cur & 0xff;
1483
- cur >>= 8;
1484
- cur_shift -= 8;
1485
- if (p === cur_subblock + 256) {
1486
- // Finished a subblock.
1487
- buf[cur_subblock] = 255;
1488
- cur_subblock = p++;
1489
- }
1490
- }
1491
-
1492
- if (next_code === 4096) {
1493
- // Table full, need a clear.
1494
- emit_code(clear_code);
1495
- next_code = eoi_code + 1;
1496
- cur_code_size = min_code_size + 1;
1497
- code_table = {};
1498
- } else {
1499
- // Table not full, insert a new entry.
1500
- // Increase our variable bit code sizes if necessary. This is a bit
1501
- // tricky as it is based on "timing" between the encoding and
1502
- // decoder. From the encoders perspective this should happen after
1503
- // we've already emitted the index buffer and are about to create the
1504
- // first table entry that would overflow our current code bit size.
1505
- if (next_code >= 1 << cur_code_size) ++cur_code_size;
1506
- code_table[cur_key] = next_code++; // Insert into code table.
1507
- }
1508
-
1509
- ib_code = k; // Index buffer to single input k.
1510
- } else {
1511
- ib_code = cur_code; // Index buffer to sequence in code table.
1512
- }
1513
- }
1514
-
1515
- emit_code(ib_code); // There will still be something in the index buffer.
1516
- emit_code(eoi_code); // End Of Information.
1517
-
1518
- // Flush / finalize the sub-blocks stream to the buffer.
1519
- emit_bytes_to_buffer(1);
1520
-
1521
- // Finish the sub-blocks, writing out any unfinished lengths and
1522
- // terminating with a sub-block of length 0. If we have already started
1523
- // but not yet used a sub-block it can just become the terminator.
1524
- if (cur_subblock + 1 === p) {
1525
- // Started but unused.
1526
- buf[cur_subblock] = 0;
1527
- } else {
1528
- // Started and used, write length and additional terminator block.
1529
- buf[cur_subblock] = p - cur_subblock - 1;
1530
- buf[p++] = 0;
1531
- }
1532
- return p;
1533
- }
1534
- }
1535
-
1536
- /*
1537
- animatedGIF.js
1538
- ==============
1539
- */
1540
-
1541
- /* Copyright 2017 Yahoo Inc.
1542
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
1543
- */
1544
-
1545
- // Dependencies
1546
- // Helpers
1547
- var noop$2 = function noop() {};
1548
-
1549
- var AnimatedGIF = function AnimatedGIF(options) {
1550
- this.canvas = null;
1551
- this.ctx = null;
1552
- this.repeat = 0;
1553
- this.frames = [];
1554
- this.numRenderedFrames = 0;
1555
- this.onRenderCompleteCallback = noop$2;
1556
- this.onRenderProgressCallback = noop$2;
1557
- this.workers = [];
1558
- this.availableWorkers = [];
1559
- this.generatingGIF = false;
1560
- this.options = options;
1561
-
1562
- // Constructs and initializes the the web workers appropriately
1563
- this.initializeWebWorkers(options);
1564
- };
1565
-
1566
- AnimatedGIF.prototype = {
1567
- 'workerMethods': workerCode(),
1568
- 'initializeWebWorkers': function initializeWebWorkers(options) {
1569
- var self = this;
1570
- var processFrameWorkerCode = NeuQuant.toString() + '(' + workerCode.toString() + '());';
1571
- var webWorkerObj = void 0;
1572
- var objectUrl = void 0;
1573
- var webWorker = void 0;
1574
- var numWorkers = void 0;
1575
- var x = -1;
1576
- var workerError = '';
1577
-
1578
- numWorkers = options.numWorkers;
1579
-
1580
- while (++x < numWorkers) {
1581
- webWorkerObj = utils.createWebWorker(processFrameWorkerCode);
1582
-
1583
- if (utils.isObject(webWorkerObj)) {
1584
- objectUrl = webWorkerObj.objectUrl;
1585
- webWorker = webWorkerObj.worker;
1586
-
1587
- self.workers.push({
1588
- worker: webWorker,
1589
- objectUrl: objectUrl
1590
- });
1591
-
1592
- self.availableWorkers.push(webWorker);
1593
- } else {
1594
- workerError = webWorkerObj;
1595
- utils.webWorkerError = !!webWorkerObj;
1596
- }
1597
- }
1598
-
1599
- this.workerError = workerError;
1600
- this.canvas = document.createElement('canvas');
1601
- this.canvas.width = options.gifWidth;
1602
- this.canvas.height = options.gifHeight;
1603
- this.ctx = this.canvas.getContext('2d');
1604
- this.frames = [];
1605
- },
1606
- // Return a worker for processing a frame
1607
- getWorker: function getWorker() {
1608
- return this.availableWorkers.pop();
1609
- },
1610
- // Restores a worker to the pool
1611
- freeWorker: function freeWorker(worker) {
1612
- this.availableWorkers.push(worker);
1613
- },
1614
- byteMap: function () {
1615
- var byteMap = [];
1616
-
1617
- for (var i = 0; i < 256; i++) {
1618
- byteMap[i] = String.fromCharCode(i);
1619
- }
1620
-
1621
- return byteMap;
1622
- }(),
1623
- bufferToString: function bufferToString(buffer) {
1624
- var numberValues = buffer.length;
1625
- var str = '';
1626
- var x = -1;
1627
-
1628
- while (++x < numberValues) {
1629
- str += this.byteMap[buffer[x]];
1630
- }
1631
-
1632
- return str;
1633
- },
1634
- onFrameFinished: function onFrameFinished(progressCallback) {
1635
- // The GIF is not written until we're done with all the frames
1636
- // because they might not be processed in the same order
1637
- var self = this;
1638
- var frames = self.frames;
1639
- var options = self.options;
1640
- var hasExistingImages = !!(options.images || []).length;
1641
- var allDone = frames.every(function (frame) {
1642
- return !frame.beingProcessed && frame.done;
1643
- });
1644
-
1645
- self.numRenderedFrames++;
1646
-
1647
- if (hasExistingImages) {
1648
- progressCallback(self.numRenderedFrames / frames.length);
1649
- }
1650
-
1651
- self.onRenderProgressCallback(self.numRenderedFrames * 0.75 / frames.length);
1652
-
1653
- if (allDone) {
1654
- if (!self.generatingGIF) {
1655
- self.generateGIF(frames, self.onRenderCompleteCallback);
1656
- }
1657
- } else {
1658
- utils.requestTimeout(function () {
1659
- self.processNextFrame();
1660
- }, 1);
1661
- }
1662
- },
1663
- processFrame: function processFrame(position) {
1664
- var AnimatedGifContext = this;
1665
- var options = this.options;
1666
- var _options = this.options,
1667
- progressCallback = _options.progressCallback,
1668
- sampleInterval = _options.sampleInterval;
1669
-
1670
- var frames = this.frames;
1671
- var frame = void 0;
1672
- var worker = void 0;
1673
- var done = function done() {
1674
- var ev = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1675
-
1676
- var data = ev.data;
1677
-
1678
- // Delete original data, and free memory
1679
- delete frame.data;
1680
-
1681
- frame.pixels = Array.prototype.slice.call(data.pixels);
1682
- frame.palette = Array.prototype.slice.call(data.palette);
1683
- frame.done = true;
1684
- frame.beingProcessed = false;
1685
-
1686
- AnimatedGifContext.freeWorker(worker);
1687
-
1688
- AnimatedGifContext.onFrameFinished(progressCallback);
1689
- };
1690
-
1691
- frame = frames[position];
1692
-
1693
- if (frame.beingProcessed || frame.done) {
1694
- this.onFrameFinished();
1695
-
1696
- return;
1697
- }
1698
-
1699
- frame.sampleInterval = sampleInterval;
1700
- frame.beingProcessed = true;
1701
- frame.gifshot = true;
1702
-
1703
- worker = this.getWorker();
1704
-
1705
- if (worker) {
1706
- // Process the frame in a web worker
1707
- worker.onmessage = done;
1708
- worker.postMessage(frame);
1709
- } else {
1710
- // Process the frame in the current thread
1711
- done({
1712
- 'data': AnimatedGifContext.workerMethods.run(frame)
1713
- });
1714
- }
1715
- },
1716
- startRendering: function startRendering(completeCallback) {
1717
- this.onRenderCompleteCallback = completeCallback;
1718
-
1719
- for (var i = 0; i < this.options.numWorkers && i < this.frames.length; i++) {
1720
- this.processFrame(i);
1721
- }
1722
- },
1723
- processNextFrame: function processNextFrame() {
1724
- var position = -1;
1725
-
1726
- for (var i = 0; i < this.frames.length; i++) {
1727
- var frame = this.frames[i];
1728
-
1729
- if (!frame.done && !frame.beingProcessed) {
1730
- position = i;
1731
- break;
1732
- }
1733
- }
1734
-
1735
- if (position >= 0) {
1736
- this.processFrame(position);
1737
- }
1738
- },
1739
- // Takes the already processed data in frames and feeds it to a new
1740
- // GifWriter instance in order to get the binary GIF file
1741
- generateGIF: function generateGIF(frames, callback) {
1742
- // TODO: Weird: using a simple JS array instead of a typed array,
1743
- // the files are WAY smaller o_o. Patches/explanations welcome!
1744
- var buffer = []; // new Uint8Array(width * height * frames.length * 5);
1745
- var gifOptions = {
1746
- loop: this.repeat
1747
- };
1748
- var options = this.options;
1749
- var interval = options.interval;
1750
-
1751
- var frameDuration = options.frameDuration;
1752
- var existingImages = options.images;
1753
- var hasExistingImages = !!existingImages.length;
1754
- var height = options.gifHeight;
1755
- var width = options.gifWidth;
1756
- var gifWriter$$1 = new gifWriter(buffer, width, height, gifOptions);
1757
- var onRenderProgressCallback = this.onRenderProgressCallback;
1758
- var delay = hasExistingImages ? interval * 100 : 0;
1759
- var bufferToString = void 0;
1760
- var gif = void 0;
1761
-
1762
- this.generatingGIF = true;
1763
-
1764
- utils.each(frames, function (iterator, frame) {
1765
- var framePalette = frame.palette;
1766
-
1767
- onRenderProgressCallback(0.75 + 0.25 * frame.position * 1.0 / frames.length);
1768
-
1769
- for (var i = 0; i < frameDuration; i++) {
1770
- gifWriter$$1.addFrame(0, 0, width, height, frame.pixels, {
1771
- palette: framePalette,
1772
- delay: delay
1773
- });
1774
- }
1775
- });
1776
-
1777
- gifWriter$$1.end();
1778
-
1779
- onRenderProgressCallback(1.0);
1780
-
1781
- this.frames = [];
1782
-
1783
- this.generatingGIF = false;
1784
-
1785
- if (utils.isFunction(callback)) {
1786
- bufferToString = this.bufferToString(buffer);
1787
- gif = 'data:image/gif;base64,' + utils.btoa(bufferToString);
1788
-
1789
- callback(gif);
1790
- }
1791
- },
1792
- // From GIF: 0 = loop forever, null = not looping, n > 0 = loop n times and stop
1793
- setRepeat: function setRepeat(r) {
1794
- this.repeat = r;
1795
- },
1796
- addFrame: function addFrame(element, gifshotOptions) {
1797
- gifshotOptions = utils.isObject(gifshotOptions) ? gifshotOptions : {};
1798
-
1799
- var self = this;
1800
- var ctx = self.ctx;
1801
- var options = self.options;
1802
- var width = options.gifWidth;
1803
- var height = options.gifHeight;
1804
- var fontSize = utils.getFontSize(gifshotOptions);
1805
- var _gifshotOptions = gifshotOptions,
1806
- filter = _gifshotOptions.filter,
1807
- fontColor = _gifshotOptions.fontColor,
1808
- fontFamily = _gifshotOptions.fontFamily,
1809
- fontWeight = _gifshotOptions.fontWeight,
1810
- gifHeight = _gifshotOptions.gifHeight,
1811
- gifWidth = _gifshotOptions.gifWidth,
1812
- text = _gifshotOptions.text,
1813
- textAlign = _gifshotOptions.textAlign,
1814
- textBaseline = _gifshotOptions.textBaseline;
1815
-
1816
- var textXCoordinate = gifshotOptions.textXCoordinate ? gifshotOptions.textXCoordinate : textAlign === 'left' ? 1 : textAlign === 'right' ? width : width / 2;
1817
- var textYCoordinate = gifshotOptions.textYCoordinate ? gifshotOptions.textYCoordinate : textBaseline === 'top' ? 1 : textBaseline === 'center' ? height / 2 : height;
1818
- var font = fontWeight + ' ' + fontSize + ' ' + fontFamily;
1819
- var imageData = void 0;
1820
-
1821
- try {
1822
- ctx.filter = filter;
1823
-
1824
- ctx.drawImage(element, 0, 0, width, height);
1825
-
1826
- if (text) {
1827
- ctx.font = font;
1828
- ctx.fillStyle = fontColor;
1829
- ctx.textAlign = textAlign;
1830
- ctx.textBaseline = textBaseline;
1831
- ctx.fillText(text, textXCoordinate, textYCoordinate);
1832
- }
1833
-
1834
- imageData = ctx.getImageData(0, 0, width, height);
1835
-
1836
- self.addFrameImageData(imageData);
1837
- } catch (e) {
1838
- return '' + e;
1839
- }
1840
- },
1841
- addFrameImageData: function addFrameImageData() {
1842
- var imageData = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1843
-
1844
- var frames = this.frames;
1845
- var imageDataArray = imageData.data;
1846
-
1847
- this.frames.push({
1848
- 'data': imageDataArray,
1849
- 'width': imageData.width,
1850
- 'height': imageData.height,
1851
- 'palette': null,
1852
- 'dithering': null,
1853
- 'done': false,
1854
- 'beingProcessed': false,
1855
- 'position': frames.length
1856
- });
1857
- },
1858
- onRenderProgress: function onRenderProgress(callback) {
1859
- this.onRenderProgressCallback = callback;
1860
- },
1861
- isRendering: function isRendering() {
1862
- return this.generatingGIF;
1863
- },
1864
- getBase64GIF: function getBase64GIF(completeCallback) {
1865
- var self = this;
1866
- var onRenderComplete = function onRenderComplete(gif) {
1867
- self.destroyWorkers();
1868
-
1869
- utils.requestTimeout(function () {
1870
- completeCallback(gif);
1871
- }, 0);
1872
- };
1873
-
1874
- self.startRendering(onRenderComplete);
1875
- },
1876
- destroyWorkers: function destroyWorkers() {
1877
- if (this.workerError) {
1878
- return;
1879
- }
1880
-
1881
- var workers = this.workers;
1882
-
1883
- // Explicitly ask web workers to die so they are explicitly GC'ed
1884
- utils.each(workers, function (iterator, workerObj) {
1885
- var worker = workerObj.worker;
1886
- var objectUrl = workerObj.objectUrl;
1887
-
1888
- worker.terminate();
1889
- utils.URL.revokeObjectURL(objectUrl);
1890
- });
1891
- }
1892
- };
1893
-
1894
- /*
1895
- getBase64GIF.js
1896
- ===============
1897
- */
1898
-
1899
- /* Copyright 2017 Yahoo Inc.
1900
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
1901
- */
1902
-
1903
- function getBase64GIF(animatedGifInstance, callback) {
1904
- // This is asynchronous, rendered with WebWorkers
1905
- animatedGifInstance.getBase64GIF(function (image) {
1906
- callback({
1907
- error: false,
1908
- errorCode: '',
1909
- errorMsg: '',
1910
- image: image
1911
- });
1912
- });
1913
- }
1914
-
1915
- /*
1916
- existingImages.js
1917
- =================
1918
- */
1919
-
1920
- /* Copyright 2017 Yahoo Inc.
1921
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
1922
- */
1923
-
1924
- function existingImages() {
1925
- var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1926
-
1927
- var self = this;
1928
- var callback = obj.callback,
1929
- images = obj.images,
1930
- options = obj.options;
1931
-
1932
- var imagesLength = obj.imagesLength;
1933
- var skipObj = {
1934
- 'getUserMedia': true,
1935
- 'window.URL': true
1936
- };
1937
- var errorObj = error.validate(skipObj);
1938
- var loadedImages = [];
1939
- var loadedImagesLength = 0;
1940
- var tempImage = void 0;
1941
- var ag = void 0;
1942
-
1943
- if (errorObj.error) {
1944
- return callback(errorObj);
1945
- }
1946
-
1947
- // change workerPath to point to where Animated_GIF.worker.js is
1948
- ag = new AnimatedGIF(options);
1949
-
1950
- utils.each(images, function (index, image) {
1951
- var currentImage = image;
1952
-
1953
- // if (image.src) {
1954
- // currentImage = currentImage.src;
1955
- // }
1956
- if (utils.isElement(currentImage)) {
1957
- if (options.crossOrigin) {
1958
- currentImage.crossOrigin = options.crossOrigin;
1959
- }
1960
-
1961
- loadedImages[index] = currentImage;
1962
- loadedImagesLength += 1;
1963
-
1964
- if (loadedImagesLength === imagesLength) {
1965
- addLoadedImagesToGif();
1966
- }
1967
- } else if (utils.isString(currentImage)) {
1968
- tempImage = new Image();
1969
-
1970
- if (options.crossOrigin) {
1971
- tempImage.crossOrigin = options.crossOrigin;
1972
- }
1973
-
1974
- (function (tempImage) {
1975
- if (image.text) {
1976
- tempImage.text = image.text;
1977
- }
1978
-
1979
- tempImage.onerror = function (e) {
1980
- var obj = void 0;
1981
-
1982
- --imagesLength; // skips over images that error out
1983
-
1984
- if (imagesLength === 0) {
1985
- obj = {};
1986
- obj.error = 'None of the requested images was capable of being retrieved';
1987
-
1988
- return callback(obj);
1989
- }
1990
- };
1991
-
1992
- tempImage.onload = function (e) {
1993
- if (image.text) {
1994
- loadedImages[index] = {
1995
- img: tempImage,
1996
- text: tempImage.text
1997
- };
1998
- } else {
1999
- loadedImages[index] = tempImage;
2000
- }
2001
-
2002
- loadedImagesLength += 1;
2003
-
2004
- if (loadedImagesLength === imagesLength) {
2005
- addLoadedImagesToGif();
2006
- }
2007
-
2008
- utils.removeElement(tempImage);
2009
- };
2010
-
2011
- tempImage.src = currentImage;
2012
- })(tempImage);
2013
-
2014
- utils.setCSSAttr(tempImage, {
2015
- position: 'fixed',
2016
- opacity: '0'
2017
- });
2018
-
2019
- document.body.appendChild(tempImage);
2020
- }
2021
- });
2022
-
2023
- function addLoadedImagesToGif() {
2024
- utils.each(loadedImages, function (index, loadedImage) {
2025
- if (loadedImage) {
2026
- if (loadedImage.text) {
2027
- ag.addFrame(loadedImage.img, options, loadedImage.text);
2028
- } else {
2029
- ag.addFrame(loadedImage, options);
2030
- }
2031
- }
2032
- });
2033
-
2034
- getBase64GIF(ag, callback);
2035
- }
2036
- }
2037
-
2038
- /*
2039
- screenShot.js
2040
- =============
2041
- */
2042
-
2043
- /* Copyright 2017 Yahoo Inc.
2044
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
2045
- */
2046
-
2047
- // Dependencies
2048
- // Helpers
2049
- var noop$3 = function noop() {};
2050
-
2051
- var screenShot = {
2052
- getGIF: function getGIF() {
2053
- var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
2054
- var callback = arguments[1];
2055
-
2056
- callback = utils.isFunction(callback) ? callback : noop$3;
2057
-
2058
- var canvas = document.createElement('canvas');
2059
- var context = void 0;
2060
- var existingImages = options.images;
2061
- var hasExistingImages = !!existingImages.length;
2062
- var cameraStream = options.cameraStream,
2063
- crop = options.crop,
2064
- filter = options.filter,
2065
- fontColor = options.fontColor,
2066
- fontFamily = options.fontFamily,
2067
- fontWeight = options.fontWeight,
2068
- keepCameraOn = options.keepCameraOn,
2069
- numWorkers = options.numWorkers,
2070
- progressCallback = options.progressCallback,
2071
- saveRenderingContexts = options.saveRenderingContexts,
2072
- savedRenderingContexts = options.savedRenderingContexts,
2073
- text = options.text,
2074
- textAlign = options.textAlign,
2075
- textBaseline = options.textBaseline,
2076
- videoElement = options.videoElement,
2077
- videoHeight = options.videoHeight,
2078
- videoWidth = options.videoWidth,
2079
- webcamVideoElement = options.webcamVideoElement;
2080
-
2081
- var gifWidth = Number(options.gifWidth);
2082
- var gifHeight = Number(options.gifHeight);
2083
- var interval = Number(options.interval);
2084
- var sampleInterval = Number(options.sampleInterval);
2085
- var waitBetweenFrames = hasExistingImages ? 0 : interval * 1000;
2086
- var renderingContextsToSave = [];
2087
- var numFrames = savedRenderingContexts.length ? savedRenderingContexts.length : options.numFrames;
2088
- var pendingFrames = numFrames;
2089
- var ag = new AnimatedGIF(options);
2090
- var fontSize = utils.getFontSize(options);
2091
- var textXCoordinate = options.textXCoordinate ? options.textXCoordinate : textAlign === 'left' ? 1 : textAlign === 'right' ? gifWidth : gifWidth / 2;
2092
- var textYCoordinate = options.textYCoordinate ? options.textYCoordinate : textBaseline === 'top' ? 1 : textBaseline === 'center' ? gifHeight / 2 : gifHeight;
2093
- var font = fontWeight + ' ' + fontSize + ' ' + fontFamily;
2094
- var sourceX = crop ? Math.floor(crop.scaledWidth / 2) : 0;
2095
- var sourceWidth = crop ? videoWidth - crop.scaledWidth : 0;
2096
- var sourceY = crop ? Math.floor(crop.scaledHeight / 2) : 0;
2097
- var sourceHeight = crop ? videoHeight - crop.scaledHeight : 0;
2098
- var captureFrames = function captureSingleFrame() {
2099
- var framesLeft = pendingFrames - 1;
2100
-
2101
- if (savedRenderingContexts.length) {
2102
- context.putImageData(savedRenderingContexts[numFrames - pendingFrames], 0, 0);
2103
-
2104
- finishCapture();
2105
- } else {
2106
- drawVideo();
2107
- }
2108
-
2109
- function drawVideo() {
2110
- try {
2111
- // Makes sure the canvas video heights/widths are in bounds
2112
- if (sourceWidth > videoWidth) {
2113
- sourceWidth = videoWidth;
2114
- }
2115
-
2116
- if (sourceHeight > videoHeight) {
2117
- sourceHeight = videoHeight;
2118
- }
2119
-
2120
- if (sourceX < 0) {
2121
- sourceX = 0;
2122
- }
2123
-
2124
- if (sourceY < 0) {
2125
- sourceY = 0;
2126
- }
2127
-
2128
- context.filter = filter;
2129
-
2130
- context.drawImage(videoElement, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, gifWidth, gifHeight);
2131
-
2132
- finishCapture();
2133
- } catch (e) {
2134
- // There is a Firefox bug that sometimes throws NS_ERROR_NOT_AVAILABLE and
2135
- // and IndexSizeError errors when drawing a video element to the canvas
2136
- if (e.name === 'NS_ERROR_NOT_AVAILABLE') {
2137
- // Wait 100ms before trying again
2138
- utils.requestTimeout(drawVideo, 100);
2139
- } else {
2140
- throw e;
2141
- }
2142
- }
2143
- }
2144
-
2145
- function finishCapture() {
2146
- var imageData = void 0;
2147
-
2148
- if (saveRenderingContexts) {
2149
- renderingContextsToSave.push(context.getImageData(0, 0, gifWidth, gifHeight));
2150
- }
2151
-
2152
- // If there is text to display, make sure to display it on the canvas after the image is drawn
2153
- if (text) {
2154
- context.font = font;
2155
- context.fillStyle = fontColor;
2156
- context.textAlign = textAlign;
2157
- context.textBaseline = textBaseline;
2158
- context.fillText(text, textXCoordinate, textYCoordinate);
2159
- }
2160
-
2161
- imageData = context.getImageData(0, 0, gifWidth, gifHeight);
2162
-
2163
- ag.addFrameImageData(imageData);
2164
-
2165
- pendingFrames = framesLeft;
2166
-
2167
- // Call back with an r value indicating how far along we are in capture
2168
- progressCallback((numFrames - pendingFrames) / numFrames);
2169
-
2170
- if (framesLeft > 0) {
2171
- // test
2172
- utils.requestTimeout(captureSingleFrame, waitBetweenFrames);
2173
- }
2174
-
2175
- if (!pendingFrames) {
2176
- ag.getBase64GIF(function (image) {
2177
- callback({
2178
- 'error': false,
2179
- 'errorCode': '',
2180
- 'errorMsg': '',
2181
- 'image': image,
2182
- 'cameraStream': cameraStream,
2183
- 'videoElement': videoElement,
2184
- 'webcamVideoElement': webcamVideoElement,
2185
- 'savedRenderingContexts': renderingContextsToSave,
2186
- 'keepCameraOn': keepCameraOn
2187
- });
2188
- });
2189
- }
2190
- }
2191
- };
2192
-
2193
- numFrames = numFrames !== undefined ? numFrames : 10;
2194
- interval = interval !== undefined ? interval : 0.1; // In seconds
2195
-
2196
- canvas.width = gifWidth;
2197
- canvas.height = gifHeight;
2198
- context = canvas.getContext('2d');
2199
-
2200
- (function capture() {
2201
- if (!savedRenderingContexts.length && videoElement.currentTime === 0) {
2202
- utils.requestTimeout(capture, 100);
2203
-
2204
- return;
2205
- }
2206
-
2207
- captureFrames();
2208
- })();
2209
- },
2210
- getCropDimensions: function getCropDimensions() {
2211
- var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
2212
-
2213
- var width = obj.videoWidth;
2214
- var height = obj.videoHeight;
2215
- var gifWidth = obj.gifWidth;
2216
- var gifHeight = obj.gifHeight;
2217
- var result = {
2218
- width: 0,
2219
- height: 0,
2220
- scaledWidth: 0,
2221
- scaledHeight: 0
2222
- };
2223
-
2224
- if (width > height) {
2225
- result.width = Math.round(width * (gifHeight / height)) - gifWidth;
2226
- result.scaledWidth = Math.round(result.width * (height / gifHeight));
2227
- } else {
2228
- result.height = Math.round(height * (gifWidth / width)) - gifHeight;
2229
- result.scaledHeight = Math.round(result.height * (width / gifWidth));
2230
- }
2231
-
2232
- return result;
2233
- }
2234
- };
2235
-
2236
- /*
2237
- videoStream.js
2238
- ==============
2239
- */
2240
-
2241
- /* Copyright 2017 Yahoo Inc.
2242
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
2243
- */
2244
-
2245
- // Dependencies
2246
- var videoStream = {
2247
- loadedData: false,
2248
- defaultVideoDimensions: {
2249
- width: 640,
2250
- height: 480
2251
- },
2252
- findVideoSize: function findVideoSizeMethod(obj) {
2253
- findVideoSizeMethod.attempts = findVideoSizeMethod.attempts || 0;
2254
-
2255
- var cameraStream = obj.cameraStream,
2256
- completedCallback = obj.completedCallback,
2257
- videoElement = obj.videoElement;
2258
-
2259
-
2260
- if (!videoElement) {
2261
- return;
2262
- }
2263
-
2264
- if (videoElement.videoWidth > 0 && videoElement.videoHeight > 0) {
2265
- videoElement.removeEventListener('loadeddata', videoStream.findVideoSize);
2266
-
2267
- completedCallback({
2268
- videoElement: videoElement,
2269
- cameraStream: cameraStream,
2270
- videoWidth: videoElement.videoWidth,
2271
- videoHeight: videoElement.videoHeight
2272
- });
2273
- } else {
2274
- if (findVideoSizeMethod.attempts < 10) {
2275
- findVideoSizeMethod.attempts += 1;
2276
-
2277
- utils.requestTimeout(function () {
2278
- videoStream.findVideoSize(obj);
2279
- }, 400);
2280
- } else {
2281
- completedCallback({
2282
- videoElement: videoElement,
2283
- cameraStream: cameraStream,
2284
- videoWidth: videoStream.defaultVideoDimensions.width,
2285
- videoHeight: videoStream.defaultVideoDimensions.height
2286
- });
2287
- }
2288
- }
2289
- },
2290
- onStreamingTimeout: function onStreamingTimeout(callback) {
2291
- if (utils.isFunction(callback)) {
2292
- callback({
2293
- error: true,
2294
- errorCode: 'getUserMedia',
2295
- errorMsg: 'There was an issue with the getUserMedia API - Timed out while trying to start streaming',
2296
- image: null,
2297
- cameraStream: {}
2298
- });
2299
- }
2300
- },
2301
- stream: function stream(obj) {
2302
- var existingVideo = utils.isArray(obj.existingVideo) ? obj.existingVideo[0] : obj.existingVideo;
2303
- var cameraStream = obj.cameraStream,
2304
- completedCallback = obj.completedCallback,
2305
- streamedCallback = obj.streamedCallback,
2306
- videoElement = obj.videoElement;
2307
-
2308
-
2309
- if (utils.isFunction(streamedCallback)) {
2310
- streamedCallback();
2311
- }
2312
-
2313
- if (existingVideo) {
2314
- if (utils.isString(existingVideo)) {
2315
- videoElement.src = existingVideo;
2316
- videoElement.innerHTML = '<source src="' + existingVideo + '" type="video/' + utils.getExtension(existingVideo) + '" />';
2317
- } else if (existingVideo instanceof Blob) {
2318
- try {
2319
- videoElement.src = utils.URL.createObjectURL(existingVideo);
2320
- } catch (e) {}
2321
-
2322
- videoElement.innerHTML = '<source src="' + existingVideo + '" type="' + existingVideo.type + '" />';
2323
- }
2324
- } else if (videoElement.mozSrcObject) {
2325
- videoElement.mozSrcObject = cameraStream;
2326
- } else if (utils.URL) {
2327
- try {
2328
- videoElement.srcObject = cameraStream;
2329
- videoElement.src = utils.URL.createObjectURL(cameraStream);
2330
- } catch (e) {
2331
- videoElement.srcObject = cameraStream;
2332
- }
2333
- }
2334
-
2335
- videoElement.play();
2336
-
2337
- utils.requestTimeout(function checkLoadedData() {
2338
- checkLoadedData.count = checkLoadedData.count || 0;
2339
-
2340
- if (videoStream.loadedData === true) {
2341
- videoStream.findVideoSize({
2342
- videoElement: videoElement,
2343
- cameraStream: cameraStream,
2344
- completedCallback: completedCallback
2345
- });
2346
-
2347
- videoStream.loadedData = false;
2348
- } else {
2349
- checkLoadedData.count += 1;
2350
-
2351
- if (checkLoadedData.count > 10) {
2352
- videoStream.findVideoSize({
2353
- videoElement: videoElement,
2354
- cameraStream: cameraStream,
2355
- completedCallback: completedCallback
2356
- });
2357
- } else {
2358
- checkLoadedData();
2359
- }
2360
- }
2361
- }, 0);
2362
- },
2363
- startStreaming: function startStreaming(obj) {
2364
- var errorCallback = utils.isFunction(obj.error) ? obj.error : utils.noop;
2365
- var streamedCallback = utils.isFunction(obj.streamed) ? obj.streamed : utils.noop;
2366
- var completedCallback = utils.isFunction(obj.completed) ? obj.completed : utils.noop;
2367
- var crossOrigin = obj.crossOrigin,
2368
- existingVideo = obj.existingVideo,
2369
- lastCameraStream = obj.lastCameraStream,
2370
- options = obj.options,
2371
- webcamVideoElement = obj.webcamVideoElement;
2372
-
2373
- var videoElement = utils.isElement(existingVideo) ? existingVideo : webcamVideoElement ? webcamVideoElement : document.createElement('video');
2374
- var cameraStream = void 0;
2375
-
2376
- if (crossOrigin) {
2377
- videoElement.crossOrigin = options.crossOrigin;
2378
- }
2379
-
2380
- videoElement.autoplay = true;
2381
- videoElement.loop = true;
2382
- videoElement.muted = true;
2383
- videoElement.addEventListener('loadeddata', function (event) {
2384
- videoStream.loadedData = true;
2385
- if (options.offset) {
2386
- videoElement.currentTime = options.offset;
2387
- }
2388
- });
2389
-
2390
- if (existingVideo) {
2391
- videoStream.stream({
2392
- videoElement: videoElement,
2393
- existingVideo: existingVideo,
2394
- completedCallback: completedCallback
2395
- });
2396
- } else if (lastCameraStream) {
2397
- videoStream.stream({
2398
- videoElement: videoElement,
2399
- cameraStream: lastCameraStream,
2400
- streamedCallback: streamedCallback,
2401
- completedCallback: completedCallback
2402
- });
2403
- } else {
2404
- utils.getUserMedia({
2405
- video: true
2406
- }, function (stream) {
2407
- videoStream.stream({
2408
- videoElement: videoElement,
2409
- cameraStream: stream,
2410
- streamedCallback: streamedCallback,
2411
- completedCallback: completedCallback
2412
- });
2413
- }, errorCallback);
2414
- }
2415
- },
2416
- startVideoStreaming: function startVideoStreaming(callback) {
2417
- var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2418
-
2419
- var timeoutLength = options.timeout !== undefined ? options.timeout : 0;
2420
- var originalCallback = options.callback;
2421
- var webcamVideoElement = options.webcamVideoElement;
2422
- var noGetUserMediaSupportTimeout = void 0;
2423
-
2424
- // Some browsers apparently have support for video streaming because of the
2425
- // presence of the getUserMedia function, but then do not answer our
2426
- // calls for streaming.
2427
- // So we'll set up this timeout and if nothing happens after a while, we'll
2428
- // conclude that there's no actual getUserMedia support.
2429
- if (timeoutLength > 0) {
2430
- noGetUserMediaSupportTimeout = utils.requestTimeout(function () {
2431
- videoStream.onStreamingTimeout(originalCallback);
2432
- }, 10000);
2433
- }
2434
-
2435
- videoStream.startStreaming({
2436
- error: function error() {
2437
- originalCallback({
2438
- error: true,
2439
- errorCode: 'getUserMedia',
2440
- errorMsg: 'There was an issue with the getUserMedia API - the user probably denied permission',
2441
- image: null,
2442
- cameraStream: {}
2443
- });
2444
- },
2445
- streamed: function streamed() {
2446
- // The streaming started somehow, so we can assume there is getUserMedia support
2447
- clearTimeout(noGetUserMediaSupportTimeout);
2448
- },
2449
- completed: function completed() {
2450
- var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
2451
- var cameraStream = obj.cameraStream,
2452
- videoElement = obj.videoElement,
2453
- videoHeight = obj.videoHeight,
2454
- videoWidth = obj.videoWidth;
2455
-
2456
-
2457
- callback({
2458
- cameraStream: cameraStream,
2459
- videoElement: videoElement,
2460
- videoHeight: videoHeight,
2461
- videoWidth: videoWidth
2462
- });
2463
- },
2464
- lastCameraStream: options.lastCameraStream,
2465
- webcamVideoElement: webcamVideoElement,
2466
- crossOrigin: options.crossOrigin,
2467
- options: options
2468
- });
2469
- },
2470
- stopVideoStreaming: function stopVideoStreaming(obj) {
2471
- obj = utils.isObject(obj) ? obj : {};
2472
-
2473
- var _obj = obj,
2474
- keepCameraOn = _obj.keepCameraOn,
2475
- videoElement = _obj.videoElement,
2476
- webcamVideoElement = _obj.webcamVideoElement;
2477
-
2478
- var cameraStream = obj.cameraStream || {};
2479
- var cameraStreamTracks = cameraStream.getTracks ? cameraStream.getTracks() || [] : [];
2480
- var hasCameraStreamTracks = !!cameraStreamTracks.length;
2481
- var firstCameraStreamTrack = cameraStreamTracks[0];
2482
-
2483
- if (!keepCameraOn && hasCameraStreamTracks) {
2484
- if (utils.isFunction(firstCameraStreamTrack.stop)) {
2485
- // Stops the camera stream
2486
- firstCameraStreamTrack.stop();
2487
- }
2488
- }
2489
-
2490
- if (utils.isElement(videoElement) && !webcamVideoElement) {
2491
- // Pauses the video, revokes the object URL (freeing up memory), and remove the video element
2492
- videoElement.pause();
2493
-
2494
- // Destroys the object url
2495
- if (utils.isFunction(utils.URL.revokeObjectURL) && !utils.webWorkerError) {
2496
- if (videoElement.src) {
2497
- utils.URL.revokeObjectURL(videoElement.src);
2498
- }
2499
- }
2500
-
2501
- // Removes the video element from the DOM
2502
- utils.removeElement(videoElement);
2503
- }
2504
- }
2505
- };
2506
-
2507
- /*
2508
- stopVideoStreaming.js
2509
- =====================
2510
- */
2511
-
2512
- /* Copyright 2017 Yahoo Inc.
2513
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
2514
- */
2515
-
2516
- function stopVideoStreaming(options) {
2517
- options = utils.isObject(options) ? options : {};
2518
-
2519
- videoStream.stopVideoStreaming(options);
2520
- }
2521
-
2522
- /*
2523
- createAndGetGIF.js
2524
- ==================
2525
- */
2526
-
2527
- /* Copyright 2017 Yahoo Inc.
2528
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
2529
- */
2530
-
2531
- // Dependencies
2532
- function createAndGetGIF(obj, callback) {
2533
- var options = obj.options || {};
2534
-
2535
- var images = options.images,
2536
- video = options.video;
2537
-
2538
- var gifWidth = Number(options.gifWidth);
2539
- var gifHeight = Number(options.gifHeight);
2540
- var numFrames = Number(options.numFrames);
2541
- var cameraStream = obj.cameraStream,
2542
- videoElement = obj.videoElement,
2543
- videoWidth = obj.videoWidth,
2544
- videoHeight = obj.videoHeight;
2545
-
2546
- var cropDimensions = screenShot.getCropDimensions({
2547
- videoWidth: videoWidth,
2548
- videoHeight: videoHeight,
2549
- gifHeight: gifHeight,
2550
- gifWidth: gifWidth
2551
- });
2552
- var completeCallback = callback;
2553
-
2554
- options.crop = cropDimensions;
2555
- options.videoElement = videoElement;
2556
- options.videoWidth = videoWidth;
2557
- options.videoHeight = videoHeight;
2558
- options.cameraStream = cameraStream;
2559
-
2560
- if (!utils.isElement(videoElement)) {
2561
- return;
2562
- }
2563
-
2564
- videoElement.width = gifWidth + cropDimensions.width;
2565
- videoElement.height = gifHeight + cropDimensions.height;
2566
-
2567
- if (!options.webcamVideoElement) {
2568
- utils.setCSSAttr(videoElement, {
2569
- position: 'fixed',
2570
- opacity: '0'
2571
- });
2572
-
2573
- document.body.appendChild(videoElement);
2574
- }
2575
-
2576
- // Firefox doesn't seem to obey autoplay if the element is not in the DOM when the content
2577
- // is loaded, so we must manually trigger play after adding it, or the video will be frozen
2578
- videoElement.play();
2579
-
2580
- screenShot.getGIF(options, function (obj) {
2581
- if ((!images || !images.length) && (!video || !video.length)) {
2582
- stopVideoStreaming(obj);
2583
- }
2584
-
2585
- completeCallback(obj);
2586
- });
2587
- }
2588
-
2589
- /*
2590
- existingVideo.js
2591
- ================
2592
- */
2593
-
2594
- /* Copyright 2017 Yahoo Inc.
2595
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
2596
- */
2597
-
2598
- // Dependencies
2599
- function existingVideo() {
2600
- var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
2601
- var callback = obj.callback,
2602
- existingVideo = obj.existingVideo,
2603
- options = obj.options;
2604
-
2605
- var skipObj = {
2606
- getUserMedia: true,
2607
- 'window.URL': true
2608
- };
2609
- var errorObj = error.validate(skipObj);
2610
- var loadedImages = 0;
2611
- var videoType = void 0;
2612
- var videoSrc = void 0;
2613
- var tempImage = void 0;
2614
- var ag = void 0;
2615
-
2616
- if (errorObj.error) {
2617
- return callback(errorObj);
2618
- }
2619
-
2620
- if (utils.isElement(existingVideo) && existingVideo.src) {
2621
- videoSrc = existingVideo.src;
2622
- videoType = utils.getExtension(videoSrc);
2623
-
2624
- if (!utils.isSupported.videoCodecs[videoType]) {
2625
- return callback(error.messages.videoCodecs);
2626
- }
2627
- } else if (utils.isArray(existingVideo)) {
2628
- utils.each(existingVideo, function (iterator, videoSrc) {
2629
- if (videoSrc instanceof Blob) {
2630
- videoType = videoSrc.type.substr(videoSrc.type.lastIndexOf('/') + 1, videoSrc.length);
2631
- } else {
2632
- videoType = videoSrc.substr(videoSrc.lastIndexOf('.') + 1, videoSrc.length);
2633
- }
2634
-
2635
- if (utils.isSupported.videoCodecs[videoType]) {
2636
- existingVideo = videoSrc;
2637
-
2638
- return false;
2639
- }
2640
- });
2641
- }
2642
-
2643
- videoStream.startStreaming({
2644
- completed: function completed(obj) {
2645
- obj.options = options || {};
2646
-
2647
- createAndGetGIF(obj, callback);
2648
- },
2649
- existingVideo: existingVideo,
2650
- crossOrigin: options.crossOrigin,
2651
- options: options
2652
- });
2653
- }
2654
-
2655
- /*
2656
- existingWebcam.js
2657
- =================
2658
- */
2659
-
2660
- /* Copyright 2017 Yahoo Inc.
2661
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
2662
- */
2663
-
2664
- // Dependencies
2665
- function existingWebcam() {
2666
- var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
2667
- var callback = obj.callback,
2668
- lastCameraStream = obj.lastCameraStream,
2669
- options = obj.options,
2670
- webcamVideoElement = obj.webcamVideoElement;
2671
-
2672
-
2673
- if (!isWebCamGIFSupported()) {
2674
- return callback(error.validate());
2675
- }
2676
-
2677
- if (options.savedRenderingContexts.length) {
2678
- screenShot.getGIF(options, function (obj) {
2679
- callback(obj);
2680
- });
2681
-
2682
- return;
2683
- }
2684
-
2685
- videoStream.startVideoStreaming(function () {
2686
- var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
2687
-
2688
- obj.options = options || {};
2689
-
2690
- createAndGetGIF(obj, callback);
2691
- }, {
2692
- lastCameraStream: lastCameraStream,
2693
- callback: callback,
2694
- webcamVideoElement: webcamVideoElement,
2695
- crossOrigin: options.crossOrigin
2696
- });
2697
- }
2698
-
2699
- /*
2700
- createGIF.js
2701
- ============
2702
- */
2703
-
2704
- /* Copyright 2017 Yahoo Inc.
2705
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
2706
- */
2707
-
2708
- // Dependencies
2709
- function createGIF(userOptions, callback) {
2710
- callback = utils.isFunction(userOptions) ? userOptions : callback;
2711
- userOptions = utils.isObject(userOptions) ? userOptions : {};
2712
-
2713
- if (!utils.isFunction(callback)) {
2714
- return;
2715
- }
2716
-
2717
- var options = utils.normalizeOptions(defaultOptions, userOptions) || {};
2718
- var lastCameraStream = userOptions.cameraStream;
2719
- var images = options.images;
2720
- var imagesLength = images ? images.length : 0;
2721
- var video = options.video;
2722
- var webcamVideoElement = options.webcamVideoElement;
2723
-
2724
- options = utils.normalizeOptions(options, {
2725
- 'gifWidth': Math.floor(options.gifWidth),
2726
- 'gifHeight': Math.floor(options.gifHeight)
2727
- });
2728
-
2729
- // If the user would like to create a GIF from an existing image(s)
2730
- if (imagesLength) {
2731
- existingImages({
2732
- 'images': images,
2733
- 'imagesLength': imagesLength,
2734
- 'callback': callback,
2735
- 'options': options
2736
- });
2737
- } else if (video) {
2738
- // If the user would like to create a GIF from an existing HTML5 video
2739
- existingVideo({
2740
- 'existingVideo': video,
2741
- callback: callback,
2742
- options: options
2743
- });
2744
- } else {
2745
- // If the user would like to create a GIF from a webcam stream
2746
- existingWebcam({
2747
- lastCameraStream: lastCameraStream,
2748
- callback: callback,
2749
- webcamVideoElement: webcamVideoElement,
2750
- options: options
2751
- });
2752
- }
2753
- }
2754
-
2755
- /*
2756
- takeSnapShot.js
2757
- ===============
2758
- */
2759
-
2760
- /* Copyright 2017 Yahoo Inc.
2761
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
2762
- */
2763
-
2764
- function takeSnapShot(userOptions, callback) {
2765
- callback = utils.isFunction(userOptions) ? userOptions : callback;
2766
- userOptions = utils.isObject(userOptions) ? userOptions : {};
2767
-
2768
- if (!utils.isFunction(callback)) {
2769
- return;
2770
- }
2771
-
2772
- var mergedOptions = utils.normalizeOptions(defaultOptions, userOptions);
2773
- var options = utils.normalizeOptions(mergedOptions, {
2774
- 'interval': .1,
2775
- 'numFrames': 1,
2776
- 'gifWidth': Math.floor(mergedOptions.gifWidth),
2777
- 'gifHeight': Math.floor(mergedOptions.gifHeight)
2778
- });
2779
-
2780
- createGIF(options, callback);
2781
- }
2782
-
2783
- /*
2784
- API.js
2785
- ======
2786
- */
2787
-
2788
- /* Copyright 2017 Yahoo Inc.
2789
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
2790
- */
2791
-
2792
- // Dependencies
2793
- var API = {
2794
- 'utils': utils$2,
2795
- 'error': error$2,
2796
- 'defaultOptions': defaultOptions$2,
2797
- 'createGIF': createGIF,
2798
- 'takeSnapShot': takeSnapShot,
2799
- 'stopVideoStreaming': stopVideoStreaming,
2800
- 'isSupported': isSupported,
2801
- 'isWebCamGIFSupported': isWebCamGIFSupported,
2802
- 'isExistingVideoGIFSupported': isExistingVideoGIFSupported,
2803
- 'isExistingImagesGIFSupported': isSupported$1,
2804
- 'VERSION': '0.4.5'
2805
- };
2806
-
2807
- /*
2808
- index.js
2809
- ========
2810
- */
2811
-
2812
- /* Copyright 2017 Yahoo Inc.
2813
- * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
2814
- */
2815
-
2816
- // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, and plain browser loading
2817
- if (typeof define === 'function' && define.amd) {
2818
- define([], function () {
2819
- return API;
2820
- });
2821
- } else if (typeof exports !== 'undefined') {
2822
- module.exports = API;
2823
- } else {
2824
- window.gifshot = API;
2825
- }
2826
- }(typeof window !== "undefined" ? window : {}, typeof document !== "undefined" ? document : { createElement: function() {} }, typeof window !== "undefined" ? window.navigator : {}));