@tsparticles/shape-image 3.1.0 → 3.2.1
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.
- package/21.min.js +2 -0
- package/21.min.js.LICENSE.txt +1 -0
- package/618.min.js +2 -0
- package/618.min.js.LICENSE.txt +1 -0
- package/623.min.js +2 -0
- package/623.min.js.LICENSE.txt +1 -0
- package/browser/GifUtils/ByteStream.js +2 -1
- package/browser/GifUtils/Utils.js +96 -0
- package/browser/ImageDrawer.js +42 -113
- package/browser/ImagePreloader.js +2 -1
- package/browser/Utils.js +1 -20
- package/browser/index.js +3 -3
- package/cjs/GifUtils/ByteStream.js +2 -1
- package/cjs/GifUtils/Utils.js +122 -1
- package/cjs/ImageDrawer.js +64 -112
- package/cjs/ImagePreloader.js +2 -1
- package/cjs/Utils.js +2 -22
- package/cjs/index.js +28 -5
- package/dist_browser_GifUtils_Utils_js.js +50 -0
- package/dist_browser_ImageDrawer_js.js +30 -0
- package/dist_browser_ImagePreloader_js.js +40 -0
- package/esm/GifUtils/ByteStream.js +2 -1
- package/esm/GifUtils/Utils.js +96 -0
- package/esm/ImageDrawer.js +42 -113
- package/esm/ImagePreloader.js +2 -1
- package/esm/Utils.js +1 -20
- package/esm/index.js +3 -3
- package/package.json +2 -2
- package/report.html +3 -3
- package/tsparticles.shape.image.js +251 -910
- package/tsparticles.shape.image.min.js +1 -1
- package/tsparticles.shape.image.min.js.LICENSE.txt +1 -1
- package/types/GifUtils/Utils.d.ts +4 -0
- package/types/ImageDrawer.d.ts +4 -4
- package/types/ImagePreloader.d.ts +1 -1
- package/types/Utils.d.ts +0 -1
- package/umd/GifUtils/ByteStream.js +2 -1
- package/umd/GifUtils/Utils.js +123 -1
- package/umd/ImageDrawer.js +66 -113
- package/umd/ImagePreloader.js +2 -1
- package/umd/Utils.js +3 -23
- package/umd/index.js +30 -6
package/21.min.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! For license information please see 21.min.js.LICENSE.txt */
|
|
2
|
+
(this.webpackChunk_tsparticles_shape_image=this.webpackChunk_tsparticles_shape_image||[]).push([[21],{21:(e,a,i)=>{i.d(a,{ImageDrawer:()=>s});var t=i(533);class s{constructor(e){this.loadImageShape=async e=>{if(!this._engine.loadImage)throw new Error(`${t.errorPrefix} image shape not initialized`);await this._engine.loadImage({gif:e.gif,name:e.name,replaceColor:e.replaceColor??!1,src:e.src})},this._engine=e}addImage(e){this._engine.images||(this._engine.images=[]),this._engine.images.push(e)}async draw(e){const{context:a,radius:t,particle:s,opacity:n}=e,o=s.image,r=o?.element;if(o){if(a.globalAlpha=n,o.gif&&o.gifData){const{drawGif:a}=await i.e(618).then(i.bind(i,618));a(e)}else if(r){const e=o.ratio,i={x:-t,y:-t},s=2*t;a.drawImage(r,i.x,i.y,s,s/e)}a.globalAlpha=1}}getSidesCount(){return 12}async init(e){const a=e.actualOptions;if(a.preload&&this._engine.loadImage)for(const e of a.preload)await this._engine.loadImage(e)}async loadShape(e){if("image"!==e.shape&&"images"!==e.shape)return;this._engine.images||(this._engine.images=[]);const a=e.shapeData;if(!a)return;this._engine.images.find((e=>e.name===a.name||e.source===a.src))||(await this.loadImageShape(a),await this.loadShape(e))}async particleInit(e,a){if("image"!==a.shape&&"images"!==a.shape)return;this._engine.images||(this._engine.images=[]);const t=this._engine.images,s=a.shapeData;if(!s)return;const n=a.getFillColor(),o=t.find((e=>e.name===s.name||e.source===s.src));if(!o)return;const r=s.replaceColor??o.replaceColor;if(o.loading)return void setTimeout((()=>{this.particleInit(e,a)}));let l;if(o.svgData&&n){const{replaceImageColor:e}=await Promise.resolve().then(i.bind(i,103));l=await e(o,s,n,a)}else l={color:n,data:o,element:o.element,gif:o.gif,gifData:o.gifData,gifLoopCount:o.gifLoopCount,loaded:!0,ratio:s.width&&s.height?s.width/s.height:o.ratio??1,replaceColor:r,source:s.src};l.ratio||(l.ratio=1);const g={image:l,fill:s.fill??a.shapeFill,close:s.close??a.shapeClose};a.image=g.image,a.shapeFill=g.fill,a.shapeClose=g.close}}}}]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/*! tsParticles Image Shape v3.2.1 by Matteo Bruni */
|
package/618.min.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! For license information please see 618.min.js.LICENSE.txt */
|
|
2
|
+
(this.webpackChunk_tsparticles_shape_image=this.webpackChunk_tsparticles_shape_image||[]).push([[618],{618:(t,e,a)=>{a.d(e,{drawGif:()=>p,loadGifImage:()=>w});const o=[0,4,2,1],i=[8,8,4,2];class r{constructor(t){this.pos=0,this.data=new Uint8ClampedArray(t)}getString(t){const e=this.data.slice(this.pos,this.pos+t);return this.pos+=e.length,e.reduce(((t,e)=>t+String.fromCharCode(e)),"")}nextByte(){return this.data[this.pos++]}nextTwoBytes(){return this.pos+=2,this.data[this.pos-2]+(this.data[this.pos-1]<<8)}readSubBlocks(){let t="",e=0;do{e=this.data[this.pos++];for(let a=e;--a>=0;t+=String.fromCharCode(this.data[this.pos++]));}while(0!==e);return t}readSubBlocksBin(){let t=this.data[this.pos],e=0;for(let a=0;0!==t;a+=t+1,t=this.data[this.pos+a])e+=t;const a=new Uint8Array(e);t=this.data[this.pos++];for(let e=0;0!==t;t=this.data[this.pos++])for(let o=t;--o>=0;a[e++]=this.data[this.pos++]);return a}skipSubBlocks(){for(const t=1,e=0;this.data[this.pos]!==e;this.pos+=this.data[this.pos]+t);this.pos++}}const s={x:0,y:0},n=0,l=.5,h=0,c=0,g=0;function f(t,e){const a=[];for(let o=0;o<e;o++)a.push({r:t.data[t.pos],g:t.data[t.pos+1],b:t.data[t.pos+2]}),t.pos+=3;return a}async function d(t,e,a,r,s,n){switch(t.nextByte()){case 59:return!0;case 44:await async function(t,e,a,r,s,n){const l=e.frames[r(!0)];l.left=t.nextTwoBytes(),l.top=t.nextTwoBytes(),l.width=t.nextTwoBytes(),l.height=t.nextTwoBytes();const h=t.nextByte(),c=128==(128&h),g=64==(64&h);l.sortFlag=32==(32&h),l.reserved=(24&h)>>>3;const d=1<<1+(7&h);c&&(l.localColorTable=f(t,d));const p=t=>{const{r:o,g:i,b:r}=(c?l.localColorTable:e.globalColorTable)[t];return t!==s(null)?{r:o,g:i,b:r,a:255}:{r:o,g:i,b:r,a:a?~~((o+i+r)/3):0}},w=(()=>{try{return new ImageData(l.width,l.height,{colorSpace:"srgb"})}catch(t){if(t instanceof DOMException&&"IndexSizeError"===t.name)return null;throw t}})();if(null==w)throw new EvalError("GIF frame size is to large");const u=t.nextByte(),m=t.readSubBlocksBin(),y=1<<u,b=(t,e)=>{const a=t>>>3,o=7&t;return(m[a]+(m[a+1]<<8)+(m[a+2]<<16)&(1<<e)-1<<o)>>>o};if(g){for(let a=0,s=u+1,h=0,c=[[0]],g=0;g<4;g++){if(o[g]<l.height){let t=0,e=0,r=!1;for(;!r;){const n=a;if(a=b(h,s),h+=s+1,a===y){s=u+1,c.length=y+2;for(let t=0;t<c.length;t++)c[t]=t<y?[t]:[]}else{a>=c.length?c.push(c[n].concat(c[n][0])):n!==y&&c.push(c[n].concat(c[a][0]));for(const r of c[a]){const{r:a,g:s,b:n,a:h}=p(r);w.data.set([a,s,n,h],o[g]*l.width+i[g]*e+t%(4*l.width)),t+=4}c.length===1<<s&&s<12&&s++}t===4*l.width*(e+1)&&(e++,o[g]+i[g]*e>=l.height&&(r=!0))}}n?.(t.pos/(t.data.length-1),r(!1)+1,w,{x:l.left,y:l.top},{width:e.width,height:e.height})}l.image=w,l.bitmap=await createImageBitmap(w)}else{let a=0,o=u+1,i=0,s=-4,h=!1;const c=[[0]];for(;!h;){const t=a;if(a=b(i,o),i+=o,a===y){o=u+1,c.length=y+2;for(let t=0;t<c.length;t++)c[t]=t<y?[t]:[]}else{if(a===y+1){h=!0;break}a>=c.length?c.push(c[t].concat(c[t][0])):t!==y&&c.push(c[t].concat(c[a][0]));for(const t of c[a]){const{r:e,g:a,b:o,a:i}=p(t);w.data.set([e,a,o,i],s+=4)}c.length>=1<<o&&o<12&&o++}}l.image=w,l.bitmap=await createImageBitmap(w),n?.((t.pos+1)/t.data.length,r(!1)+1,l.image,{x:l.left,y:l.top},{width:e.width,height:e.height})}}(t,e,a,r,s,n);break;case 33:!function(t,e,a,o){switch(t.nextByte()){case 249:{const i=e.frames[a(!1)];t.pos++;const r=t.nextByte();i.GCreserved=(224&r)>>>5,i.disposalMethod=(28&r)>>>2,i.userInputDelayFlag=2==(2&r);const s=1==(1&r);i.delayTime=10*t.nextTwoBytes();const n=t.nextByte();s&&o(n),t.pos++;break}case 255:{t.pos++;const a={identifier:t.getString(8),authenticationCode:t.getString(3),data:t.readSubBlocksBin()};e.applicationExtensions.push(a);break}case 254:e.comments.push([a(!1),t.readSubBlocks()]);break;case 1:if(0===e.globalColorTable.length)throw new EvalError("plain text extension without global color table");t.pos++,e.frames[a(!1)].plainTextData={left:t.nextTwoBytes(),top:t.nextTwoBytes(),width:t.nextTwoBytes(),height:t.nextTwoBytes(),charSize:{width:t.nextTwoBytes(),height:t.nextTwoBytes()},foregroundColor:t.nextByte(),backgroundColor:t.nextByte(),text:t.readSubBlocks()};break;default:t.skipSubBlocks()}}(t,e,r,s);break;default:throw new EvalError("undefined block found")}return!1}function p(t){const{context:e,radius:a,particle:o,delta:i}=t,r=o.image;if(!r?.gifData||!r.gif)return;const f=new OffscreenCanvas(r.gifData.width,r.gifData.height),d=f.getContext("2d");if(!d)throw new Error("could not create offscreen canvas context");d.imageSmoothingQuality="low",d.imageSmoothingEnabled=!1,d.clearRect(s.x,s.y,f.width,f.height),void 0===o.gifLoopCount&&(o.gifLoopCount=r.gifLoopCount??g);let p=o.gifFrame??n;const w={x:-r.gifData.width*l,y:-r.gifData.height*l},u=r.gifData.frames[p];if(void 0===o.gifTime&&(o.gifTime=h),u.bitmap){switch(e.scale(a/r.gifData.width,a/r.gifData.height),u.disposalMethod){case 4:case 5:case 6:case 7:case 0:d.drawImage(u.bitmap,u.left,u.top),e.drawImage(f,w.x,w.y),d.clearRect(s.x,s.y,f.width,f.height);break;case 1:d.drawImage(u.bitmap,u.left,u.top),e.drawImage(f,w.x,w.y);break;case 2:d.drawImage(u.bitmap,u.left,u.top),e.drawImage(f,w.x,w.y),d.clearRect(s.x,s.y,f.width,f.height),r.gifData.globalColorTable.length?d.putImageData(r.gifData.backgroundImage,w.x,w.y):d.putImageData(r.gifData.frames[c].image,w.x+u.left,w.y+u.top);break;case 3:{const t=d.getImageData(s.x,s.y,f.width,f.height);d.drawImage(u.bitmap,u.left,u.top),e.drawImage(f,w.x,w.y),d.clearRect(s.x,s.y,f.width,f.height),d.putImageData(t,s.x,s.y)}}if(o.gifTime+=i.value,o.gifTime>u.delayTime){if(o.gifTime-=u.delayTime,++p>=r.gifData.frames.length){if(--o.gifLoopCount<=g)return;p=c,d.clearRect(s.x,s.y,f.width,f.height)}o.gifFrame=p}e.scale(r.gifData.width/a,r.gifData.height/a)}}async function w(t){if("gif"===t.type){t.loading=!0;try{t.gifData=await async function(t,e,a){a||(a=!1);const o=await fetch(t);if(!o.ok&&404===o.status)throw new EvalError("file not found");const i=await o.arrayBuffer(),s={width:0,height:0,totalTime:0,colorRes:0,pixelAspectRatio:0,frames:[],sortFlag:!1,globalColorTable:[],backgroundImage:new ImageData(1,1,{colorSpace:"srgb"}),comments:[],applicationExtensions:[]},n=new r(new Uint8ClampedArray(i));if("GIF89a"!==n.getString(6))throw new Error("not a supported GIF file");s.width=n.nextTwoBytes(),s.height=n.nextTwoBytes();const l=n.nextByte(),h=128==(128&l);s.colorRes=(112&l)>>>4,s.sortFlag=8==(8&l);const c=1<<1+(7&l),g=n.nextByte();s.pixelAspectRatio=n.nextByte(),0!==s.pixelAspectRatio&&(s.pixelAspectRatio=(s.pixelAspectRatio+15)/64),h&&(s.globalColorTable=f(n,c));const p=(()=>{try{return new ImageData(s.width,s.height,{colorSpace:"srgb"})}catch(t){if(t instanceof DOMException&&"IndexSizeError"===t.name)return null;throw t}})();if(null==p)throw new Error("GIF frame size is to large");const{r:w,g:u,b:m}=s.globalColorTable[g];p.data.set(h?[w,u,m,255]:[0,0,0,0]);for(let t=4;t<p.data.length;t*=2)p.data.copyWithin(t,0,t);s.backgroundImage=p;let y=-1,b=!0,x=-1;const B=t=>(t&&(b=!0),y),T=t=>(null!=t&&(x=t),x);try{do{b&&(s.frames.push({left:0,top:0,width:0,height:0,disposalMethod:0,image:new ImageData(1,1,{colorSpace:"srgb"}),plainTextData:null,userInputDelayFlag:!1,delayTime:0,sortFlag:!1,localColorTable:[],reserved:0,GCreserved:0}),y++,x=-1,b=!1)}while(!await d(n,s,a,B,T,e));s.frames.length--;for(const t of s.frames){if(t.userInputDelayFlag&&0===t.delayTime){s.totalTime=1/0;break}s.totalTime+=t.delayTime}return s}catch(t){if(t instanceof EvalError)throw new Error(`error while parsing frame ${y} "${t.message}"`);throw t}}(t.source),t.gifLoopCount=function(t){for(const e of t.applicationExtensions)if(e.identifier+e.authenticationCode==="NETSCAPE2.0")return e.data[1]+(e.data[2]<<8);return NaN}(t.gifData)??g,t.gifLoopCount||(t.gifLoopCount=1/0)}catch{t.error=!0}t.loading=!1}else{const{loadImage:e}=await Promise.resolve().then(a.bind(a,103));await e(t)}}}}]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/*! tsParticles Image Shape v3.2.1 by Matteo Bruni */
|
package/623.min.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! For license information please see 623.min.js.LICENSE.txt */
|
|
2
|
+
(this.webpackChunk_tsparticles_shape_image=this.webpackChunk_tsparticles_shape_image||[]).push([[623],{623:(e,i,s)=>{s.d(i,{ImagePreloaderPlugin:()=>r});class o{constructor(){this.src="",this.gif=!1}load(e){e&&(void 0!==e.gif&&(this.gif=e.gif),void 0!==e.height&&(this.height=e.height),void 0!==e.name&&(this.name=e.name),void 0!==e.replaceColor&&(this.replaceColor=e.replaceColor),void 0!==e.src&&(this.src=e.src),void 0!==e.width&&(this.width=e.width))}}class r{constructor(e){this.id="imagePreloader",this._engine=e}async getPlugin(){return await Promise.resolve(),{}}loadOptions(e,i){if(!i?.preload)return;e.preload||(e.preload=[]);const s=e.preload;for(const e of i.preload){const i=s.find((i=>i.name===e.name||i.src===e.src));if(i)i.load(e);else{const i=new o;i.load(e),s.push(i)}}}needsPlugin(){return!0}}}}]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/*! tsParticles Image Shape v3.2.1 by Matteo Bruni */
|
|
@@ -27,12 +27,13 @@ export class ByteStream {
|
|
|
27
27
|
return blockString;
|
|
28
28
|
}
|
|
29
29
|
readSubBlocksBin() {
|
|
30
|
-
let size =
|
|
30
|
+
let size = this.data[this.pos], len = 0;
|
|
31
31
|
const emptySize = 0, increment = 1;
|
|
32
32
|
for (let offset = 0; size !== emptySize; offset += size + increment, size = this.data[this.pos + offset]) {
|
|
33
33
|
len += size;
|
|
34
34
|
}
|
|
35
35
|
const blockData = new Uint8Array(len);
|
|
36
|
+
size = this.data[this.pos++];
|
|
36
37
|
for (let i = 0; size !== emptySize; size = this.data[this.pos++]) {
|
|
37
38
|
for (let count = size; --count >= emptySize; blockData[i++] = this.data[this.pos++]) {
|
|
38
39
|
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { InterlaceOffsets, InterlaceSteps } from "./Constants.js";
|
|
2
2
|
import { ByteStream } from "./ByteStream.js";
|
|
3
|
+
const origin = {
|
|
4
|
+
x: 0,
|
|
5
|
+
y: 0,
|
|
6
|
+
}, defaultFrame = 0, half = 0.5, initialTime = 0, firstIndex = 0, defaultLoopCount = 0;
|
|
3
7
|
function parseColorTable(byteStream, count) {
|
|
4
8
|
const colors = [];
|
|
5
9
|
for (let i = 0; i < count; i++) {
|
|
@@ -329,3 +333,95 @@ export async function decodeGIF(gifURL, progressCallback, avgAlpha) {
|
|
|
329
333
|
throw error;
|
|
330
334
|
}
|
|
331
335
|
}
|
|
336
|
+
export function drawGif(data) {
|
|
337
|
+
const { context, radius, particle, delta } = data, image = particle.image;
|
|
338
|
+
if (!image?.gifData || !image.gif) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const offscreenCanvas = new OffscreenCanvas(image.gifData.width, image.gifData.height), offscreenContext = offscreenCanvas.getContext("2d");
|
|
342
|
+
if (!offscreenContext) {
|
|
343
|
+
throw new Error("could not create offscreen canvas context");
|
|
344
|
+
}
|
|
345
|
+
offscreenContext.imageSmoothingQuality = "low";
|
|
346
|
+
offscreenContext.imageSmoothingEnabled = false;
|
|
347
|
+
offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
|
|
348
|
+
if (particle.gifLoopCount === undefined) {
|
|
349
|
+
particle.gifLoopCount = image.gifLoopCount ?? defaultLoopCount;
|
|
350
|
+
}
|
|
351
|
+
let frameIndex = particle.gifFrame ?? defaultFrame;
|
|
352
|
+
const pos = { x: -image.gifData.width * half, y: -image.gifData.height * half }, frame = image.gifData.frames[frameIndex];
|
|
353
|
+
if (particle.gifTime === undefined) {
|
|
354
|
+
particle.gifTime = initialTime;
|
|
355
|
+
}
|
|
356
|
+
if (!frame.bitmap) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
context.scale(radius / image.gifData.width, radius / image.gifData.height);
|
|
360
|
+
switch (frame.disposalMethod) {
|
|
361
|
+
case 4:
|
|
362
|
+
case 5:
|
|
363
|
+
case 6:
|
|
364
|
+
case 7:
|
|
365
|
+
case 0:
|
|
366
|
+
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
|
|
367
|
+
context.drawImage(offscreenCanvas, pos.x, pos.y);
|
|
368
|
+
offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
|
|
369
|
+
break;
|
|
370
|
+
case 1:
|
|
371
|
+
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
|
|
372
|
+
context.drawImage(offscreenCanvas, pos.x, pos.y);
|
|
373
|
+
break;
|
|
374
|
+
case 2:
|
|
375
|
+
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
|
|
376
|
+
context.drawImage(offscreenCanvas, pos.x, pos.y);
|
|
377
|
+
offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
|
|
378
|
+
if (!image.gifData.globalColorTable.length) {
|
|
379
|
+
offscreenContext.putImageData(image.gifData.frames[firstIndex].image, pos.x + frame.left, pos.y + frame.top);
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
offscreenContext.putImageData(image.gifData.backgroundImage, pos.x, pos.y);
|
|
383
|
+
}
|
|
384
|
+
break;
|
|
385
|
+
case 3:
|
|
386
|
+
{
|
|
387
|
+
const previousImageData = offscreenContext.getImageData(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
|
|
388
|
+
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
|
|
389
|
+
context.drawImage(offscreenCanvas, pos.x, pos.y);
|
|
390
|
+
offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
|
|
391
|
+
offscreenContext.putImageData(previousImageData, origin.x, origin.y);
|
|
392
|
+
}
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
particle.gifTime += delta.value;
|
|
396
|
+
if (particle.gifTime > frame.delayTime) {
|
|
397
|
+
particle.gifTime -= frame.delayTime;
|
|
398
|
+
if (++frameIndex >= image.gifData.frames.length) {
|
|
399
|
+
if (--particle.gifLoopCount <= defaultLoopCount) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
frameIndex = firstIndex;
|
|
403
|
+
offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
|
|
404
|
+
}
|
|
405
|
+
particle.gifFrame = frameIndex;
|
|
406
|
+
}
|
|
407
|
+
context.scale(image.gifData.width / radius, image.gifData.height / radius);
|
|
408
|
+
}
|
|
409
|
+
export async function loadGifImage(image) {
|
|
410
|
+
if (image.type !== "gif") {
|
|
411
|
+
const { loadImage } = await import("../Utils.js");
|
|
412
|
+
await loadImage(image);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
image.loading = true;
|
|
416
|
+
try {
|
|
417
|
+
image.gifData = await decodeGIF(image.source);
|
|
418
|
+
image.gifLoopCount = getGIFLoopAmount(image.gifData) ?? defaultLoopCount;
|
|
419
|
+
if (!image.gifLoopCount) {
|
|
420
|
+
image.gifLoopCount = Infinity;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
catch {
|
|
424
|
+
image.error = true;
|
|
425
|
+
}
|
|
426
|
+
image.loading = false;
|
|
427
|
+
}
|
package/browser/ImageDrawer.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import { errorPrefix
|
|
2
|
-
|
|
3
|
-
const origin = {
|
|
4
|
-
x: 0,
|
|
5
|
-
y: 0,
|
|
6
|
-
}, defaultLoopCount = 0, defaultFrame = 0, half = 0.5, initialTime = 0, firstIndex = 0, double = 2, defaultAlpha = 1, sides = 12, defaultRatio = 1;
|
|
1
|
+
import { errorPrefix } from "@tsparticles/engine";
|
|
2
|
+
const double = 2, defaultAlpha = 1, sides = 12, defaultRatio = 1;
|
|
7
3
|
export class ImageDrawer {
|
|
8
4
|
constructor(engine) {
|
|
9
5
|
this.loadImageShape = async (imageShape) => {
|
|
@@ -25,80 +21,15 @@ export class ImageDrawer {
|
|
|
25
21
|
}
|
|
26
22
|
this._engine.images.push(image);
|
|
27
23
|
}
|
|
28
|
-
draw(data) {
|
|
29
|
-
const { context, radius, particle, opacity
|
|
24
|
+
async draw(data) {
|
|
25
|
+
const { context, radius, particle, opacity } = data, image = particle.image, element = image?.element;
|
|
30
26
|
if (!image) {
|
|
31
27
|
return;
|
|
32
28
|
}
|
|
33
29
|
context.globalAlpha = opacity;
|
|
34
30
|
if (image.gif && image.gifData) {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
throw new Error("could not create offscreen canvas context");
|
|
38
|
-
}
|
|
39
|
-
offscreenContext.imageSmoothingQuality = "low";
|
|
40
|
-
offscreenContext.imageSmoothingEnabled = false;
|
|
41
|
-
offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
|
|
42
|
-
if (particle.gifLoopCount === undefined) {
|
|
43
|
-
particle.gifLoopCount = image.gifLoopCount ?? defaultLoopCount;
|
|
44
|
-
}
|
|
45
|
-
let frameIndex = particle.gifFrame ?? defaultFrame;
|
|
46
|
-
const pos = { x: -image.gifData.width * half, y: -image.gifData.height * half }, frame = image.gifData.frames[frameIndex];
|
|
47
|
-
if (particle.gifTime === undefined) {
|
|
48
|
-
particle.gifTime = initialTime;
|
|
49
|
-
}
|
|
50
|
-
if (!frame.bitmap) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
context.scale(radius / image.gifData.width, radius / image.gifData.height);
|
|
54
|
-
switch (frame.disposalMethod) {
|
|
55
|
-
case 4:
|
|
56
|
-
case 5:
|
|
57
|
-
case 6:
|
|
58
|
-
case 7:
|
|
59
|
-
case 0:
|
|
60
|
-
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
|
|
61
|
-
context.drawImage(offscreenCanvas, pos.x, pos.y);
|
|
62
|
-
offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
|
|
63
|
-
break;
|
|
64
|
-
case 1:
|
|
65
|
-
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
|
|
66
|
-
context.drawImage(offscreenCanvas, pos.x, pos.y);
|
|
67
|
-
break;
|
|
68
|
-
case 2:
|
|
69
|
-
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
|
|
70
|
-
context.drawImage(offscreenCanvas, pos.x, pos.y);
|
|
71
|
-
offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
|
|
72
|
-
if (!image.gifData.globalColorTable.length) {
|
|
73
|
-
offscreenContext.putImageData(image.gifData.frames[firstIndex].image, pos.x + frame.left, pos.y + frame.top);
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
offscreenContext.putImageData(image.gifData.backgroundImage, pos.x, pos.y);
|
|
77
|
-
}
|
|
78
|
-
break;
|
|
79
|
-
case 3:
|
|
80
|
-
{
|
|
81
|
-
const previousImageData = offscreenContext.getImageData(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
|
|
82
|
-
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
|
|
83
|
-
context.drawImage(offscreenCanvas, pos.x, pos.y);
|
|
84
|
-
offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
|
|
85
|
-
offscreenContext.putImageData(previousImageData, origin.x, origin.y);
|
|
86
|
-
}
|
|
87
|
-
break;
|
|
88
|
-
}
|
|
89
|
-
particle.gifTime += delta.value;
|
|
90
|
-
if (particle.gifTime > frame.delayTime) {
|
|
91
|
-
particle.gifTime -= frame.delayTime;
|
|
92
|
-
if (++frameIndex >= image.gifData.frames.length) {
|
|
93
|
-
if (--particle.gifLoopCount <= defaultLoopCount) {
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
frameIndex = firstIndex;
|
|
97
|
-
offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
|
|
98
|
-
}
|
|
99
|
-
particle.gifFrame = frameIndex;
|
|
100
|
-
}
|
|
101
|
-
context.scale(image.gifData.width / radius, image.gifData.height / radius);
|
|
31
|
+
const { drawGif } = await import("./GifUtils/Utils.js");
|
|
32
|
+
drawGif(data);
|
|
102
33
|
}
|
|
103
34
|
else if (element) {
|
|
104
35
|
const ratio = image.ratio, pos = {
|
|
@@ -121,7 +52,7 @@ export class ImageDrawer {
|
|
|
121
52
|
await this._engine.loadImage(imageData);
|
|
122
53
|
}
|
|
123
54
|
}
|
|
124
|
-
loadShape(particle) {
|
|
55
|
+
async loadShape(particle) {
|
|
125
56
|
if (particle.shape !== "image" && particle.shape !== "images") {
|
|
126
57
|
return;
|
|
127
58
|
}
|
|
@@ -134,12 +65,11 @@ export class ImageDrawer {
|
|
|
134
65
|
}
|
|
135
66
|
const image = this._engine.images.find((t) => t.name === imageData.name || t.source === imageData.src);
|
|
136
67
|
if (!image) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
});
|
|
68
|
+
await this.loadImageShape(imageData);
|
|
69
|
+
await this.loadShape(particle);
|
|
140
70
|
}
|
|
141
71
|
}
|
|
142
|
-
particleInit(container, particle) {
|
|
72
|
+
async particleInit(container, particle) {
|
|
143
73
|
if (particle.shape !== "image" && particle.shape !== "images") {
|
|
144
74
|
return;
|
|
145
75
|
}
|
|
@@ -157,42 +87,41 @@ export class ImageDrawer {
|
|
|
157
87
|
const replaceColor = imageData.replaceColor ?? image.replaceColor;
|
|
158
88
|
if (image.loading) {
|
|
159
89
|
setTimeout(() => {
|
|
160
|
-
this.particleInit(container, particle);
|
|
90
|
+
void this.particleInit(container, particle);
|
|
161
91
|
});
|
|
162
92
|
return;
|
|
163
93
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
if (!imageRes.ratio) {
|
|
186
|
-
imageRes.ratio = 1;
|
|
187
|
-
}
|
|
188
|
-
const fill = imageData.fill ?? particle.shapeFill, close = imageData.close ?? particle.shapeClose, imageShape = {
|
|
189
|
-
image: imageRes,
|
|
190
|
-
fill,
|
|
191
|
-
close,
|
|
94
|
+
let imageRes;
|
|
95
|
+
if (image.svgData && color) {
|
|
96
|
+
const { replaceImageColor } = await import("./Utils.js");
|
|
97
|
+
imageRes = await replaceImageColor(image, imageData, color, particle);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
imageRes = {
|
|
101
|
+
color,
|
|
102
|
+
data: image,
|
|
103
|
+
element: image.element,
|
|
104
|
+
gif: image.gif,
|
|
105
|
+
gifData: image.gifData,
|
|
106
|
+
gifLoopCount: image.gifLoopCount,
|
|
107
|
+
loaded: true,
|
|
108
|
+
ratio: imageData.width && imageData.height
|
|
109
|
+
? imageData.width / imageData.height
|
|
110
|
+
: image.ratio ?? defaultRatio,
|
|
111
|
+
replaceColor: replaceColor,
|
|
112
|
+
source: imageData.src,
|
|
192
113
|
};
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
114
|
+
}
|
|
115
|
+
if (!imageRes.ratio) {
|
|
116
|
+
imageRes.ratio = 1;
|
|
117
|
+
}
|
|
118
|
+
const fill = imageData.fill ?? particle.shapeFill, close = imageData.close ?? particle.shapeClose, imageShape = {
|
|
119
|
+
image: imageRes,
|
|
120
|
+
fill,
|
|
121
|
+
close,
|
|
122
|
+
};
|
|
123
|
+
particle.image = imageShape.image;
|
|
124
|
+
particle.shapeFill = imageShape.fill;
|
|
125
|
+
particle.shapeClose = imageShape.close;
|
|
197
126
|
}
|
|
198
127
|
}
|
package/browser/Utils.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { errorPrefix, getLogger, getStyleFromHsl } from "@tsparticles/engine";
|
|
2
|
-
|
|
3
|
-
const stringStart = 0, defaultLoopCount = 0, defaultOpacity = 1;
|
|
2
|
+
const stringStart = 0, defaultOpacity = 1;
|
|
4
3
|
const currentColorRegex = /(#(?:[0-9a-f]{2}){2,4}|(#[0-9a-f]{3})|(rgb|hsl)a?\((-?\d+%?[,\s]+){2,3}\s*[\d.]+%?\))|currentcolor/gi;
|
|
5
4
|
function replaceColorSvg(imageShape, color, opacity) {
|
|
6
5
|
const { svgData } = imageShape;
|
|
@@ -33,24 +32,6 @@ export async function loadImage(image) {
|
|
|
33
32
|
img.src = image.source;
|
|
34
33
|
});
|
|
35
34
|
}
|
|
36
|
-
export async function loadGifImage(image) {
|
|
37
|
-
if (image.type !== "gif") {
|
|
38
|
-
await loadImage(image);
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
image.loading = true;
|
|
42
|
-
try {
|
|
43
|
-
image.gifData = await decodeGIF(image.source);
|
|
44
|
-
image.gifLoopCount = getGIFLoopAmount(image.gifData) ?? defaultLoopCount;
|
|
45
|
-
if (!image.gifLoopCount) {
|
|
46
|
-
image.gifLoopCount = Infinity;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
catch {
|
|
50
|
-
image.error = true;
|
|
51
|
-
}
|
|
52
|
-
image.loading = false;
|
|
53
|
-
}
|
|
54
35
|
export async function downloadSvgImage(image) {
|
|
55
36
|
if (image.type !== "svg") {
|
|
56
37
|
await loadImage(image);
|
package/browser/index.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { downloadSvgImage,
|
|
2
|
-
import { ImageDrawer } from "./ImageDrawer.js";
|
|
3
|
-
import { ImagePreloaderPlugin } from "./ImagePreloader.js";
|
|
1
|
+
import { downloadSvgImage, loadImage } from "./Utils.js";
|
|
4
2
|
import { errorPrefix } from "@tsparticles/engine";
|
|
5
3
|
const extLength = 3;
|
|
6
4
|
function addLoadImageToEngine(engine) {
|
|
@@ -31,6 +29,7 @@ function addLoadImageToEngine(engine) {
|
|
|
31
29
|
engine.images.push(image);
|
|
32
30
|
let imageFunc;
|
|
33
31
|
if (data.gif) {
|
|
32
|
+
const { loadGifImage } = await import("./GifUtils/Utils.js");
|
|
34
33
|
imageFunc = loadGifImage;
|
|
35
34
|
}
|
|
36
35
|
else {
|
|
@@ -45,6 +44,7 @@ function addLoadImageToEngine(engine) {
|
|
|
45
44
|
}
|
|
46
45
|
export async function loadImageShape(engine, refresh = true) {
|
|
47
46
|
addLoadImageToEngine(engine);
|
|
47
|
+
const { ImagePreloaderPlugin } = await import("./ImagePreloader.js"), { ImageDrawer } = await import("./ImageDrawer.js");
|
|
48
48
|
const preloader = new ImagePreloaderPlugin(engine);
|
|
49
49
|
await engine.addPlugin(preloader, refresh);
|
|
50
50
|
await engine.addShape(["image", "images"], new ImageDrawer(engine), refresh);
|
|
@@ -30,12 +30,13 @@ class ByteStream {
|
|
|
30
30
|
return blockString;
|
|
31
31
|
}
|
|
32
32
|
readSubBlocksBin() {
|
|
33
|
-
let size =
|
|
33
|
+
let size = this.data[this.pos], len = 0;
|
|
34
34
|
const emptySize = 0, increment = 1;
|
|
35
35
|
for (let offset = 0; size !== emptySize; offset += size + increment, size = this.data[this.pos + offset]) {
|
|
36
36
|
len += size;
|
|
37
37
|
}
|
|
38
38
|
const blockData = new Uint8Array(len);
|
|
39
|
+
size = this.data[this.pos++];
|
|
39
40
|
for (let i = 0; size !== emptySize; size = this.data[this.pos++]) {
|
|
40
41
|
for (let count = size; --count >= emptySize; blockData[i++] = this.data[this.pos++]) {
|
|
41
42
|
}
|
package/cjs/GifUtils/Utils.js
CHANGED
|
@@ -1,8 +1,35 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.decodeGIF = exports.getGIFLoopAmount = void 0;
|
|
26
|
+
exports.loadGifImage = exports.drawGif = exports.decodeGIF = exports.getGIFLoopAmount = void 0;
|
|
4
27
|
const Constants_js_1 = require("./Constants.js");
|
|
5
28
|
const ByteStream_js_1 = require("./ByteStream.js");
|
|
29
|
+
const origin = {
|
|
30
|
+
x: 0,
|
|
31
|
+
y: 0,
|
|
32
|
+
}, defaultFrame = 0, half = 0.5, initialTime = 0, firstIndex = 0, defaultLoopCount = 0;
|
|
6
33
|
function parseColorTable(byteStream, count) {
|
|
7
34
|
const colors = [];
|
|
8
35
|
for (let i = 0; i < count; i++) {
|
|
@@ -334,3 +361,97 @@ async function decodeGIF(gifURL, progressCallback, avgAlpha) {
|
|
|
334
361
|
}
|
|
335
362
|
}
|
|
336
363
|
exports.decodeGIF = decodeGIF;
|
|
364
|
+
function drawGif(data) {
|
|
365
|
+
const { context, radius, particle, delta } = data, image = particle.image;
|
|
366
|
+
if (!image?.gifData || !image.gif) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
const offscreenCanvas = new OffscreenCanvas(image.gifData.width, image.gifData.height), offscreenContext = offscreenCanvas.getContext("2d");
|
|
370
|
+
if (!offscreenContext) {
|
|
371
|
+
throw new Error("could not create offscreen canvas context");
|
|
372
|
+
}
|
|
373
|
+
offscreenContext.imageSmoothingQuality = "low";
|
|
374
|
+
offscreenContext.imageSmoothingEnabled = false;
|
|
375
|
+
offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
|
|
376
|
+
if (particle.gifLoopCount === undefined) {
|
|
377
|
+
particle.gifLoopCount = image.gifLoopCount ?? defaultLoopCount;
|
|
378
|
+
}
|
|
379
|
+
let frameIndex = particle.gifFrame ?? defaultFrame;
|
|
380
|
+
const pos = { x: -image.gifData.width * half, y: -image.gifData.height * half }, frame = image.gifData.frames[frameIndex];
|
|
381
|
+
if (particle.gifTime === undefined) {
|
|
382
|
+
particle.gifTime = initialTime;
|
|
383
|
+
}
|
|
384
|
+
if (!frame.bitmap) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
context.scale(radius / image.gifData.width, radius / image.gifData.height);
|
|
388
|
+
switch (frame.disposalMethod) {
|
|
389
|
+
case 4:
|
|
390
|
+
case 5:
|
|
391
|
+
case 6:
|
|
392
|
+
case 7:
|
|
393
|
+
case 0:
|
|
394
|
+
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
|
|
395
|
+
context.drawImage(offscreenCanvas, pos.x, pos.y);
|
|
396
|
+
offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
|
|
397
|
+
break;
|
|
398
|
+
case 1:
|
|
399
|
+
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
|
|
400
|
+
context.drawImage(offscreenCanvas, pos.x, pos.y);
|
|
401
|
+
break;
|
|
402
|
+
case 2:
|
|
403
|
+
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
|
|
404
|
+
context.drawImage(offscreenCanvas, pos.x, pos.y);
|
|
405
|
+
offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
|
|
406
|
+
if (!image.gifData.globalColorTable.length) {
|
|
407
|
+
offscreenContext.putImageData(image.gifData.frames[firstIndex].image, pos.x + frame.left, pos.y + frame.top);
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
offscreenContext.putImageData(image.gifData.backgroundImage, pos.x, pos.y);
|
|
411
|
+
}
|
|
412
|
+
break;
|
|
413
|
+
case 3:
|
|
414
|
+
{
|
|
415
|
+
const previousImageData = offscreenContext.getImageData(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
|
|
416
|
+
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
|
|
417
|
+
context.drawImage(offscreenCanvas, pos.x, pos.y);
|
|
418
|
+
offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
|
|
419
|
+
offscreenContext.putImageData(previousImageData, origin.x, origin.y);
|
|
420
|
+
}
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
423
|
+
particle.gifTime += delta.value;
|
|
424
|
+
if (particle.gifTime > frame.delayTime) {
|
|
425
|
+
particle.gifTime -= frame.delayTime;
|
|
426
|
+
if (++frameIndex >= image.gifData.frames.length) {
|
|
427
|
+
if (--particle.gifLoopCount <= defaultLoopCount) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
frameIndex = firstIndex;
|
|
431
|
+
offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
|
|
432
|
+
}
|
|
433
|
+
particle.gifFrame = frameIndex;
|
|
434
|
+
}
|
|
435
|
+
context.scale(image.gifData.width / radius, image.gifData.height / radius);
|
|
436
|
+
}
|
|
437
|
+
exports.drawGif = drawGif;
|
|
438
|
+
async function loadGifImage(image) {
|
|
439
|
+
if (image.type !== "gif") {
|
|
440
|
+
const { loadImage } = await Promise.resolve().then(() => __importStar(require("../Utils.js")));
|
|
441
|
+
await loadImage(image);
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
image.loading = true;
|
|
445
|
+
try {
|
|
446
|
+
image.gifData = await decodeGIF(image.source);
|
|
447
|
+
image.gifLoopCount = getGIFLoopAmount(image.gifData) ?? defaultLoopCount;
|
|
448
|
+
if (!image.gifLoopCount) {
|
|
449
|
+
image.gifLoopCount = Infinity;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
catch {
|
|
453
|
+
image.error = true;
|
|
454
|
+
}
|
|
455
|
+
image.loading = false;
|
|
456
|
+
}
|
|
457
|
+
exports.loadGifImage = loadGifImage;
|