@tsparticles/shape-image 3.0.0-alpha.1 → 3.0.0-beta.0

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 (92) hide show
  1. package/README.md +16 -12
  2. package/browser/GifUtils/ByteStream.js +44 -0
  3. package/browser/GifUtils/Constants.js +2 -0
  4. package/browser/GifUtils/Enums/DisposalMethod.js +1 -0
  5. package/browser/GifUtils/Types/ApplicationExtension.js +1 -0
  6. package/browser/GifUtils/Types/Frame.js +1 -0
  7. package/browser/GifUtils/Types/GIF.js +1 -0
  8. package/browser/GifUtils/Types/GIFDataHeaders.js +1 -0
  9. package/browser/GifUtils/Types/GIFProgressCallbackFunction.js +1 -0
  10. package/browser/GifUtils/Types/PlainTextData.js +1 -0
  11. package/browser/GifUtils/Utils.js +324 -0
  12. package/browser/ImageDrawer.js +122 -58
  13. package/browser/ImagePreloader.js +33 -0
  14. package/browser/Options/Classes/Preload.js +29 -0
  15. package/browser/Options/Interfaces/IPreload.js +1 -0
  16. package/browser/Utils.js +35 -9
  17. package/browser/index.js +42 -2
  18. package/browser/types.js +1 -0
  19. package/cjs/GifUtils/ByteStream.js +48 -0
  20. package/cjs/GifUtils/Constants.js +5 -0
  21. package/cjs/GifUtils/Enums/DisposalMethod.js +2 -0
  22. package/cjs/GifUtils/Types/ApplicationExtension.js +2 -0
  23. package/cjs/GifUtils/Types/Frame.js +2 -0
  24. package/cjs/GifUtils/Types/GIF.js +2 -0
  25. package/cjs/GifUtils/Types/GIFDataHeaders.js +2 -0
  26. package/cjs/GifUtils/Types/GIFProgressCallbackFunction.js +2 -0
  27. package/cjs/GifUtils/Types/PlainTextData.js +2 -0
  28. package/cjs/GifUtils/Utils.js +329 -0
  29. package/cjs/ImageDrawer.js +124 -71
  30. package/cjs/ImagePreloader.js +37 -0
  31. package/cjs/Options/Classes/Preload.js +33 -0
  32. package/cjs/Options/Interfaces/IPreload.js +2 -0
  33. package/cjs/Utils.js +66 -52
  34. package/cjs/index.js +42 -13
  35. package/cjs/types.js +2 -0
  36. package/esm/GifUtils/ByteStream.js +44 -0
  37. package/esm/GifUtils/Constants.js +2 -0
  38. package/esm/GifUtils/Enums/DisposalMethod.js +1 -0
  39. package/esm/GifUtils/Types/ApplicationExtension.js +1 -0
  40. package/esm/GifUtils/Types/Frame.js +1 -0
  41. package/esm/GifUtils/Types/GIF.js +1 -0
  42. package/esm/GifUtils/Types/GIFDataHeaders.js +1 -0
  43. package/esm/GifUtils/Types/GIFProgressCallbackFunction.js +1 -0
  44. package/esm/GifUtils/Types/PlainTextData.js +1 -0
  45. package/esm/GifUtils/Utils.js +324 -0
  46. package/esm/ImageDrawer.js +122 -58
  47. package/esm/ImagePreloader.js +33 -0
  48. package/esm/Options/Classes/Preload.js +29 -0
  49. package/esm/Options/Interfaces/IPreload.js +1 -0
  50. package/esm/Utils.js +35 -9
  51. package/esm/index.js +42 -2
  52. package/esm/types.js +1 -0
  53. package/package.json +6 -5
  54. package/report.html +4 -4
  55. package/tsparticles.shape.image.js +676 -73
  56. package/tsparticles.shape.image.min.js +1 -1
  57. package/tsparticles.shape.image.min.js.LICENSE.txt +1 -8
  58. package/types/GifUtils/ByteStream.d.ts +11 -0
  59. package/types/GifUtils/Constants.d.ts +2 -0
  60. package/types/GifUtils/Enums/DisposalMethod.d.ts +10 -0
  61. package/types/GifUtils/Types/ApplicationExtension.d.ts +5 -0
  62. package/types/GifUtils/Types/Frame.d.ts +19 -0
  63. package/types/GifUtils/Types/GIF.d.ts +16 -0
  64. package/types/GifUtils/Types/GIFDataHeaders.d.ts +9 -0
  65. package/types/GifUtils/Types/GIFProgressCallbackFunction.d.ts +2 -0
  66. package/types/GifUtils/Types/PlainTextData.d.ts +11 -0
  67. package/types/GifUtils/Utils.d.ts +4 -0
  68. package/types/IImageShape.d.ts +2 -1
  69. package/types/ImageDrawer.d.ts +9 -9
  70. package/types/ImagePreloader.d.ts +10 -0
  71. package/types/Options/Classes/Preload.d.ts +12 -0
  72. package/types/Options/Interfaces/IPreload.d.ts +8 -0
  73. package/types/Utils.d.ts +15 -5
  74. package/types/index.d.ts +2 -2
  75. package/types/types.d.ts +17 -0
  76. package/umd/GifUtils/ByteStream.js +58 -0
  77. package/umd/GifUtils/Constants.js +15 -0
  78. package/umd/GifUtils/Enums/DisposalMethod.js +12 -0
  79. package/umd/GifUtils/Types/ApplicationExtension.js +12 -0
  80. package/umd/GifUtils/Types/Frame.js +12 -0
  81. package/umd/GifUtils/Types/GIF.js +12 -0
  82. package/umd/GifUtils/Types/GIFDataHeaders.js +12 -0
  83. package/umd/GifUtils/Types/GIFProgressCallbackFunction.js +12 -0
  84. package/umd/GifUtils/Types/PlainTextData.js +12 -0
  85. package/umd/GifUtils/Utils.js +339 -0
  86. package/umd/ImageDrawer.js +122 -58
  87. package/umd/ImagePreloader.js +47 -0
  88. package/umd/Options/Classes/Preload.js +43 -0
  89. package/umd/Options/Interfaces/IPreload.js +12 -0
  90. package/umd/Utils.js +37 -10
  91. package/umd/index.js +43 -3
  92. package/umd/types.js +12 -0
@@ -4,7 +4,7 @@
4
4
  * Demo / Generator : https://particles.js.org/
5
5
  * GitHub : https://www.github.com/matteobruni/tsparticles
6
6
  * How to use? : Check the GitHub README
7
- * v3.0.0-alpha.1
7
+ * v3.0.0-beta.0
8
8
  */
9
9
  (function webpackUniversalModuleDefinition(root, factory) {
10
10
  if(typeof exports === 'object' && typeof module === 'object')
@@ -91,13 +91,430 @@ __webpack_require__.r(__webpack_exports__);
91
91
 
92
92
  // EXPORTS
93
93
  __webpack_require__.d(__webpack_exports__, {
94
- "loadImageShape": () => (/* binding */ loadImageShape)
94
+ loadImageShape: () => (/* binding */ loadImageShape)
95
95
  });
96
96
 
97
97
  // EXTERNAL MODULE: external {"commonjs":"@tsparticles/engine","commonjs2":"@tsparticles/engine","amd":"@tsparticles/engine","root":"window"}
98
98
  var engine_root_window_ = __webpack_require__(533);
99
+ ;// CONCATENATED MODULE: ./dist/browser/GifUtils/Constants.js
100
+ const InterlaceOffsets = [0, 4, 2, 1];
101
+ const InterlaceSteps = [8, 8, 4, 2];
102
+ ;// CONCATENATED MODULE: ./dist/browser/GifUtils/ByteStream.js
103
+ class ByteStream {
104
+ constructor(bytes) {
105
+ this.pos = 0;
106
+ this.data = new Uint8ClampedArray(bytes);
107
+ }
108
+ getString(count) {
109
+ const slice = this.data.slice(this.pos, this.pos + count);
110
+ this.pos += slice.length;
111
+ return slice.reduce((acc, curr) => acc + String.fromCharCode(curr), "");
112
+ }
113
+ nextByte() {
114
+ return this.data[this.pos++];
115
+ }
116
+ nextTwoBytes() {
117
+ this.pos += 2;
118
+ return this.data[this.pos - 2] + (this.data[this.pos - 1] << 8);
119
+ }
120
+ readSubBlocks() {
121
+ let blockString = "",
122
+ size = 0;
123
+ do {
124
+ size = this.data[this.pos++];
125
+ for (let count = size; --count >= 0; blockString += String.fromCharCode(this.data[this.pos++])) {}
126
+ } while (size !== 0);
127
+ return blockString;
128
+ }
129
+ readSubBlocksBin() {
130
+ let size = 0,
131
+ len = 0;
132
+ for (let offset = 0; (size = this.data[this.pos + offset]) !== 0; offset += size + 1) {
133
+ len += size;
134
+ }
135
+ const blockData = new Uint8Array(len);
136
+ for (let i = 0; (size = this.data[this.pos++]) !== 0;) {
137
+ for (let count = size; --count >= 0; blockData[i++] = this.data[this.pos++]) {}
138
+ }
139
+ return blockData;
140
+ }
141
+ skipSubBlocks() {
142
+ for (; this.data[this.pos] !== 0; this.pos += this.data[this.pos] + 1) {}
143
+ this.pos++;
144
+ }
145
+ }
146
+ ;// CONCATENATED MODULE: ./dist/browser/GifUtils/Utils.js
147
+
148
+
149
+ function parseColorTable(byteStream, count) {
150
+ const colors = [];
151
+ for (let i = 0; i < count; i++) {
152
+ colors.push({
153
+ r: byteStream.data[byteStream.pos],
154
+ g: byteStream.data[byteStream.pos + 1],
155
+ b: byteStream.data[byteStream.pos + 2]
156
+ });
157
+ byteStream.pos += 3;
158
+ }
159
+ return colors;
160
+ }
161
+ async function parseExtensionBlock(byteStream, gif, getFrameIndex, getTransparencyIndex) {
162
+ switch (byteStream.nextByte()) {
163
+ case 249:
164
+ {
165
+ const frame = gif.frames[getFrameIndex(false)];
166
+ byteStream.pos++;
167
+ const packedByte = byteStream.nextByte();
168
+ frame.GCreserved = (packedByte & 0xe0) >>> 5;
169
+ frame.disposalMethod = (packedByte & 0x1c) >>> 2;
170
+ frame.userInputDelayFlag = (packedByte & 2) === 2;
171
+ const transparencyFlag = (packedByte & 1) === 1;
172
+ frame.delayTime = byteStream.nextTwoBytes() * 0xa;
173
+ const transparencyIndex = byteStream.nextByte();
174
+ if (transparencyFlag) {
175
+ getTransparencyIndex(transparencyIndex);
176
+ }
177
+ byteStream.pos++;
178
+ break;
179
+ }
180
+ case 255:
181
+ {
182
+ byteStream.pos++;
183
+ const applicationExtension = {
184
+ identifier: byteStream.getString(8),
185
+ authenticationCode: byteStream.getString(3),
186
+ data: byteStream.readSubBlocksBin()
187
+ };
188
+ gif.applicationExtensions.push(applicationExtension);
189
+ break;
190
+ }
191
+ case 254:
192
+ {
193
+ gif.comments.push([getFrameIndex(false), byteStream.readSubBlocks()]);
194
+ break;
195
+ }
196
+ case 1:
197
+ {
198
+ if (gif.globalColorTable.length === 0) {
199
+ throw new EvalError("plain text extension without global color table");
200
+ }
201
+ byteStream.pos++;
202
+ gif.frames[getFrameIndex(false)].plainTextData = {
203
+ left: byteStream.nextTwoBytes(),
204
+ top: byteStream.nextTwoBytes(),
205
+ width: byteStream.nextTwoBytes(),
206
+ height: byteStream.nextTwoBytes(),
207
+ charSize: {
208
+ width: byteStream.nextTwoBytes(),
209
+ height: byteStream.nextTwoBytes()
210
+ },
211
+ foregroundColor: byteStream.nextByte(),
212
+ backgroundColor: byteStream.nextByte(),
213
+ text: byteStream.readSubBlocks()
214
+ };
215
+ break;
216
+ }
217
+ default:
218
+ byteStream.skipSubBlocks();
219
+ break;
220
+ }
221
+ }
222
+ async function parseImageBlock(byteStream, gif, avgAlpha, getFrameIndex, getTransparencyIndex, progressCallback) {
223
+ const frame = gif.frames[getFrameIndex(true)];
224
+ frame.left = byteStream.nextTwoBytes();
225
+ frame.top = byteStream.nextTwoBytes();
226
+ frame.width = byteStream.nextTwoBytes();
227
+ frame.height = byteStream.nextTwoBytes();
228
+ const packedByte = byteStream.nextByte(),
229
+ localColorTableFlag = (packedByte & 0x80) === 0x80,
230
+ interlacedFlag = (packedByte & 0x40) === 0x40;
231
+ frame.sortFlag = (packedByte & 0x20) === 0x20;
232
+ frame.reserved = (packedByte & 0x18) >>> 3;
233
+ const localColorCount = 1 << (packedByte & 7) + 1;
234
+ if (localColorTableFlag) {
235
+ frame.localColorTable = parseColorTable(byteStream, localColorCount);
236
+ }
237
+ const getColor = index => {
238
+ const {
239
+ r,
240
+ g,
241
+ b
242
+ } = (localColorTableFlag ? frame.localColorTable : gif.globalColorTable)[index];
243
+ return {
244
+ r,
245
+ g,
246
+ b,
247
+ a: index === getTransparencyIndex(null) ? avgAlpha ? ~~((r + g + b) / 3) : 0 : 255
248
+ };
249
+ };
250
+ const image = (() => {
251
+ try {
252
+ return new ImageData(frame.width, frame.height, {
253
+ colorSpace: "srgb"
254
+ });
255
+ } catch (error) {
256
+ if (error instanceof DOMException && error.name === "IndexSizeError") {
257
+ return null;
258
+ }
259
+ throw error;
260
+ }
261
+ })();
262
+ if (image == null) {
263
+ throw new EvalError("GIF frame size is to large");
264
+ }
265
+ const minCodeSize = byteStream.nextByte(),
266
+ imageData = byteStream.readSubBlocksBin(),
267
+ clearCode = 1 << minCodeSize;
268
+ const readBits = (pos, len) => {
269
+ const bytePos = pos >>> 3,
270
+ bitPos = pos & 7;
271
+ return (imageData[bytePos] + (imageData[bytePos + 1] << 8) + (imageData[bytePos + 2] << 16) & (1 << len) - 1 << bitPos) >>> bitPos;
272
+ };
273
+ if (interlacedFlag) {
274
+ for (let code = 0, size = minCodeSize + 1, pos = 0, dic = [[0]], pass = 0; pass < 4; pass++) {
275
+ if (InterlaceOffsets[pass] < frame.height) {
276
+ for (let pixelPos = 0, lineIndex = 0;;) {
277
+ const last = code;
278
+ code = readBits(pos, size);
279
+ pos += size + 1;
280
+ if (code === clearCode) {
281
+ size = minCodeSize + 1;
282
+ dic.length = clearCode + 2;
283
+ for (let i = 0; i < dic.length; i++) {
284
+ dic[i] = i < clearCode ? [i] : [];
285
+ }
286
+ } else {
287
+ if (code >= dic.length) {
288
+ dic.push(dic[last].concat(dic[last][0]));
289
+ } else if (last !== clearCode) {
290
+ dic.push(dic[last].concat(dic[code][0]));
291
+ }
292
+ for (let i = 0; i < dic[code].length; i++) {
293
+ const {
294
+ r,
295
+ g,
296
+ b,
297
+ a
298
+ } = getColor(dic[code][i]);
299
+ image.data.set([r, g, b, a], InterlaceOffsets[pass] * frame.width + InterlaceSteps[pass] * lineIndex + pixelPos % (frame.width * 4));
300
+ pixelPos += 4;
301
+ }
302
+ if (dic.length === 1 << size && size < 0xc) {
303
+ size++;
304
+ }
305
+ }
306
+ if (pixelPos === frame.width * 4 * (lineIndex + 1)) {
307
+ lineIndex++;
308
+ if (InterlaceOffsets[pass] + InterlaceSteps[pass] * lineIndex >= frame.height) {
309
+ break;
310
+ }
311
+ }
312
+ }
313
+ }
314
+ progressCallback?.(byteStream.pos / (byteStream.data.length - 1), getFrameIndex(false) + 1, image, {
315
+ x: frame.left,
316
+ y: frame.top
317
+ }, {
318
+ width: gif.width,
319
+ height: gif.height
320
+ });
321
+ }
322
+ frame.image = image;
323
+ frame.bitmap = await createImageBitmap(image);
324
+ } else {
325
+ for (let code = 0, size = minCodeSize + 1, pos = 0, dic = [[0]], pixelPos = -4;;) {
326
+ const last = code;
327
+ code = readBits(pos, size);
328
+ pos += size;
329
+ if (code === clearCode) {
330
+ size = minCodeSize + 1;
331
+ dic.length = clearCode + 2;
332
+ for (let i = 0; i < dic.length; i++) {
333
+ dic[i] = i < clearCode ? [i] : [];
334
+ }
335
+ } else {
336
+ if (code === clearCode + 1) {
337
+ break;
338
+ }
339
+ if (code >= dic.length) {
340
+ dic.push(dic[last].concat(dic[last][0]));
341
+ } else if (last !== clearCode) {
342
+ dic.push(dic[last].concat(dic[code][0]));
343
+ }
344
+ for (let i = 0; i < dic[code].length; i++) {
345
+ const {
346
+ r,
347
+ g,
348
+ b,
349
+ a
350
+ } = getColor(dic[code][i]);
351
+ image.data.set([r, g, b, a], pixelPos += 4);
352
+ }
353
+ if (dic.length >= 1 << size && size < 0xc) {
354
+ size++;
355
+ }
356
+ }
357
+ }
358
+ frame.image = image;
359
+ frame.bitmap = await createImageBitmap(image);
360
+ progressCallback?.((byteStream.pos + 1) / byteStream.data.length, getFrameIndex(false) + 1, frame.image, {
361
+ x: frame.left,
362
+ y: frame.top
363
+ }, {
364
+ width: gif.width,
365
+ height: gif.height
366
+ });
367
+ }
368
+ }
369
+ async function parseBlock(byteStream, gif, avgAlpha, getFrameIndex, getTransparencyIndex, progressCallback) {
370
+ switch (byteStream.nextByte()) {
371
+ case 59:
372
+ return true;
373
+ case 44:
374
+ await parseImageBlock(byteStream, gif, avgAlpha, getFrameIndex, getTransparencyIndex, progressCallback);
375
+ break;
376
+ case 33:
377
+ await parseExtensionBlock(byteStream, gif, getFrameIndex, getTransparencyIndex);
378
+ break;
379
+ default:
380
+ throw new EvalError("undefined block found");
381
+ }
382
+ return false;
383
+ }
384
+ function getGIFLoopAmount(gif) {
385
+ for (const extension of gif.applicationExtensions) {
386
+ if (extension.identifier + extension.authenticationCode !== "NETSCAPE2.0") {
387
+ continue;
388
+ }
389
+ return extension.data[1] + (extension.data[2] << 8);
390
+ }
391
+ return NaN;
392
+ }
393
+ async function decodeGIF(gifURL, progressCallback, avgAlpha) {
394
+ if (!avgAlpha) avgAlpha = false;
395
+ const res = await fetch(gifURL);
396
+ if (!res.ok && res.status === 404) {
397
+ throw new EvalError("file not found");
398
+ }
399
+ const buffer = await res.arrayBuffer();
400
+ const gif = {
401
+ width: 0,
402
+ height: 0,
403
+ totalTime: 0,
404
+ colorRes: 0,
405
+ pixelAspectRatio: 0,
406
+ frames: [],
407
+ sortFlag: false,
408
+ globalColorTable: [],
409
+ backgroundImage: new ImageData(1, 1, {
410
+ colorSpace: "srgb"
411
+ }),
412
+ comments: [],
413
+ applicationExtensions: []
414
+ },
415
+ byteStream = new ByteStream(new Uint8ClampedArray(buffer));
416
+ if (byteStream.getString(6) !== "GIF89a") {
417
+ throw new Error("not a supported GIF file");
418
+ }
419
+ gif.width = byteStream.nextTwoBytes();
420
+ gif.height = byteStream.nextTwoBytes();
421
+ const packedByte = byteStream.nextByte(),
422
+ globalColorTableFlag = (packedByte & 0x80) === 0x80;
423
+ gif.colorRes = (packedByte & 0x70) >>> 4;
424
+ gif.sortFlag = (packedByte & 8) === 8;
425
+ const globalColorCount = 1 << (packedByte & 7) + 1,
426
+ backgroundColorIndex = byteStream.nextByte();
427
+ gif.pixelAspectRatio = byteStream.nextByte();
428
+ if (gif.pixelAspectRatio !== 0) {
429
+ gif.pixelAspectRatio = (gif.pixelAspectRatio + 0xf) / 0x40;
430
+ }
431
+ if (globalColorTableFlag) {
432
+ gif.globalColorTable = parseColorTable(byteStream, globalColorCount);
433
+ }
434
+ const backgroundImage = (() => {
435
+ try {
436
+ return new ImageData(gif.width, gif.height, {
437
+ colorSpace: "srgb"
438
+ });
439
+ } catch (error) {
440
+ if (error instanceof DOMException && error.name === "IndexSizeError") {
441
+ return null;
442
+ }
443
+ throw error;
444
+ }
445
+ })();
446
+ if (backgroundImage == null) {
447
+ throw new Error("GIF frame size is to large");
448
+ }
449
+ const {
450
+ r,
451
+ g,
452
+ b
453
+ } = gif.globalColorTable[backgroundColorIndex];
454
+ backgroundImage.data.set(globalColorTableFlag ? [r, g, b, 255] : [0, 0, 0, 0]);
455
+ for (let i = 4; i < backgroundImage.data.length; i *= 2) {
456
+ backgroundImage.data.copyWithin(i, 0, i);
457
+ }
458
+ gif.backgroundImage = backgroundImage;
459
+ let frameIndex = -1,
460
+ incrementFrameIndex = true,
461
+ transparencyIndex = -1;
462
+ const getframeIndex = increment => {
463
+ if (increment) {
464
+ incrementFrameIndex = true;
465
+ }
466
+ return frameIndex;
467
+ };
468
+ const getTransparencyIndex = newValue => {
469
+ if (newValue != null) {
470
+ transparencyIndex = newValue;
471
+ }
472
+ return transparencyIndex;
473
+ };
474
+ try {
475
+ do {
476
+ if (incrementFrameIndex) {
477
+ gif.frames.push({
478
+ left: 0,
479
+ top: 0,
480
+ width: 0,
481
+ height: 0,
482
+ disposalMethod: 0,
483
+ image: new ImageData(1, 1, {
484
+ colorSpace: "srgb"
485
+ }),
486
+ plainTextData: null,
487
+ userInputDelayFlag: false,
488
+ delayTime: 0,
489
+ sortFlag: false,
490
+ localColorTable: [],
491
+ reserved: 0,
492
+ GCreserved: 0
493
+ });
494
+ frameIndex++;
495
+ transparencyIndex = -1;
496
+ incrementFrameIndex = false;
497
+ }
498
+ } while (!(await parseBlock(byteStream, gif, avgAlpha, getframeIndex, getTransparencyIndex, progressCallback)));
499
+ gif.frames.length--;
500
+ for (const frame of gif.frames) {
501
+ if (frame.userInputDelayFlag && frame.delayTime === 0) {
502
+ gif.totalTime = Infinity;
503
+ break;
504
+ }
505
+ gif.totalTime += frame.delayTime;
506
+ }
507
+ return gif;
508
+ } catch (error) {
509
+ if (error instanceof EvalError) {
510
+ throw new Error(`error while parsing frame ${frameIndex} "${error.message}"`);
511
+ }
512
+ throw error;
513
+ }
514
+ }
99
515
  ;// CONCATENATED MODULE: ./dist/browser/Utils.js
100
516
 
517
+
101
518
  const currentColorRegex = /(#(?:[0-9a-f]{2}){2,4}|(#[0-9a-f]{3})|(rgb|hsl)a?\((-?\d+%?[,\s]+){2,3}\s*[\d.]+%?\))|currentcolor/gi;
102
519
  function replaceColorSvg(imageShape, color, opacity) {
103
520
  const {
@@ -126,12 +543,29 @@ async function loadImage(image) {
126
543
  image.element = undefined;
127
544
  image.error = true;
128
545
  image.loading = false;
129
- console.error(`Error tsParticles - loading image: ${image.source}`);
546
+ (0,engine_root_window_.getLogger)().error(`${engine_root_window_.errorPrefix} loading image: ${image.source}`);
130
547
  resolve();
131
548
  });
132
549
  img.src = image.source;
133
550
  });
134
551
  }
552
+ async function loadGifImage(image) {
553
+ if (image.type !== "gif") {
554
+ await loadImage(image);
555
+ return;
556
+ }
557
+ image.loading = true;
558
+ try {
559
+ image.gifData = await decodeGIF(image.source);
560
+ image.gifLoopCount = getGIFLoopAmount(image.gifData) ?? 0;
561
+ if (image.gifLoopCount === 0) {
562
+ image.gifLoopCount = Infinity;
563
+ }
564
+ } catch {
565
+ image.error = true;
566
+ }
567
+ image.loading = false;
568
+ }
135
569
  async function downloadSvgImage(image) {
136
570
  if (image.type !== "svg") {
137
571
  await loadImage(image);
@@ -140,25 +574,25 @@ async function downloadSvgImage(image) {
140
574
  image.loading = true;
141
575
  const response = await fetch(image.source);
142
576
  if (!response.ok) {
143
- console.error("Error tsParticles - Image not found");
577
+ (0,engine_root_window_.getLogger)().error(`${engine_root_window_.errorPrefix} Image not found`);
144
578
  image.error = true;
145
- }
146
- if (!image.error) {
579
+ } else {
147
580
  image.svgData = await response.text();
148
581
  }
149
582
  image.loading = false;
150
583
  }
151
584
  function replaceImageColor(image, imageData, color, particle) {
152
- var _a, _b, _c;
153
- const svgColoredData = replaceColorSvg(image, color, (_b = (_a = particle.opacity) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : 1),
585
+ const svgColoredData = replaceColorSvg(image, color, particle.opacity?.value ?? 1),
154
586
  imageRes = {
155
587
  color,
156
- data: Object.assign(Object.assign({}, image), {
588
+ gif: imageData.gif,
589
+ data: {
590
+ ...image,
157
591
  svgData: svgColoredData
158
- }),
592
+ },
159
593
  loaded: false,
160
594
  ratio: imageData.width / imageData.height,
161
- replaceColor: (_c = imageData.replaceColor) !== null && _c !== void 0 ? _c : imageData.replace_color,
595
+ replaceColor: imageData.replaceColor,
162
596
  source: imageData.src
163
597
  };
164
598
  return new Promise(resolve => {
@@ -176,10 +610,11 @@ function replaceImageColor(image, imageData, color, particle) {
176
610
  });
177
611
  img.addEventListener("error", async () => {
178
612
  domUrl.revokeObjectURL(url);
179
- const img2 = Object.assign(Object.assign({}, image), {
613
+ const img2 = {
614
+ ...image,
180
615
  error: false,
181
616
  loading: true
182
- });
617
+ };
183
618
  await loadImage(img2);
184
619
  imageRes.loaded = true;
185
620
  imageRes.element = img2.element;
@@ -190,75 +625,159 @@ function replaceImageColor(image, imageData, color, particle) {
190
625
  }
191
626
  ;// CONCATENATED MODULE: ./dist/browser/ImageDrawer.js
192
627
 
628
+
193
629
  class ImageDrawer {
194
- constructor() {
195
- this._images = [];
196
- }
197
- addImage(container, image) {
198
- const containerImages = this.getImages(container);
199
- containerImages === null || containerImages === void 0 ? void 0 : containerImages.images.push(image);
630
+ constructor(engine) {
631
+ this.loadImageShape = async imageShape => {
632
+ if (!this._engine.loadImage) {
633
+ throw new Error(`${engine_root_window_.errorPrefix} image shape not initialized`);
634
+ }
635
+ await this._engine.loadImage({
636
+ gif: imageShape.gif,
637
+ name: imageShape.name,
638
+ replaceColor: imageShape.replaceColor ?? false,
639
+ src: imageShape.src
640
+ });
641
+ };
642
+ this._engine = engine;
200
643
  }
201
- destroy() {
202
- this._images = [];
644
+ addImage(image) {
645
+ if (!this._engine.images) {
646
+ this._engine.images = [];
647
+ }
648
+ this._engine.images.push(image);
203
649
  }
204
- draw(context, particle, radius, opacity) {
205
- var _a;
650
+ draw(context, particle, radius, opacity, delta) {
206
651
  const image = particle.image,
207
- element = image === null || image === void 0 ? void 0 : image.element;
208
- if (!element) {
652
+ element = image?.element;
653
+ if (!image) {
209
654
  return;
210
655
  }
211
- const ratio = (_a = image === null || image === void 0 ? void 0 : image.ratio) !== null && _a !== void 0 ? _a : 1,
212
- pos = {
213
- x: -radius,
214
- y: -radius
215
- };
216
656
  context.globalAlpha = opacity;
217
- context.drawImage(element, pos.x, pos.y, radius * 2, radius * 2 / ratio);
218
- context.globalAlpha = 1;
219
- }
220
- getImages(container) {
221
- const containerImages = this._images.find(t => t.id === container.id);
222
- if (!containerImages) {
223
- this._images.push({
224
- id: container.id,
225
- images: []
226
- });
227
- return this.getImages(container);
228
- } else {
229
- return containerImages;
657
+ if (image.gif && image.gifData) {
658
+ const offscreenCanvas = new OffscreenCanvas(image.gifData.width, image.gifData.height),
659
+ offscreenContext = offscreenCanvas.getContext("2d");
660
+ if (!offscreenContext) {
661
+ throw new Error("could not create offscreen canvas context");
662
+ }
663
+ offscreenContext.imageSmoothingQuality = "low";
664
+ offscreenContext.imageSmoothingEnabled = false;
665
+ offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height);
666
+ if (particle.gifLoopCount === undefined) {
667
+ particle.gifLoopCount = image.gifLoopCount ?? 0;
668
+ }
669
+ let frameIndex = particle.gifFrame ?? 0;
670
+ const pos = {
671
+ x: -image.gifData.width * 0.5,
672
+ y: -image.gifData.height * 0.5
673
+ },
674
+ frame = image.gifData.frames[frameIndex];
675
+ if (particle.gifTime === undefined) {
676
+ particle.gifTime = 0;
677
+ }
678
+ if (!frame.bitmap) {
679
+ return;
680
+ }
681
+ context.scale(radius / image.gifData.width, radius / image.gifData.height);
682
+ switch (frame.disposalMethod) {
683
+ case 4:
684
+ case 5:
685
+ case 6:
686
+ case 7:
687
+ case 0:
688
+ offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
689
+ context.drawImage(offscreenCanvas, pos.x, pos.y);
690
+ offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height);
691
+ break;
692
+ case 1:
693
+ offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
694
+ context.drawImage(offscreenCanvas, pos.x, pos.y);
695
+ break;
696
+ case 2:
697
+ offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
698
+ context.drawImage(offscreenCanvas, pos.x, pos.y);
699
+ offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height);
700
+ if (image.gifData.globalColorTable.length === 0) {
701
+ offscreenContext.putImageData(image.gifData.frames[0].image, pos.x + frame.left, pos.y + frame.top);
702
+ } else {
703
+ offscreenContext.putImageData(image.gifData.backgroundImage, pos.x, pos.y);
704
+ }
705
+ break;
706
+ case 3:
707
+ {
708
+ const previousImageData = offscreenContext.getImageData(0, 0, offscreenCanvas.width, offscreenCanvas.height);
709
+ offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
710
+ context.drawImage(offscreenCanvas, pos.x, pos.y);
711
+ offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height);
712
+ offscreenContext.putImageData(previousImageData, 0, 0);
713
+ }
714
+ break;
715
+ }
716
+ particle.gifTime += delta.value;
717
+ if (particle.gifTime > frame.delayTime) {
718
+ particle.gifTime -= frame.delayTime;
719
+ if (++frameIndex >= image.gifData.frames.length) {
720
+ if (--particle.gifLoopCount <= 0) {
721
+ return;
722
+ }
723
+ frameIndex = 0;
724
+ offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height);
725
+ }
726
+ particle.gifFrame = frameIndex;
727
+ }
728
+ context.scale(image.gifData.width / radius, image.gifData.height / radius);
729
+ } else if (element) {
730
+ const ratio = image.ratio,
731
+ pos = {
732
+ x: -radius,
733
+ y: -radius
734
+ };
735
+ context.drawImage(element, pos.x, pos.y, radius * 2, radius * 2 / ratio);
230
736
  }
737
+ context.globalAlpha = 1;
231
738
  }
232
739
  getSidesCount() {
233
740
  return 12;
234
741
  }
742
+ async init(container) {
743
+ const options = container.actualOptions;
744
+ if (!options.preload || !this._engine.loadImage) {
745
+ return;
746
+ }
747
+ for (const imageData of options.preload) {
748
+ await this._engine.loadImage(imageData);
749
+ }
750
+ }
235
751
  loadShape(particle) {
236
752
  if (particle.shape !== "image" && particle.shape !== "images") {
237
753
  return;
238
754
  }
239
- const container = particle.container,
240
- images = this.getImages(container).images,
241
- imageData = particle.shapeData,
242
- image = images.find(t => t.source === imageData.src);
755
+ if (!this._engine.images) {
756
+ this._engine.images = [];
757
+ }
758
+ const imageData = particle.shapeData,
759
+ image = this._engine.images.find(t => t.name === imageData.name || t.source === imageData.src);
243
760
  if (!image) {
244
- this.loadImageShape(container, imageData).then(() => {
761
+ this.loadImageShape(imageData).then(() => {
245
762
  this.loadShape(particle);
246
763
  });
247
764
  }
248
765
  }
249
766
  particleInit(container, particle) {
250
- var _a;
251
767
  if (particle.shape !== "image" && particle.shape !== "images") {
252
768
  return;
253
769
  }
254
- const images = this.getImages(container).images,
770
+ if (!this._engine.images) {
771
+ this._engine.images = [];
772
+ }
773
+ const images = this._engine.images,
255
774
  imageData = particle.shapeData,
256
775
  color = particle.getFillColor(),
257
- replaceColor = (_a = imageData.replaceColor) !== null && _a !== void 0 ? _a : imageData.replace_color,
258
- image = images.find(t => t.source === imageData.src);
776
+ image = images.find(t => t.name === imageData.name || t.source === imageData.src);
259
777
  if (!image) {
260
778
  return;
261
779
  }
780
+ const replaceColor = imageData.replaceColor ?? image.replaceColor;
262
781
  if (image.loading) {
263
782
  setTimeout(() => {
264
783
  this.particleInit(container, particle);
@@ -266,7 +785,6 @@ class ImageDrawer {
266
785
  return;
267
786
  }
268
787
  (async () => {
269
- var _a, _b;
270
788
  let imageRes;
271
789
  if (image.svgData && color) {
272
790
  imageRes = await replaceImageColor(image, imageData, color, particle);
@@ -275,8 +793,11 @@ class ImageDrawer {
275
793
  color,
276
794
  data: image,
277
795
  element: image.element,
796
+ gif: image.gif,
797
+ gifData: image.gifData,
798
+ gifLoopCount: image.gifLoopCount,
278
799
  loaded: true,
279
- ratio: imageData.width / imageData.height,
800
+ ratio: imageData.width && imageData.height ? imageData.width / imageData.height : image.ratio ?? 1,
280
801
  replaceColor: replaceColor,
281
802
  source: imageData.src
282
803
  };
@@ -284,8 +805,8 @@ class ImageDrawer {
284
805
  if (!imageRes.ratio) {
285
806
  imageRes.ratio = 1;
286
807
  }
287
- const fill = (_a = imageData.fill) !== null && _a !== void 0 ? _a : particle.fill,
288
- close = (_b = imageData.close) !== null && _b !== void 0 ? _b : particle.close,
808
+ const fill = imageData.fill ?? particle.fill,
809
+ close = imageData.close ?? particle.close,
289
810
  imageShape = {
290
811
  image: imageRes,
291
812
  fill,
@@ -296,31 +817,113 @@ class ImageDrawer {
296
817
  particle.close = imageShape.close;
297
818
  })();
298
819
  }
299
- async loadImageShape(container, imageShape) {
300
- var _a;
301
- const source = imageShape.src;
302
- if (!source) {
303
- throw new Error("Error tsParticles - No image.src");
820
+ }
821
+ ;// CONCATENATED MODULE: ./dist/browser/Options/Classes/Preload.js
822
+ class Preload {
823
+ constructor() {
824
+ this.src = "";
825
+ this.gif = false;
826
+ }
827
+ load(data) {
828
+ if (!data) {
829
+ return;
830
+ }
831
+ if (data.gif !== undefined) {
832
+ this.gif = data.gif;
833
+ }
834
+ if (data.height !== undefined) {
835
+ this.height = data.height;
836
+ }
837
+ if (data.name !== undefined) {
838
+ this.name = data.name;
839
+ }
840
+ if (data.replaceColor !== undefined) {
841
+ this.replaceColor = data.replaceColor;
842
+ }
843
+ if (data.src !== undefined) {
844
+ this.src = data.src;
845
+ }
846
+ if (data.width !== undefined) {
847
+ this.width = data.width;
848
+ }
849
+ }
850
+ }
851
+ ;// CONCATENATED MODULE: ./dist/browser/ImagePreloader.js
852
+
853
+ class ImagePreloaderPlugin {
854
+ constructor(engine) {
855
+ this.id = "imagePreloader";
856
+ this._engine = engine;
857
+ }
858
+ getPlugin() {
859
+ return {};
860
+ }
861
+ loadOptions(options, source) {
862
+ if (!source || !source.preload) {
863
+ return;
864
+ }
865
+ if (!options.preload) {
866
+ options.preload = [];
867
+ }
868
+ const preloadOptions = options.preload;
869
+ for (const item of source.preload) {
870
+ const existing = preloadOptions.find(t => t.name === item.name || t.src === item.src);
871
+ if (existing) {
872
+ existing.load(item);
873
+ } else {
874
+ const preload = new Preload();
875
+ preload.load(item);
876
+ preloadOptions.push(preload);
877
+ }
878
+ }
879
+ }
880
+ needsPlugin() {
881
+ return true;
882
+ }
883
+ }
884
+ ;// CONCATENATED MODULE: ./dist/browser/index.js
885
+
886
+
887
+
888
+
889
+ function addLoadImageToEngine(engine) {
890
+ if (engine.loadImage) {
891
+ return;
892
+ }
893
+ engine.loadImage = async data => {
894
+ if (!data.name && !data.src) {
895
+ throw new Error(`${engine_root_window_.errorPrefix} no image source provided`);
896
+ }
897
+ if (!engine.images) {
898
+ engine.images = [];
899
+ }
900
+ if (engine.images.find(t => t.name === data.name || t.source === data.src)) {
901
+ return;
304
902
  }
305
903
  try {
306
904
  const image = {
307
- source: source,
308
- type: source.substring(source.length - 3),
905
+ gif: data.gif ?? false,
906
+ name: data.name ?? data.src,
907
+ source: data.src,
908
+ type: data.src.substring(data.src.length - 3),
309
909
  error: false,
310
- loading: true
910
+ loading: true,
911
+ replaceColor: data.replaceColor,
912
+ ratio: data.width && data.height ? data.width / data.height : undefined
311
913
  };
312
- this.addImage(container, image);
313
- const imageFunc = ((_a = imageShape.replaceColor) !== null && _a !== void 0 ? _a : imageShape.replace_color) ? downloadSvgImage : loadImage;
914
+ engine.images.push(image);
915
+ const imageFunc = data.gif ? loadGifImage : data.replaceColor ? downloadSvgImage : loadImage;
314
916
  await imageFunc(image);
315
- } catch (_b) {
316
- throw new Error(`tsParticles error - ${imageShape.src} not found`);
917
+ } catch {
918
+ throw new Error(`${engine_root_window_.errorPrefix} ${data.name ?? data.src} not found`);
317
919
  }
318
- }
920
+ };
319
921
  }
320
- ;// CONCATENATED MODULE: ./dist/browser/index.js
321
-
322
- async function loadImageShape(engine) {
323
- await engine.addShape(["image", "images"], new ImageDrawer());
922
+ async function loadImageShape(engine, refresh = true) {
923
+ addLoadImageToEngine(engine);
924
+ const preloader = new ImagePreloaderPlugin(engine);
925
+ await engine.addPlugin(preloader, refresh);
926
+ await engine.addShape(["image", "images"], new ImageDrawer(engine), refresh);
324
927
  }
325
928
  })();
326
929