@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
@@ -0,0 +1,339 @@
1
+ (function (factory) {
2
+ if (typeof module === "object" && typeof module.exports === "object") {
3
+ var v = factory(require, exports);
4
+ if (v !== undefined) module.exports = v;
5
+ }
6
+ else if (typeof define === "function" && define.amd) {
7
+ define(["require", "exports", "./Constants", "./ByteStream"], factory);
8
+ }
9
+ })(function (require, exports) {
10
+ "use strict";
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.decodeGIF = exports.getGIFLoopAmount = void 0;
13
+ const Constants_1 = require("./Constants");
14
+ const ByteStream_1 = require("./ByteStream");
15
+ function parseColorTable(byteStream, count) {
16
+ const colors = [];
17
+ for (let i = 0; i < count; i++) {
18
+ colors.push({
19
+ r: byteStream.data[byteStream.pos],
20
+ g: byteStream.data[byteStream.pos + 1],
21
+ b: byteStream.data[byteStream.pos + 2],
22
+ });
23
+ byteStream.pos += 3;
24
+ }
25
+ return colors;
26
+ }
27
+ async function parseExtensionBlock(byteStream, gif, getFrameIndex, getTransparencyIndex) {
28
+ switch (byteStream.nextByte()) {
29
+ case 249: {
30
+ const frame = gif.frames[getFrameIndex(false)];
31
+ byteStream.pos++;
32
+ const packedByte = byteStream.nextByte();
33
+ frame.GCreserved = (packedByte & 0xe0) >>> 5;
34
+ frame.disposalMethod = (packedByte & 0x1c) >>> 2;
35
+ frame.userInputDelayFlag = (packedByte & 2) === 2;
36
+ const transparencyFlag = (packedByte & 1) === 1;
37
+ frame.delayTime = byteStream.nextTwoBytes() * 0xa;
38
+ const transparencyIndex = byteStream.nextByte();
39
+ if (transparencyFlag) {
40
+ getTransparencyIndex(transparencyIndex);
41
+ }
42
+ byteStream.pos++;
43
+ break;
44
+ }
45
+ case 255: {
46
+ byteStream.pos++;
47
+ const applicationExtension = {
48
+ identifier: byteStream.getString(8),
49
+ authenticationCode: byteStream.getString(3),
50
+ data: byteStream.readSubBlocksBin(),
51
+ };
52
+ gif.applicationExtensions.push(applicationExtension);
53
+ break;
54
+ }
55
+ case 254: {
56
+ gif.comments.push([getFrameIndex(false), byteStream.readSubBlocks()]);
57
+ break;
58
+ }
59
+ case 1: {
60
+ if (gif.globalColorTable.length === 0) {
61
+ throw new EvalError("plain text extension without global color table");
62
+ }
63
+ byteStream.pos++;
64
+ gif.frames[getFrameIndex(false)].plainTextData = {
65
+ left: byteStream.nextTwoBytes(),
66
+ top: byteStream.nextTwoBytes(),
67
+ width: byteStream.nextTwoBytes(),
68
+ height: byteStream.nextTwoBytes(),
69
+ charSize: {
70
+ width: byteStream.nextTwoBytes(),
71
+ height: byteStream.nextTwoBytes(),
72
+ },
73
+ foregroundColor: byteStream.nextByte(),
74
+ backgroundColor: byteStream.nextByte(),
75
+ text: byteStream.readSubBlocks(),
76
+ };
77
+ break;
78
+ }
79
+ default:
80
+ byteStream.skipSubBlocks();
81
+ break;
82
+ }
83
+ }
84
+ async function parseImageBlock(byteStream, gif, avgAlpha, getFrameIndex, getTransparencyIndex, progressCallback) {
85
+ const frame = gif.frames[getFrameIndex(true)];
86
+ frame.left = byteStream.nextTwoBytes();
87
+ frame.top = byteStream.nextTwoBytes();
88
+ frame.width = byteStream.nextTwoBytes();
89
+ frame.height = byteStream.nextTwoBytes();
90
+ const packedByte = byteStream.nextByte(), localColorTableFlag = (packedByte & 0x80) === 0x80, interlacedFlag = (packedByte & 0x40) === 0x40;
91
+ frame.sortFlag = (packedByte & 0x20) === 0x20;
92
+ frame.reserved = (packedByte & 0x18) >>> 3;
93
+ const localColorCount = 1 << ((packedByte & 7) + 1);
94
+ if (localColorTableFlag) {
95
+ frame.localColorTable = parseColorTable(byteStream, localColorCount);
96
+ }
97
+ const getColor = (index) => {
98
+ const { r, g, b } = (localColorTableFlag ? frame.localColorTable : gif.globalColorTable)[index];
99
+ return { r, g, b, a: index === getTransparencyIndex(null) ? (avgAlpha ? ~~((r + g + b) / 3) : 0) : 255 };
100
+ };
101
+ const image = (() => {
102
+ try {
103
+ return new ImageData(frame.width, frame.height, { colorSpace: "srgb" });
104
+ }
105
+ catch (error) {
106
+ if (error instanceof DOMException && error.name === "IndexSizeError") {
107
+ return null;
108
+ }
109
+ throw error;
110
+ }
111
+ })();
112
+ if (image == null) {
113
+ throw new EvalError("GIF frame size is to large");
114
+ }
115
+ const minCodeSize = byteStream.nextByte(), imageData = byteStream.readSubBlocksBin(), clearCode = 1 << minCodeSize;
116
+ const readBits = (pos, len) => {
117
+ const bytePos = pos >>> 3, bitPos = pos & 7;
118
+ return (((imageData[bytePos] + (imageData[bytePos + 1] << 8) + (imageData[bytePos + 2] << 16)) &
119
+ (((1 << len) - 1) << bitPos)) >>>
120
+ bitPos);
121
+ };
122
+ if (interlacedFlag) {
123
+ for (let code = 0, size = minCodeSize + 1, pos = 0, dic = [[0]], pass = 0; pass < 4; pass++) {
124
+ if (Constants_1.InterlaceOffsets[pass] < frame.height) {
125
+ for (let pixelPos = 0, lineIndex = 0;;) {
126
+ const last = code;
127
+ code = readBits(pos, size);
128
+ pos += size + 1;
129
+ if (code === clearCode) {
130
+ size = minCodeSize + 1;
131
+ dic.length = clearCode + 2;
132
+ for (let i = 0; i < dic.length; i++) {
133
+ dic[i] = i < clearCode ? [i] : [];
134
+ }
135
+ }
136
+ else {
137
+ if (code >= dic.length) {
138
+ dic.push(dic[last].concat(dic[last][0]));
139
+ }
140
+ else if (last !== clearCode) {
141
+ dic.push(dic[last].concat(dic[code][0]));
142
+ }
143
+ for (let i = 0; i < dic[code].length; i++) {
144
+ const { r, g, b, a } = getColor(dic[code][i]);
145
+ image.data.set([r, g, b, a], Constants_1.InterlaceOffsets[pass] * frame.width +
146
+ Constants_1.InterlaceSteps[pass] * lineIndex +
147
+ (pixelPos % (frame.width * 4)));
148
+ pixelPos += 4;
149
+ }
150
+ if (dic.length === 1 << size && size < 0xc) {
151
+ size++;
152
+ }
153
+ }
154
+ if (pixelPos === frame.width * 4 * (lineIndex + 1)) {
155
+ lineIndex++;
156
+ if (Constants_1.InterlaceOffsets[pass] + Constants_1.InterlaceSteps[pass] * lineIndex >= frame.height) {
157
+ break;
158
+ }
159
+ }
160
+ }
161
+ }
162
+ progressCallback?.(byteStream.pos / (byteStream.data.length - 1), getFrameIndex(false) + 1, image, { x: frame.left, y: frame.top }, { width: gif.width, height: gif.height });
163
+ }
164
+ frame.image = image;
165
+ frame.bitmap = await createImageBitmap(image);
166
+ }
167
+ else {
168
+ for (let code = 0, size = minCodeSize + 1, pos = 0, dic = [[0]], pixelPos = -4;;) {
169
+ const last = code;
170
+ code = readBits(pos, size);
171
+ pos += size;
172
+ if (code === clearCode) {
173
+ size = minCodeSize + 1;
174
+ dic.length = clearCode + 2;
175
+ for (let i = 0; i < dic.length; i++) {
176
+ dic[i] = i < clearCode ? [i] : [];
177
+ }
178
+ }
179
+ else {
180
+ if (code === clearCode + 1) {
181
+ break;
182
+ }
183
+ if (code >= dic.length) {
184
+ dic.push(dic[last].concat(dic[last][0]));
185
+ }
186
+ else if (last !== clearCode) {
187
+ dic.push(dic[last].concat(dic[code][0]));
188
+ }
189
+ for (let i = 0; i < dic[code].length; i++) {
190
+ const { r, g, b, a } = getColor(dic[code][i]);
191
+ image.data.set([r, g, b, a], (pixelPos += 4));
192
+ }
193
+ if (dic.length >= 1 << size && size < 0xc) {
194
+ size++;
195
+ }
196
+ }
197
+ }
198
+ frame.image = image;
199
+ frame.bitmap = await createImageBitmap(image);
200
+ progressCallback?.((byteStream.pos + 1) / byteStream.data.length, getFrameIndex(false) + 1, frame.image, { x: frame.left, y: frame.top }, { width: gif.width, height: gif.height });
201
+ }
202
+ }
203
+ async function parseBlock(byteStream, gif, avgAlpha, getFrameIndex, getTransparencyIndex, progressCallback) {
204
+ switch (byteStream.nextByte()) {
205
+ case 59:
206
+ return true;
207
+ case 44:
208
+ await parseImageBlock(byteStream, gif, avgAlpha, getFrameIndex, getTransparencyIndex, progressCallback);
209
+ break;
210
+ case 33:
211
+ await parseExtensionBlock(byteStream, gif, getFrameIndex, getTransparencyIndex);
212
+ break;
213
+ default:
214
+ throw new EvalError("undefined block found");
215
+ }
216
+ return false;
217
+ }
218
+ function getGIFLoopAmount(gif) {
219
+ for (const extension of gif.applicationExtensions) {
220
+ if (extension.identifier + extension.authenticationCode !== "NETSCAPE2.0") {
221
+ continue;
222
+ }
223
+ return extension.data[1] + (extension.data[2] << 8);
224
+ }
225
+ return NaN;
226
+ }
227
+ exports.getGIFLoopAmount = getGIFLoopAmount;
228
+ async function decodeGIF(gifURL, progressCallback, avgAlpha) {
229
+ if (!avgAlpha)
230
+ avgAlpha = false;
231
+ const res = await fetch(gifURL);
232
+ if (!res.ok && res.status === 404) {
233
+ throw new EvalError("file not found");
234
+ }
235
+ const buffer = await res.arrayBuffer();
236
+ const gif = {
237
+ width: 0,
238
+ height: 0,
239
+ totalTime: 0,
240
+ colorRes: 0,
241
+ pixelAspectRatio: 0,
242
+ frames: [],
243
+ sortFlag: false,
244
+ globalColorTable: [],
245
+ backgroundImage: new ImageData(1, 1, { colorSpace: "srgb" }),
246
+ comments: [],
247
+ applicationExtensions: [],
248
+ }, byteStream = new ByteStream_1.ByteStream(new Uint8ClampedArray(buffer));
249
+ if (byteStream.getString(6) !== "GIF89a") {
250
+ throw new Error("not a supported GIF file");
251
+ }
252
+ gif.width = byteStream.nextTwoBytes();
253
+ gif.height = byteStream.nextTwoBytes();
254
+ const packedByte = byteStream.nextByte(), globalColorTableFlag = (packedByte & 0x80) === 0x80;
255
+ gif.colorRes = (packedByte & 0x70) >>> 4;
256
+ gif.sortFlag = (packedByte & 8) === 8;
257
+ const globalColorCount = 1 << ((packedByte & 7) + 1), backgroundColorIndex = byteStream.nextByte();
258
+ gif.pixelAspectRatio = byteStream.nextByte();
259
+ if (gif.pixelAspectRatio !== 0) {
260
+ gif.pixelAspectRatio = (gif.pixelAspectRatio + 0xf) / 0x40;
261
+ }
262
+ if (globalColorTableFlag) {
263
+ gif.globalColorTable = parseColorTable(byteStream, globalColorCount);
264
+ }
265
+ const backgroundImage = (() => {
266
+ try {
267
+ return new ImageData(gif.width, gif.height, { colorSpace: "srgb" });
268
+ }
269
+ catch (error) {
270
+ if (error instanceof DOMException && error.name === "IndexSizeError") {
271
+ return null;
272
+ }
273
+ throw error;
274
+ }
275
+ })();
276
+ if (backgroundImage == null) {
277
+ throw new Error("GIF frame size is to large");
278
+ }
279
+ const { r, g, b } = gif.globalColorTable[backgroundColorIndex];
280
+ backgroundImage.data.set(globalColorTableFlag ? [r, g, b, 255] : [0, 0, 0, 0]);
281
+ for (let i = 4; i < backgroundImage.data.length; i *= 2) {
282
+ backgroundImage.data.copyWithin(i, 0, i);
283
+ }
284
+ gif.backgroundImage = backgroundImage;
285
+ let frameIndex = -1, incrementFrameIndex = true, transparencyIndex = -1;
286
+ const getframeIndex = (increment) => {
287
+ if (increment) {
288
+ incrementFrameIndex = true;
289
+ }
290
+ return frameIndex;
291
+ };
292
+ const getTransparencyIndex = (newValue) => {
293
+ if (newValue != null) {
294
+ transparencyIndex = newValue;
295
+ }
296
+ return transparencyIndex;
297
+ };
298
+ try {
299
+ do {
300
+ if (incrementFrameIndex) {
301
+ gif.frames.push({
302
+ left: 0,
303
+ top: 0,
304
+ width: 0,
305
+ height: 0,
306
+ disposalMethod: 0,
307
+ image: new ImageData(1, 1, { colorSpace: "srgb" }),
308
+ plainTextData: null,
309
+ userInputDelayFlag: false,
310
+ delayTime: 0,
311
+ sortFlag: false,
312
+ localColorTable: [],
313
+ reserved: 0,
314
+ GCreserved: 0,
315
+ });
316
+ frameIndex++;
317
+ transparencyIndex = -1;
318
+ incrementFrameIndex = false;
319
+ }
320
+ } while (!(await parseBlock(byteStream, gif, avgAlpha, getframeIndex, getTransparencyIndex, progressCallback)));
321
+ gif.frames.length--;
322
+ for (const frame of gif.frames) {
323
+ if (frame.userInputDelayFlag && frame.delayTime === 0) {
324
+ gif.totalTime = Infinity;
325
+ break;
326
+ }
327
+ gif.totalTime += frame.delayTime;
328
+ }
329
+ return gif;
330
+ }
331
+ catch (error) {
332
+ if (error instanceof EvalError) {
333
+ throw new Error(`error while parsing frame ${frameIndex} "${error.message}"`);
334
+ }
335
+ throw error;
336
+ }
337
+ }
338
+ exports.decodeGIF = decodeGIF;
339
+ });
@@ -4,74 +4,157 @@
4
4
  if (v !== undefined) module.exports = v;
5
5
  }
6
6
  else if (typeof define === "function" && define.amd) {
7
- define(["require", "exports", "./Utils"], factory);
7
+ define(["require", "exports", "@tsparticles/engine", "./Utils"], factory);
8
8
  }
9
9
  })(function (require, exports) {
10
10
  "use strict";
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.ImageDrawer = void 0;
13
+ const engine_1 = require("@tsparticles/engine");
13
14
  const Utils_1 = require("./Utils");
14
15
  class ImageDrawer {
15
- constructor() {
16
- this._images = [];
17
- }
18
- addImage(container, image) {
19
- const containerImages = this.getImages(container);
20
- containerImages === null || containerImages === void 0 ? void 0 : containerImages.images.push(image);
16
+ constructor(engine) {
17
+ this.loadImageShape = async (imageShape) => {
18
+ if (!this._engine.loadImage) {
19
+ throw new Error(`${engine_1.errorPrefix} image shape not initialized`);
20
+ }
21
+ await this._engine.loadImage({
22
+ gif: imageShape.gif,
23
+ name: imageShape.name,
24
+ replaceColor: imageShape.replaceColor ?? false,
25
+ src: imageShape.src,
26
+ });
27
+ };
28
+ this._engine = engine;
21
29
  }
22
- destroy() {
23
- this._images = [];
30
+ addImage(image) {
31
+ if (!this._engine.images) {
32
+ this._engine.images = [];
33
+ }
34
+ this._engine.images.push(image);
24
35
  }
25
- draw(context, particle, radius, opacity) {
26
- var _a;
27
- const image = particle.image, element = image === null || image === void 0 ? void 0 : image.element;
28
- if (!element) {
36
+ draw(context, particle, radius, opacity, delta) {
37
+ const image = particle.image, element = image?.element;
38
+ if (!image) {
29
39
  return;
30
40
  }
31
- const ratio = (_a = image === null || image === void 0 ? void 0 : image.ratio) !== null && _a !== void 0 ? _a : 1, pos = {
32
- x: -radius,
33
- y: -radius,
34
- };
35
41
  context.globalAlpha = opacity;
36
- context.drawImage(element, pos.x, pos.y, radius * 2, (radius * 2) / ratio);
37
- context.globalAlpha = 1;
38
- }
39
- getImages(container) {
40
- const containerImages = this._images.find((t) => t.id === container.id);
41
- if (!containerImages) {
42
- this._images.push({
43
- id: container.id,
44
- images: [],
45
- });
46
- return this.getImages(container);
42
+ if (image.gif && image.gifData) {
43
+ const offscreenCanvas = new OffscreenCanvas(image.gifData.width, image.gifData.height), offscreenContext = offscreenCanvas.getContext("2d");
44
+ if (!offscreenContext) {
45
+ throw new Error("could not create offscreen canvas context");
46
+ }
47
+ offscreenContext.imageSmoothingQuality = "low";
48
+ offscreenContext.imageSmoothingEnabled = false;
49
+ offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height);
50
+ if (particle.gifLoopCount === undefined) {
51
+ particle.gifLoopCount = image.gifLoopCount ?? 0;
52
+ }
53
+ let frameIndex = particle.gifFrame ?? 0;
54
+ const pos = { x: -image.gifData.width * 0.5, y: -image.gifData.height * 0.5 }, frame = image.gifData.frames[frameIndex];
55
+ if (particle.gifTime === undefined) {
56
+ particle.gifTime = 0;
57
+ }
58
+ if (!frame.bitmap) {
59
+ return;
60
+ }
61
+ context.scale(radius / image.gifData.width, radius / image.gifData.height);
62
+ switch (frame.disposalMethod) {
63
+ case 4:
64
+ case 5:
65
+ case 6:
66
+ case 7:
67
+ case 0:
68
+ offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
69
+ context.drawImage(offscreenCanvas, pos.x, pos.y);
70
+ offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height);
71
+ break;
72
+ case 1:
73
+ offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
74
+ context.drawImage(offscreenCanvas, pos.x, pos.y);
75
+ break;
76
+ case 2:
77
+ offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
78
+ context.drawImage(offscreenCanvas, pos.x, pos.y);
79
+ offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height);
80
+ if (image.gifData.globalColorTable.length === 0) {
81
+ offscreenContext.putImageData(image.gifData.frames[0].image, pos.x + frame.left, pos.y + frame.top);
82
+ }
83
+ else {
84
+ offscreenContext.putImageData(image.gifData.backgroundImage, pos.x, pos.y);
85
+ }
86
+ break;
87
+ case 3:
88
+ {
89
+ const previousImageData = offscreenContext.getImageData(0, 0, offscreenCanvas.width, offscreenCanvas.height);
90
+ offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
91
+ context.drawImage(offscreenCanvas, pos.x, pos.y);
92
+ offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height);
93
+ offscreenContext.putImageData(previousImageData, 0, 0);
94
+ }
95
+ break;
96
+ }
97
+ particle.gifTime += delta.value;
98
+ if (particle.gifTime > frame.delayTime) {
99
+ particle.gifTime -= frame.delayTime;
100
+ if (++frameIndex >= image.gifData.frames.length) {
101
+ if (--particle.gifLoopCount <= 0) {
102
+ return;
103
+ }
104
+ frameIndex = 0;
105
+ offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height);
106
+ }
107
+ particle.gifFrame = frameIndex;
108
+ }
109
+ context.scale(image.gifData.width / radius, image.gifData.height / radius);
47
110
  }
48
- else {
49
- return containerImages;
111
+ else if (element) {
112
+ const ratio = image.ratio, pos = {
113
+ x: -radius,
114
+ y: -radius,
115
+ };
116
+ context.drawImage(element, pos.x, pos.y, radius * 2, (radius * 2) / ratio);
50
117
  }
118
+ context.globalAlpha = 1;
51
119
  }
52
120
  getSidesCount() {
53
121
  return 12;
54
122
  }
123
+ async init(container) {
124
+ const options = container.actualOptions;
125
+ if (!options.preload || !this._engine.loadImage) {
126
+ return;
127
+ }
128
+ for (const imageData of options.preload) {
129
+ await this._engine.loadImage(imageData);
130
+ }
131
+ }
55
132
  loadShape(particle) {
56
133
  if (particle.shape !== "image" && particle.shape !== "images") {
57
134
  return;
58
135
  }
59
- const container = particle.container, images = this.getImages(container).images, imageData = particle.shapeData, image = images.find((t) => t.source === imageData.src);
136
+ if (!this._engine.images) {
137
+ this._engine.images = [];
138
+ }
139
+ const imageData = particle.shapeData, image = this._engine.images.find((t) => t.name === imageData.name || t.source === imageData.src);
60
140
  if (!image) {
61
- this.loadImageShape(container, imageData).then(() => {
141
+ this.loadImageShape(imageData).then(() => {
62
142
  this.loadShape(particle);
63
143
  });
64
144
  }
65
145
  }
66
146
  particleInit(container, particle) {
67
- var _a;
68
147
  if (particle.shape !== "image" && particle.shape !== "images") {
69
148
  return;
70
149
  }
71
- const images = this.getImages(container).images, imageData = particle.shapeData, color = particle.getFillColor(), replaceColor = (_a = imageData.replaceColor) !== null && _a !== void 0 ? _a : imageData.replace_color, image = images.find((t) => t.source === imageData.src);
150
+ if (!this._engine.images) {
151
+ this._engine.images = [];
152
+ }
153
+ const images = this._engine.images, imageData = particle.shapeData, color = particle.getFillColor(), image = images.find((t) => t.name === imageData.name || t.source === imageData.src);
72
154
  if (!image) {
73
155
  return;
74
156
  }
157
+ const replaceColor = imageData.replaceColor ?? image.replaceColor;
75
158
  if (image.loading) {
76
159
  setTimeout(() => {
77
160
  this.particleInit(container, particle);
@@ -79,7 +162,6 @@
79
162
  return;
80
163
  }
81
164
  (async () => {
82
- var _a, _b;
83
165
  let imageRes;
84
166
  if (image.svgData && color) {
85
167
  imageRes = await (0, Utils_1.replaceImageColor)(image, imageData, color, particle);
@@ -89,8 +171,11 @@
89
171
  color,
90
172
  data: image,
91
173
  element: image.element,
174
+ gif: image.gif,
175
+ gifData: image.gifData,
176
+ gifLoopCount: image.gifLoopCount,
92
177
  loaded: true,
93
- ratio: imageData.width / imageData.height,
178
+ ratio: imageData.width && imageData.height ? imageData.width / imageData.height : image.ratio ?? 1,
94
179
  replaceColor: replaceColor,
95
180
  source: imageData.src,
96
181
  };
@@ -98,7 +183,7 @@
98
183
  if (!imageRes.ratio) {
99
184
  imageRes.ratio = 1;
100
185
  }
101
- const fill = (_a = imageData.fill) !== null && _a !== void 0 ? _a : particle.fill, close = (_b = imageData.close) !== null && _b !== void 0 ? _b : particle.close, imageShape = {
186
+ const fill = imageData.fill ?? particle.fill, close = imageData.close ?? particle.close, imageShape = {
102
187
  image: imageRes,
103
188
  fill,
104
189
  close,
@@ -108,27 +193,6 @@
108
193
  particle.close = imageShape.close;
109
194
  })();
110
195
  }
111
- async loadImageShape(container, imageShape) {
112
- var _a;
113
- const source = imageShape.src;
114
- if (!source) {
115
- throw new Error("Error tsParticles - No image.src");
116
- }
117
- try {
118
- const image = {
119
- source: source,
120
- type: source.substring(source.length - 3),
121
- error: false,
122
- loading: true,
123
- };
124
- this.addImage(container, image);
125
- const imageFunc = ((_a = imageShape.replaceColor) !== null && _a !== void 0 ? _a : imageShape.replace_color) ? Utils_1.downloadSvgImage : Utils_1.loadImage;
126
- await imageFunc(image);
127
- }
128
- catch (_b) {
129
- throw new Error(`tsParticles error - ${imageShape.src} not found`);
130
- }
131
- }
132
196
  }
133
197
  exports.ImageDrawer = ImageDrawer;
134
198
  });
@@ -0,0 +1,47 @@
1
+ (function (factory) {
2
+ if (typeof module === "object" && typeof module.exports === "object") {
3
+ var v = factory(require, exports);
4
+ if (v !== undefined) module.exports = v;
5
+ }
6
+ else if (typeof define === "function" && define.amd) {
7
+ define(["require", "exports", "./Options/Classes/Preload"], factory);
8
+ }
9
+ })(function (require, exports) {
10
+ "use strict";
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ImagePreloaderPlugin = void 0;
13
+ const Preload_1 = require("./Options/Classes/Preload");
14
+ class ImagePreloaderPlugin {
15
+ constructor(engine) {
16
+ this.id = "imagePreloader";
17
+ this._engine = engine;
18
+ }
19
+ getPlugin() {
20
+ return {};
21
+ }
22
+ loadOptions(options, source) {
23
+ if (!source || !source.preload) {
24
+ return;
25
+ }
26
+ if (!options.preload) {
27
+ options.preload = [];
28
+ }
29
+ const preloadOptions = options.preload;
30
+ for (const item of source.preload) {
31
+ const existing = preloadOptions.find((t) => t.name === item.name || t.src === item.src);
32
+ if (existing) {
33
+ existing.load(item);
34
+ }
35
+ else {
36
+ const preload = new Preload_1.Preload();
37
+ preload.load(item);
38
+ preloadOptions.push(preload);
39
+ }
40
+ }
41
+ }
42
+ needsPlugin() {
43
+ return true;
44
+ }
45
+ }
46
+ exports.ImagePreloaderPlugin = ImagePreloaderPlugin;
47
+ });
@@ -0,0 +1,43 @@
1
+ (function (factory) {
2
+ if (typeof module === "object" && typeof module.exports === "object") {
3
+ var v = factory(require, exports);
4
+ if (v !== undefined) module.exports = v;
5
+ }
6
+ else if (typeof define === "function" && define.amd) {
7
+ define(["require", "exports"], factory);
8
+ }
9
+ })(function (require, exports) {
10
+ "use strict";
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.Preload = void 0;
13
+ class Preload {
14
+ constructor() {
15
+ this.src = "";
16
+ this.gif = false;
17
+ }
18
+ load(data) {
19
+ if (!data) {
20
+ return;
21
+ }
22
+ if (data.gif !== undefined) {
23
+ this.gif = data.gif;
24
+ }
25
+ if (data.height !== undefined) {
26
+ this.height = data.height;
27
+ }
28
+ if (data.name !== undefined) {
29
+ this.name = data.name;
30
+ }
31
+ if (data.replaceColor !== undefined) {
32
+ this.replaceColor = data.replaceColor;
33
+ }
34
+ if (data.src !== undefined) {
35
+ this.src = data.src;
36
+ }
37
+ if (data.width !== undefined) {
38
+ this.width = data.width;
39
+ }
40
+ }
41
+ }
42
+ exports.Preload = Preload;
43
+ });