CCParticleBatchNode.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. /**
  2. * Copyright (c) 2008-2010 Ricardo Quesada
  3. * Copyright (c) 2011-2012 cocos2d-x.org
  4. * Copyright (c) 2013-2014 Chukong Technologies Inc.
  5. * Copyright (C) 2009 Matt Oswald
  6. * Copyright (c) 2011 Marco Tillemans
  7. *
  8. * http://www.cocos2d-x.org
  9. *
  10. * Permission is hereby granted, free of charge, to any person obtaining a copy
  11. * of this software and associated documentation files (the "Software"), to deal
  12. * in the Software without restriction, including without limitation the rights
  13. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  14. * copies of the Software, and to permit persons to whom the Software is
  15. * furnished to do so, subject to the following conditions:
  16. *
  17. * The above copyright notice and this permission notice shall be included in
  18. * all copies or substantial portions of the Software.
  19. *
  20. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  21. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  22. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  23. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  24. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  25. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  26. * THE SOFTWARE.
  27. *
  28. */
  29. /**
  30. * paticle default capacity
  31. * @constant
  32. * @type Number
  33. */
  34. cc.PARTICLE_DEFAULT_CAPACITY = 500;
  35. /**
  36. * <p>
  37. * cc.ParticleBatchNode is like a batch node: if it contains children, it will draw them in 1 single OpenGL call <br/>
  38. * (often known as "batch draw"). </br>
  39. *
  40. * A cc.ParticleBatchNode can reference one and only one texture (one image file, one texture atlas).<br/>
  41. * Only the cc.ParticleSystems that are contained in that texture can be added to the cc.SpriteBatchNode.<br/>
  42. * All cc.ParticleSystems added to a cc.SpriteBatchNode are drawn in one OpenGL ES draw call.<br/>
  43. * If the cc.ParticleSystems are not added to a cc.ParticleBatchNode then an OpenGL ES draw call will be needed for each one, which is less efficient.</br>
  44. *
  45. * Limitations:<br/>
  46. * - At the moment only cc.ParticleSystem is supported<br/>
  47. * - All systems need to be drawn with the same parameters, blend function, aliasing, texture<br/>
  48. *
  49. * Most efficient usage<br/>
  50. * - Initialize the ParticleBatchNode with the texture and enough capacity for all the particle systems<br/>
  51. * - Initialize all particle systems and add them as child to the batch node<br/>
  52. * </p>
  53. * @class
  54. * @extends cc.ParticleSystem
  55. * @param {String|cc.Texture2D} fileImage
  56. * @param {Number} capacity
  57. *
  58. * @property {cc.Texture2D|HTMLImageElement|HTMLCanvasElement} texture - The used texture
  59. * @property {cc.TextureAtlas} textureAtlas - The texture atlas used for drawing the quads
  60. *
  61. * @example
  62. * 1.
  63. * //Create a cc.ParticleBatchNode with image path and capacity
  64. * var particleBatchNode = new cc.ParticleBatchNode("res/grossini_dance.png",30);
  65. *
  66. * 2.
  67. * //Create a cc.ParticleBatchNode with a texture and capacity
  68. * var texture = cc.TextureCache.getInstance().addImage("res/grossini_dance.png");
  69. * var particleBatchNode = new cc.ParticleBatchNode(texture, 30);
  70. */
  71. cc.ParticleBatchNode = cc.Node.extend(/** @lends cc.ParticleBatchNode# */{
  72. textureAtlas:null,
  73. TextureProtocol:true,
  74. //the blend function used for drawing the quads
  75. _blendFunc:null,
  76. _className:"ParticleBatchNode",
  77. /**
  78. * initializes the particle system with the name of a file on disk (for a list of supported formats look at the cc.Texture2D class), a capacity of particles
  79. * Constructor of cc.ParticleBatchNode
  80. * @param {String|cc.Texture2D} fileImage
  81. * @param {Number} capacity
  82. * @example
  83. * 1.
  84. * //Create a cc.ParticleBatchNode with image path and capacity
  85. * var particleBatchNode = new cc.ParticleBatchNode("res/grossini_dance.png",30);
  86. *
  87. * 2.
  88. * //Create a cc.ParticleBatchNode with a texture and capacity
  89. * var texture = cc.TextureCache.getInstance().addImage("res/grossini_dance.png");
  90. * var particleBatchNode = new cc.ParticleBatchNode(texture, 30);
  91. */
  92. ctor:function (fileImage, capacity) {
  93. cc.Node.prototype.ctor.call(this);
  94. this._blendFunc = {src:cc.BLEND_SRC, dst:cc.BLEND_DST};
  95. if (typeof(fileImage) == "string") {
  96. this.init(fileImage, capacity);
  97. } else if (fileImage instanceof cc.Texture2D) {
  98. this.initWithTexture(fileImage, capacity);
  99. }
  100. },
  101. /**
  102. * initializes the particle system with cc.Texture2D, a capacity of particles
  103. * @param {cc.Texture2D|HTMLImageElement|HTMLCanvasElement} texture
  104. * @param {Number} capacity
  105. * @return {Boolean}
  106. */
  107. initWithTexture:function (texture, capacity) {
  108. this.textureAtlas = new cc.TextureAtlas();
  109. this.textureAtlas.initWithTexture(texture, capacity);
  110. // no lazy alloc in this node
  111. this._children.length = 0;
  112. if (cc._renderType === cc._RENDER_TYPE_WEBGL)
  113. this.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR);
  114. return true;
  115. },
  116. /**
  117. * initializes the particle system with the name of a file on disk (for a list of supported formats look at the cc.Texture2D class), a capacity of particles
  118. * @param {String} fileImage
  119. * @param {Number} capacity
  120. * @return {Boolean}
  121. */
  122. initWithFile:function (fileImage, capacity) {
  123. var tex = cc.textureCache.addImage(fileImage);
  124. return this.initWithTexture(tex, capacity);
  125. },
  126. /**
  127. * initializes the particle system with the name of a file on disk (for a list of supported formats look at the cc.Texture2D class), a capacity of particles
  128. * @param {String} fileImage
  129. * @param {Number} capacity
  130. * @return {Boolean}
  131. */
  132. init:function (fileImage, capacity) {
  133. var tex = cc.TextureCache.getInstance().addImage(fileImage);
  134. return this.initWithTexture(tex, capacity);
  135. },
  136. /**
  137. * Add a child into the cc.ParticleBatchNode
  138. * @param {cc.ParticleSystem} child
  139. * @param {Number} zOrder
  140. * @param {Number} tag
  141. */
  142. addChild:function (child, zOrder, tag) {
  143. if(!child)
  144. throw "cc.ParticleBatchNode.addChild() : child should be non-null";
  145. if(!(child instanceof cc.ParticleSystem))
  146. throw "cc.ParticleBatchNode.addChild() : only supports cc.ParticleSystem as children";
  147. zOrder = (zOrder == null) ? child.zIndex : zOrder;
  148. tag = (tag == null) ? child.tag : tag;
  149. if(child.getTexture() != this.textureAtlas.texture)
  150. throw "cc.ParticleSystem.addChild() : the child is not using the same texture id";
  151. // If this is the 1st children, then copy blending function
  152. var childBlendFunc = child.getBlendFunc();
  153. if (this._children.length === 0)
  154. this.setBlendFunc(childBlendFunc);
  155. else{
  156. if((childBlendFunc.src != this._blendFunc.src) || (childBlendFunc.dst != this._blendFunc.dst)){
  157. cc.log("cc.ParticleSystem.addChild() : Can't add a ParticleSystem that uses a different blending function");
  158. return;
  159. }
  160. }
  161. //no lazy sorting, so don't call super addChild, call helper instead
  162. var pos = this._addChildHelper(child, zOrder, tag);
  163. //get new atlasIndex
  164. var atlasIndex = 0;
  165. if (pos != 0) {
  166. var p = this._children[pos - 1];
  167. atlasIndex = p.getAtlasIndex() + p.getTotalParticles();
  168. } else
  169. atlasIndex = 0;
  170. this.insertChild(child, atlasIndex);
  171. // update quad info
  172. child.setBatchNode(this);
  173. },
  174. /**
  175. * Inserts a child into the cc.ParticleBatchNode
  176. * @param {cc.ParticleSystem} pSystem
  177. * @param {Number} index
  178. */
  179. insertChild:function (pSystem, index) {
  180. var totalParticles = pSystem.getTotalParticles();
  181. var locTextureAtlas = this.textureAtlas;
  182. var totalQuads = locTextureAtlas.totalQuads;
  183. pSystem.setAtlasIndex(index);
  184. if (totalQuads + totalParticles > locTextureAtlas.getCapacity()) {
  185. this._increaseAtlasCapacityTo(totalQuads + totalParticles);
  186. // after a realloc empty quads of textureAtlas can be filled with gibberish (realloc doesn't perform calloc), insert empty quads to prevent it
  187. locTextureAtlas.fillWithEmptyQuadsFromIndex(locTextureAtlas.getCapacity() - totalParticles, totalParticles);
  188. }
  189. // make room for quads, not necessary for last child
  190. if (pSystem.getAtlasIndex() + totalParticles != totalQuads)
  191. locTextureAtlas.moveQuadsFromIndex(index, index + totalParticles);
  192. // increase totalParticles here for new particles, update method of particlesystem will fill the quads
  193. locTextureAtlas.increaseTotalQuadsWith(totalParticles);
  194. this._updateAllAtlasIndexes();
  195. },
  196. /**
  197. * @param {cc.ParticleSystem} child
  198. * @param {Boolean} cleanup
  199. */
  200. removeChild:function (child, cleanup) {
  201. // explicit nil handling
  202. if (child == null)
  203. return;
  204. if(!(child instanceof cc.ParticleSystem))
  205. throw "cc.ParticleBatchNode.removeChild(): only supports cc.ParticleSystem as children";
  206. if(this._children.indexOf(child) == -1){
  207. cc.log("cc.ParticleBatchNode.removeChild(): doesn't contain the sprite. Can't remove it");
  208. return;
  209. }
  210. cc.Node.prototype.removeChild.call(this, child, cleanup);
  211. var locTextureAtlas = this.textureAtlas;
  212. // remove child helper
  213. locTextureAtlas.removeQuadsAtIndex(child.getAtlasIndex(), child.getTotalParticles());
  214. // after memmove of data, empty the quads at the end of array
  215. locTextureAtlas.fillWithEmptyQuadsFromIndex(locTextureAtlas.totalQuads, child.getTotalParticles());
  216. // paticle could be reused for self rendering
  217. child.setBatchNode(null);
  218. this._updateAllAtlasIndexes();
  219. },
  220. /**
  221. * Reorder will be done in this function, no "lazy" reorder to particles
  222. * @param {cc.ParticleSystem} child
  223. * @param {Number} zOrder
  224. */
  225. reorderChild:function (child, zOrder) {
  226. if(!child)
  227. throw "cc.ParticleBatchNode.reorderChild(): child should be non-null";
  228. if(!(child instanceof cc.ParticleSystem))
  229. throw "cc.ParticleBatchNode.reorderChild(): only supports cc.QuadParticleSystems as children";
  230. if(this._children.indexOf(child) === -1){
  231. cc.log("cc.ParticleBatchNode.reorderChild(): Child doesn't belong to batch");
  232. return;
  233. }
  234. if (zOrder == child.zIndex)
  235. return;
  236. // no reordering if only 1 child
  237. if (this._children.length > 1) {
  238. var getIndexes = this._getCurrentIndex(child, zOrder);
  239. if (getIndexes.oldIndex != getIndexes.newIndex) {
  240. // reorder m_pChildren.array
  241. this._children.splice(getIndexes.oldIndex, 1)
  242. this._children.splice(getIndexes.newIndex, 0, child);
  243. // save old altasIndex
  244. var oldAtlasIndex = child.getAtlasIndex();
  245. // update atlas index
  246. this._updateAllAtlasIndexes();
  247. // Find new AtlasIndex
  248. var newAtlasIndex = 0;
  249. var locChildren = this._children;
  250. for (var i = 0; i < locChildren.length; i++) {
  251. var pNode = locChildren[i];
  252. if (pNode == child) {
  253. newAtlasIndex = child.getAtlasIndex();
  254. break;
  255. }
  256. }
  257. // reorder textureAtlas quads
  258. this.textureAtlas.moveQuadsFromIndex(oldAtlasIndex, child.getTotalParticles(), newAtlasIndex);
  259. child.updateWithNoTime();
  260. }
  261. }
  262. child._setLocalZOrder(zOrder);
  263. },
  264. /**
  265. * @param {Number} index
  266. * @param {Boolean} doCleanup
  267. */
  268. removeChildAtIndex:function (index, doCleanup) {
  269. this.removeChild(this._children[i], doCleanup);
  270. },
  271. /**
  272. * @param {Boolean} doCleanup
  273. */
  274. removeAllChildren:function (doCleanup) {
  275. var locChildren = this._children;
  276. for (var i = 0; i < locChildren.length; i++) {
  277. locChildren[i].setBatchNode(null);
  278. }
  279. cc.Node.prototype.removeAllChildren.call(this, doCleanup);
  280. this.textureAtlas.removeAllQuads();
  281. },
  282. /**
  283. * disables a particle by inserting a 0'd quad into the texture atlas
  284. * @param {Number} particleIndex
  285. */
  286. disableParticle:function (particleIndex) {
  287. var quad = this.textureAtlas.quads[particleIndex];
  288. quad.br.vertices.x = quad.br.vertices.y = quad.tr.vertices.x = quad.tr.vertices.y =
  289. quad.tl.vertices.x = quad.tl.vertices.y = quad.bl.vertices.x = quad.bl.vertices.y = 0.0;
  290. this.textureAtlas._setDirty(true);
  291. },
  292. /**
  293. * Render function using the canvas 2d context or WebGL context, internal usage only, please do not call this function
  294. * @function
  295. * @param {CanvasRenderingContext2D | WebGLRenderingContext} ctx The render context
  296. */
  297. draw:function (ctx) {
  298. //cc.PROFILER_STOP("CCParticleBatchNode - draw");
  299. if (cc._renderType === cc._RENDER_TYPE_CANVAS)
  300. return;
  301. if (this.textureAtlas.totalQuads == 0)
  302. return;
  303. cc.nodeDrawSetup(this);
  304. cc.glBlendFuncForParticle(this._blendFunc.src, this._blendFunc.dst);
  305. this.textureAtlas.drawQuads();
  306. //cc.PROFILER_STOP("CCParticleBatchNode - draw");
  307. },
  308. /**
  309. * returns the used texture
  310. * @return {cc.Texture2D|HTMLImageElement|HTMLCanvasElement}
  311. */
  312. getTexture:function () {
  313. return this.textureAtlas.texture;
  314. },
  315. /**
  316. * sets a new texture. it will be retained
  317. * @param {cc.Texture2D|HTMLImageElement|HTMLCanvasElement} texture
  318. */
  319. setTexture:function (texture) {
  320. this.textureAtlas.texture = texture;
  321. // If the new texture has No premultiplied alpha, AND the blendFunc hasn't been changed, then update it
  322. var locBlendFunc = this._blendFunc;
  323. if (texture && !texture.hasPremultipliedAlpha() && ( locBlendFunc.src == cc.BLEND_SRC && locBlendFunc.dst == cc.BLEND_DST )) {
  324. locBlendFunc.src = cc.SRC_ALPHA;
  325. locBlendFunc.dst = cc.ONE_MINUS_SRC_ALPHA;
  326. }
  327. },
  328. /**
  329. * set the blending function used for the texture
  330. * @param {Number|Object} src
  331. * @param {Number} dst
  332. */
  333. setBlendFunc:function (src, dst) {
  334. if (dst === undefined){
  335. this._blendFunc.src = src.src;
  336. this._blendFunc.dst = src.dst;
  337. } else{
  338. this._blendFunc.src = src;
  339. this._blendFunc.src = dst;
  340. }
  341. },
  342. /**
  343. * returns the blending function used for the texture
  344. * @return {cc.BlendFunc}
  345. */
  346. getBlendFunc:function () {
  347. return {src:this._blendFunc.src, dst:this._blendFunc.dst};
  348. },
  349. // override visit.
  350. // Don't call visit on it's children
  351. /**
  352. * Recursive method that visit its children and draw them
  353. * @function
  354. * @param {CanvasRenderingContext2D|WebGLRenderingContext} ctx
  355. */
  356. visit:function (ctx) {
  357. if (cc._renderType === cc._RENDER_TYPE_CANVAS)
  358. return;
  359. // CAREFUL:
  360. // This visit is almost identical to cc.Node#visit
  361. // with the exception that it doesn't call visit on it's children
  362. //
  363. // The alternative is to have a void cc.Sprite#visit, but
  364. // although this is less mantainable, is faster
  365. //
  366. if (!this._visible)
  367. return;
  368. cc.kmGLPushMatrix();
  369. if (this.grid && this.grid.isActive()) {
  370. this.grid.beforeDraw();
  371. this.transformAncestors();
  372. }
  373. this.transform(ctx);
  374. this.draw(ctx);
  375. if (this.grid && this.grid.isActive())
  376. this.grid.afterDraw(this);
  377. cc.kmGLPopMatrix();
  378. },
  379. _updateAllAtlasIndexes:function () {
  380. var index = 0;
  381. var locChildren = this._children;
  382. for (var i = 0; i < locChildren.length; i++) {
  383. var child = locChildren[i];
  384. child.setAtlasIndex(index);
  385. index += child.getTotalParticles();
  386. }
  387. },
  388. _increaseAtlasCapacityTo:function (quantity) {
  389. cc.log("cocos2d: cc.ParticleBatchNode: resizing TextureAtlas capacity from [" + this.textureAtlas.getCapacity()
  390. + "] to [" + quantity + "].");
  391. if (!this.textureAtlas.resizeCapacity(quantity)) {
  392. // serious problems
  393. cc.log("cc.ParticleBatchNode._increaseAtlasCapacityTo() : WARNING: Not enough memory to resize the atlas");
  394. }
  395. },
  396. _searchNewPositionInChildrenForZ:function (z) {
  397. var locChildren = this._children;
  398. var count = locChildren.length;
  399. for (var i = 0; i < count; i++) {
  400. if (locChildren[i].zIndex > z)
  401. return i;
  402. }
  403. return count;
  404. },
  405. _getCurrentIndex:function (child, z) {
  406. var foundCurrentIdx = false;
  407. var foundNewIdx = false;
  408. var newIndex = 0;
  409. var oldIndex = 0;
  410. var minusOne = 0, locChildren = this._children;
  411. var count = locChildren.length;
  412. for (var i = 0; i < count; i++) {
  413. var pNode = locChildren[i];
  414. // new index
  415. if (pNode.zIndex > z && !foundNewIdx) {
  416. newIndex = i;
  417. foundNewIdx = true;
  418. if (foundCurrentIdx && foundNewIdx)
  419. break;
  420. }
  421. // current index
  422. if (child == pNode) {
  423. oldIndex = i;
  424. foundCurrentIdx = true;
  425. if (!foundNewIdx)
  426. minusOne = -1;
  427. if (foundCurrentIdx && foundNewIdx)
  428. break;
  429. }
  430. }
  431. if (!foundNewIdx)
  432. newIndex = count;
  433. newIndex += minusOne;
  434. return {newIndex:newIndex, oldIndex:oldIndex};
  435. },
  436. //
  437. // <p>
  438. // don't use lazy sorting, reordering the particle systems quads afterwards would be too complex <br/>
  439. // XXX research whether lazy sorting + freeing current quads and calloc a new block with size of capacity would be faster <br/>
  440. // XXX or possibly using vertexZ for reordering, that would be fastest <br/>
  441. // this helper is almost equivalent to CCNode's addChild, but doesn't make use of the lazy sorting <br/>
  442. // </p>
  443. // @param {cc.ParticleSystem} child
  444. // @param {Number} z
  445. // @param {Number} aTag
  446. // @return {Number}
  447. // @private
  448. //
  449. _addChildHelper:function (child, z, aTag) {
  450. if(!child)
  451. throw "cc.ParticleBatchNode._addChildHelper(): child should be non-null";
  452. if(child.parent){
  453. cc.log("cc.ParticleBatchNode._addChildHelper(): child already added. It can't be added again");
  454. return null;
  455. }
  456. if (!this._children)
  457. this._children = [];
  458. //don't use a lazy insert
  459. var pos = this._searchNewPositionInChildrenForZ(z);
  460. this._children.splice(pos, 0, child);
  461. child.tag = aTag;
  462. child._setLocalZOrder(z);
  463. child.parent = this;
  464. if (this._running) {
  465. child.onEnter();
  466. child.onEnterTransitionDidFinish();
  467. }
  468. return pos;
  469. },
  470. _updateBlendFunc:function () {
  471. if (!this.textureAtlas.texture.hasPremultipliedAlpha()) {
  472. this._blendFunc.src = cc.SRC_ALPHA;
  473. this._blendFunc.dst = cc.ONE_MINUS_SRC_ALPHA;
  474. }
  475. },
  476. /**
  477. * return the texture atlas used for drawing the quads
  478. * @return {cc.TextureAtlas}
  479. */
  480. getTextureAtlas:function () {
  481. return this.textureAtlas;
  482. },
  483. /**
  484. * set the texture atlas used for drawing the quads
  485. * @param {cc.TextureAtlas} textureAtlas
  486. */
  487. setTextureAtlas:function (textureAtlas) {
  488. this.textureAtlas = textureAtlas;
  489. }
  490. });
  491. var _p = cc.ParticleBatchNode.prototype;
  492. // Extended properties
  493. /** @expose */
  494. _p.texture;
  495. cc.defineGetterSetter(_p, "texture", _p.getTexture, _p.setTexture);
  496. /**
  497. * initializes the particle system with the name of a file on disk (for a list of supported formats look at the cc.Texture2D class), a capacity of particles
  498. * @deprecated since v3.0 please use new cc.ParticleBatchNode(filename, capacity) instead.
  499. * @param {String|cc.Texture2D} fileImage
  500. * @param {Number} capacity
  501. * @return {cc.ParticleBatchNode}
  502. * @example
  503. * 1.
  504. * //Create a cc.ParticleBatchNode with image path and capacity
  505. * var particleBatchNode = cc.ParticleBatchNode.create("res/grossini_dance.png",30);
  506. *
  507. * 2.
  508. * //Create a cc.ParticleBatchNode with a texture and capacity
  509. * var texture = cc.TextureCache.getInstance().addImage("res/grossini_dance.png");
  510. * var particleBatchNode = cc.ParticleBatchNode.create(texture, 30);
  511. */
  512. cc.ParticleBatchNode.create = function (fileImage, capacity) {
  513. return new cc.ParticleBatchNode(fileImage, capacity);
  514. };