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