Texture.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966
  1. import Cartesian2 from "../Core/Cartesian2.js";
  2. import Check from "../Core/Check.js";
  3. import createGuid from "../Core/createGuid.js";
  4. import defaultValue from "../Core/defaultValue.js";
  5. import defined from "../Core/defined.js";
  6. import destroyObject from "../Core/destroyObject.js";
  7. import DeveloperError from "../Core/DeveloperError.js";
  8. import CesiumMath from "../Core/Math.js";
  9. import PixelFormat from "../Core/PixelFormat.js";
  10. import ContextLimits from "./ContextLimits.js";
  11. import MipmapHint from "./MipmapHint.js";
  12. import PixelDatatype from "./PixelDatatype.js";
  13. import Sampler from "./Sampler.js";
  14. import TextureMagnificationFilter from "./TextureMagnificationFilter.js";
  15. import TextureMinificationFilter from "./TextureMinificationFilter.js";
  16. /**
  17. * @private
  18. */
  19. function Texture(options) {
  20. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  21. //>>includeStart('debug', pragmas.debug);
  22. Check.defined("options.context", options.context);
  23. //>>includeEnd('debug');
  24. var context = options.context;
  25. var width = options.width;
  26. var height = options.height;
  27. var source = options.source;
  28. if (defined(source)) {
  29. if (!defined(width)) {
  30. width = defaultValue(source.videoWidth, source.width);
  31. }
  32. if (!defined(height)) {
  33. height = defaultValue(source.videoHeight, source.height);
  34. }
  35. }
  36. var pixelFormat = defaultValue(options.pixelFormat, PixelFormat.RGBA);
  37. var pixelDatatype = defaultValue(
  38. options.pixelDatatype,
  39. PixelDatatype.UNSIGNED_BYTE
  40. );
  41. var internalFormat = PixelFormat.toInternalFormat(
  42. pixelFormat,
  43. pixelDatatype,
  44. context
  45. );
  46. var isCompressed = PixelFormat.isCompressedFormat(internalFormat);
  47. //>>includeStart('debug', pragmas.debug);
  48. if (!defined(width) || !defined(height)) {
  49. throw new DeveloperError(
  50. "options requires a source field to create an initialized texture or width and height fields to create a blank texture."
  51. );
  52. }
  53. Check.typeOf.number.greaterThan("width", width, 0);
  54. if (width > ContextLimits.maximumTextureSize) {
  55. throw new DeveloperError(
  56. "Width must be less than or equal to the maximum texture size (" +
  57. ContextLimits.maximumTextureSize +
  58. "). Check maximumTextureSize."
  59. );
  60. }
  61. Check.typeOf.number.greaterThan("height", height, 0);
  62. if (height > ContextLimits.maximumTextureSize) {
  63. throw new DeveloperError(
  64. "Height must be less than or equal to the maximum texture size (" +
  65. ContextLimits.maximumTextureSize +
  66. "). Check maximumTextureSize."
  67. );
  68. }
  69. if (!PixelFormat.validate(pixelFormat)) {
  70. throw new DeveloperError("Invalid options.pixelFormat.");
  71. }
  72. if (!isCompressed && !PixelDatatype.validate(pixelDatatype)) {
  73. throw new DeveloperError("Invalid options.pixelDatatype.");
  74. }
  75. if (
  76. pixelFormat === PixelFormat.DEPTH_COMPONENT &&
  77. pixelDatatype !== PixelDatatype.UNSIGNED_SHORT &&
  78. pixelDatatype !== PixelDatatype.UNSIGNED_INT
  79. ) {
  80. throw new DeveloperError(
  81. "When options.pixelFormat is DEPTH_COMPONENT, options.pixelDatatype must be UNSIGNED_SHORT or UNSIGNED_INT."
  82. );
  83. }
  84. if (
  85. pixelFormat === PixelFormat.DEPTH_STENCIL &&
  86. pixelDatatype !== PixelDatatype.UNSIGNED_INT_24_8
  87. ) {
  88. throw new DeveloperError(
  89. "When options.pixelFormat is DEPTH_STENCIL, options.pixelDatatype must be UNSIGNED_INT_24_8."
  90. );
  91. }
  92. if (pixelDatatype === PixelDatatype.FLOAT && !context.floatingPointTexture) {
  93. throw new DeveloperError(
  94. "When options.pixelDatatype is FLOAT, this WebGL implementation must support the OES_texture_float extension. Check context.floatingPointTexture."
  95. );
  96. }
  97. if (
  98. pixelDatatype === PixelDatatype.HALF_FLOAT &&
  99. !context.halfFloatingPointTexture
  100. ) {
  101. throw new DeveloperError(
  102. "When options.pixelDatatype is HALF_FLOAT, this WebGL implementation must support the OES_texture_half_float extension. Check context.halfFloatingPointTexture."
  103. );
  104. }
  105. if (PixelFormat.isDepthFormat(pixelFormat)) {
  106. if (defined(source)) {
  107. throw new DeveloperError(
  108. "When options.pixelFormat is DEPTH_COMPONENT or DEPTH_STENCIL, source cannot be provided."
  109. );
  110. }
  111. if (!context.depthTexture) {
  112. throw new DeveloperError(
  113. "When options.pixelFormat is DEPTH_COMPONENT or DEPTH_STENCIL, this WebGL implementation must support WEBGL_depth_texture. Check context.depthTexture."
  114. );
  115. }
  116. }
  117. if (isCompressed) {
  118. if (!defined(source) || !defined(source.arrayBufferView)) {
  119. throw new DeveloperError(
  120. "When options.pixelFormat is compressed, options.source.arrayBufferView must be defined."
  121. );
  122. }
  123. if (PixelFormat.isDXTFormat(internalFormat) && !context.s3tc) {
  124. throw new DeveloperError(
  125. "When options.pixelFormat is S3TC compressed, this WebGL implementation must support the WEBGL_texture_compression_s3tc extension. Check context.s3tc."
  126. );
  127. } else if (PixelFormat.isPVRTCFormat(internalFormat) && !context.pvrtc) {
  128. throw new DeveloperError(
  129. "When options.pixelFormat is PVRTC compressed, this WebGL implementation must support the WEBGL_texture_compression_pvrtc extension. Check context.pvrtc."
  130. );
  131. } else if (PixelFormat.isETC1Format(internalFormat) && !context.etc1) {
  132. throw new DeveloperError(
  133. "When options.pixelFormat is ETC1 compressed, this WebGL implementation must support the WEBGL_texture_compression_etc1 extension. Check context.etc1."
  134. );
  135. }
  136. if (
  137. PixelFormat.compressedTextureSizeInBytes(
  138. internalFormat,
  139. width,
  140. height
  141. ) !== source.arrayBufferView.byteLength
  142. ) {
  143. throw new DeveloperError(
  144. "The byte length of the array buffer is invalid for the compressed texture with the given width and height."
  145. );
  146. }
  147. }
  148. //>>includeEnd('debug');
  149. // Use premultiplied alpha for opaque textures should perform better on Chrome:
  150. // http://media.tojicode.com/webglCamp4/#20
  151. var preMultiplyAlpha =
  152. options.preMultiplyAlpha ||
  153. pixelFormat === PixelFormat.RGB ||
  154. pixelFormat === PixelFormat.LUMINANCE;
  155. var flipY = defaultValue(options.flipY, true);
  156. var initialized = true;
  157. var gl = context._gl;
  158. var textureTarget = gl.TEXTURE_2D;
  159. var texture = gl.createTexture();
  160. gl.activeTexture(gl.TEXTURE0);
  161. gl.bindTexture(textureTarget, texture);
  162. var unpackAlignment = 4;
  163. if (defined(source) && defined(source.arrayBufferView) && !isCompressed) {
  164. unpackAlignment = PixelFormat.alignmentInBytes(
  165. pixelFormat,
  166. pixelDatatype,
  167. width
  168. );
  169. }
  170. gl.pixelStorei(gl.UNPACK_ALIGNMENT, unpackAlignment);
  171. if (defined(source)) {
  172. if (defined(source.arrayBufferView)) {
  173. gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
  174. gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
  175. // Source: typed array
  176. var arrayBufferView = source.arrayBufferView;
  177. if (isCompressed) {
  178. gl.compressedTexImage2D(
  179. textureTarget,
  180. 0,
  181. internalFormat,
  182. width,
  183. height,
  184. 0,
  185. arrayBufferView
  186. );
  187. } else {
  188. if (flipY) {
  189. arrayBufferView = PixelFormat.flipY(
  190. arrayBufferView,
  191. pixelFormat,
  192. pixelDatatype,
  193. width,
  194. height
  195. );
  196. }
  197. gl.texImage2D(
  198. textureTarget,
  199. 0,
  200. internalFormat,
  201. width,
  202. height,
  203. 0,
  204. pixelFormat,
  205. PixelDatatype.toWebGLConstant(pixelDatatype, context),
  206. arrayBufferView
  207. );
  208. if (defined(source.mipLevels)) {
  209. var mipWidth = width;
  210. var mipHeight = height;
  211. for (var i = 0; i < source.mipLevels.length; ++i) {
  212. mipWidth = Math.floor(mipWidth / 2) | 0;
  213. if (mipWidth < 1) {
  214. mipWidth = 1;
  215. }
  216. mipHeight = Math.floor(mipHeight / 2) | 0;
  217. if (mipHeight < 1) {
  218. mipHeight = 1;
  219. }
  220. gl.texImage2D(
  221. textureTarget,
  222. i + 1,
  223. internalFormat,
  224. mipWidth,
  225. mipHeight,
  226. 0,
  227. pixelFormat,
  228. PixelDatatype.toWebGLConstant(pixelDatatype, context),
  229. source.mipLevels[i]
  230. );
  231. }
  232. }
  233. }
  234. } else if (defined(source.framebuffer)) {
  235. gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
  236. gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
  237. // Source: framebuffer
  238. if (source.framebuffer !== context.defaultFramebuffer) {
  239. source.framebuffer._bind();
  240. }
  241. gl.copyTexImage2D(
  242. textureTarget,
  243. 0,
  244. internalFormat,
  245. source.xOffset,
  246. source.yOffset,
  247. width,
  248. height,
  249. 0
  250. );
  251. if (source.framebuffer !== context.defaultFramebuffer) {
  252. source.framebuffer._unBind();
  253. }
  254. } else {
  255. // Only valid for DOM-Element uploads
  256. gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha);
  257. gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
  258. // Source: ImageData, HTMLImageElement, HTMLCanvasElement, or HTMLVideoElement
  259. gl.texImage2D(
  260. textureTarget,
  261. 0,
  262. internalFormat,
  263. pixelFormat,
  264. PixelDatatype.toWebGLConstant(pixelDatatype, context),
  265. source
  266. );
  267. }
  268. } else {
  269. gl.texImage2D(
  270. textureTarget,
  271. 0,
  272. internalFormat,
  273. width,
  274. height,
  275. 0,
  276. pixelFormat,
  277. PixelDatatype.toWebGLConstant(pixelDatatype, context),
  278. null
  279. );
  280. initialized = false;
  281. }
  282. gl.bindTexture(textureTarget, null);
  283. var sizeInBytes;
  284. if (isCompressed) {
  285. sizeInBytes = PixelFormat.compressedTextureSizeInBytes(
  286. pixelFormat,
  287. width,
  288. height
  289. );
  290. } else {
  291. sizeInBytes = PixelFormat.textureSizeInBytes(
  292. pixelFormat,
  293. pixelDatatype,
  294. width,
  295. height
  296. );
  297. }
  298. this._id = createGuid();
  299. this._context = context;
  300. this._textureFilterAnisotropic = context._textureFilterAnisotropic;
  301. this._textureTarget = textureTarget;
  302. this._texture = texture;
  303. this._internalFormat = internalFormat;
  304. this._pixelFormat = pixelFormat;
  305. this._pixelDatatype = pixelDatatype;
  306. this._width = width;
  307. this._height = height;
  308. this._dimensions = new Cartesian2(width, height);
  309. this._hasMipmap = false;
  310. this._sizeInBytes = sizeInBytes;
  311. this._preMultiplyAlpha = preMultiplyAlpha;
  312. this._flipY = flipY;
  313. this._initialized = initialized;
  314. this._sampler = undefined;
  315. this.sampler = defined(options.sampler) ? options.sampler : new Sampler();
  316. }
  317. /**
  318. * This function is identical to using the Texture constructor except that it can be
  319. * replaced with a mock/spy in tests.
  320. * @private
  321. */
  322. Texture.create = function (options) {
  323. return new Texture(options);
  324. };
  325. /**
  326. * Creates a texture, and copies a subimage of the framebuffer to it. When called without arguments,
  327. * the texture is the same width and height as the framebuffer and contains its contents.
  328. *
  329. * @param {Object} options Object with the following properties:
  330. * @param {Context} options.context The context in which the Texture gets created.
  331. * @param {PixelFormat} [options.pixelFormat=PixelFormat.RGB] The texture's internal pixel format.
  332. * @param {Number} [options.framebufferXOffset=0] An offset in the x direction in the framebuffer where copying begins from.
  333. * @param {Number} [options.framebufferYOffset=0] An offset in the y direction in the framebuffer where copying begins from.
  334. * @param {Number} [options.width=canvas.clientWidth] The width of the texture in texels.
  335. * @param {Number} [options.height=canvas.clientHeight] The height of the texture in texels.
  336. * @param {Framebuffer} [options.framebuffer=defaultFramebuffer] The framebuffer from which to create the texture. If this
  337. * parameter is not specified, the default framebuffer is used.
  338. * @returns {Texture} A texture with contents from the framebuffer.
  339. *
  340. * @exception {DeveloperError} Invalid pixelFormat.
  341. * @exception {DeveloperError} pixelFormat cannot be DEPTH_COMPONENT, DEPTH_STENCIL or a compressed format.
  342. * @exception {DeveloperError} framebufferXOffset must be greater than or equal to zero.
  343. * @exception {DeveloperError} framebufferYOffset must be greater than or equal to zero.
  344. * @exception {DeveloperError} framebufferXOffset + width must be less than or equal to canvas.clientWidth.
  345. * @exception {DeveloperError} framebufferYOffset + height must be less than or equal to canvas.clientHeight.
  346. *
  347. *
  348. * @example
  349. * // Create a texture with the contents of the framebuffer.
  350. * var t = Texture.fromFramebuffer({
  351. * context : context
  352. * });
  353. *
  354. * @see Sampler
  355. *
  356. * @private
  357. */
  358. Texture.fromFramebuffer = function (options) {
  359. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  360. //>>includeStart('debug', pragmas.debug);
  361. Check.defined("options.context", options.context);
  362. //>>includeEnd('debug');
  363. var context = options.context;
  364. var gl = context._gl;
  365. var pixelFormat = defaultValue(options.pixelFormat, PixelFormat.RGB);
  366. var framebufferXOffset = defaultValue(options.framebufferXOffset, 0);
  367. var framebufferYOffset = defaultValue(options.framebufferYOffset, 0);
  368. var width = defaultValue(options.width, gl.drawingBufferWidth);
  369. var height = defaultValue(options.height, gl.drawingBufferHeight);
  370. var framebuffer = options.framebuffer;
  371. //>>includeStart('debug', pragmas.debug);
  372. if (!PixelFormat.validate(pixelFormat)) {
  373. throw new DeveloperError("Invalid pixelFormat.");
  374. }
  375. if (
  376. PixelFormat.isDepthFormat(pixelFormat) ||
  377. PixelFormat.isCompressedFormat(pixelFormat)
  378. ) {
  379. throw new DeveloperError(
  380. "pixelFormat cannot be DEPTH_COMPONENT, DEPTH_STENCIL or a compressed format."
  381. );
  382. }
  383. Check.defined("options.context", options.context);
  384. Check.typeOf.number.greaterThanOrEquals(
  385. "framebufferXOffset",
  386. framebufferXOffset,
  387. 0
  388. );
  389. Check.typeOf.number.greaterThanOrEquals(
  390. "framebufferYOffset",
  391. framebufferYOffset,
  392. 0
  393. );
  394. if (framebufferXOffset + width > gl.drawingBufferWidth) {
  395. throw new DeveloperError(
  396. "framebufferXOffset + width must be less than or equal to drawingBufferWidth"
  397. );
  398. }
  399. if (framebufferYOffset + height > gl.drawingBufferHeight) {
  400. throw new DeveloperError(
  401. "framebufferYOffset + height must be less than or equal to drawingBufferHeight."
  402. );
  403. }
  404. //>>includeEnd('debug');
  405. var texture = new Texture({
  406. context: context,
  407. width: width,
  408. height: height,
  409. pixelFormat: pixelFormat,
  410. source: {
  411. framebuffer: defined(framebuffer)
  412. ? framebuffer
  413. : context.defaultFramebuffer,
  414. xOffset: framebufferXOffset,
  415. yOffset: framebufferYOffset,
  416. width: width,
  417. height: height,
  418. },
  419. });
  420. return texture;
  421. };
  422. Object.defineProperties(Texture.prototype, {
  423. /**
  424. * A unique id for the texture
  425. * @memberof Texture.prototype
  426. * @type {String}
  427. * @readonly
  428. * @private
  429. */
  430. id: {
  431. get: function () {
  432. return this._id;
  433. },
  434. },
  435. /**
  436. * The sampler to use when sampling this texture.
  437. * Create a sampler by calling {@link Sampler}. If this
  438. * parameter is not specified, a default sampler is used. The default sampler clamps texture
  439. * coordinates in both directions, uses linear filtering for both magnification and minification,
  440. * and uses a maximum anisotropy of 1.0.
  441. * @memberof Texture.prototype
  442. * @type {Object}
  443. */
  444. sampler: {
  445. get: function () {
  446. return this._sampler;
  447. },
  448. set: function (sampler) {
  449. var minificationFilter = sampler.minificationFilter;
  450. var magnificationFilter = sampler.magnificationFilter;
  451. var context = this._context;
  452. var pixelFormat = this._pixelFormat;
  453. var pixelDatatype = this._pixelDatatype;
  454. var mipmap =
  455. minificationFilter ===
  456. TextureMinificationFilter.NEAREST_MIPMAP_NEAREST ||
  457. minificationFilter ===
  458. TextureMinificationFilter.NEAREST_MIPMAP_LINEAR ||
  459. minificationFilter ===
  460. TextureMinificationFilter.LINEAR_MIPMAP_NEAREST ||
  461. minificationFilter === TextureMinificationFilter.LINEAR_MIPMAP_LINEAR;
  462. // float textures only support nearest filtering unless the linear extensions are supported, so override the sampler's settings
  463. if (
  464. (pixelDatatype === PixelDatatype.FLOAT &&
  465. !context.textureFloatLinear) ||
  466. (pixelDatatype === PixelDatatype.HALF_FLOAT &&
  467. !context.textureHalfFloatLinear)
  468. ) {
  469. minificationFilter = mipmap
  470. ? TextureMinificationFilter.NEAREST_MIPMAP_NEAREST
  471. : TextureMinificationFilter.NEAREST;
  472. magnificationFilter = TextureMagnificationFilter.NEAREST;
  473. }
  474. // WebGL 2 depth texture only support nearest filtering. See section 3.8.13 OpenGL ES 3 spec
  475. if (context.webgl2) {
  476. if (PixelFormat.isDepthFormat(pixelFormat)) {
  477. minificationFilter = TextureMinificationFilter.NEAREST;
  478. magnificationFilter = TextureMagnificationFilter.NEAREST;
  479. }
  480. }
  481. var gl = context._gl;
  482. var target = this._textureTarget;
  483. gl.activeTexture(gl.TEXTURE0);
  484. gl.bindTexture(target, this._texture);
  485. gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, minificationFilter);
  486. gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, magnificationFilter);
  487. gl.texParameteri(target, gl.TEXTURE_WRAP_S, sampler.wrapS);
  488. gl.texParameteri(target, gl.TEXTURE_WRAP_T, sampler.wrapT);
  489. if (defined(this._textureFilterAnisotropic)) {
  490. gl.texParameteri(
  491. target,
  492. this._textureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT,
  493. sampler.maximumAnisotropy
  494. );
  495. }
  496. gl.bindTexture(target, null);
  497. this._sampler = sampler;
  498. },
  499. },
  500. pixelFormat: {
  501. get: function () {
  502. return this._pixelFormat;
  503. },
  504. },
  505. pixelDatatype: {
  506. get: function () {
  507. return this._pixelDatatype;
  508. },
  509. },
  510. dimensions: {
  511. get: function () {
  512. return this._dimensions;
  513. },
  514. },
  515. preMultiplyAlpha: {
  516. get: function () {
  517. return this._preMultiplyAlpha;
  518. },
  519. },
  520. flipY: {
  521. get: function () {
  522. return this._flipY;
  523. },
  524. },
  525. width: {
  526. get: function () {
  527. return this._width;
  528. },
  529. },
  530. height: {
  531. get: function () {
  532. return this._height;
  533. },
  534. },
  535. sizeInBytes: {
  536. get: function () {
  537. if (this._hasMipmap) {
  538. return Math.floor((this._sizeInBytes * 4) / 3);
  539. }
  540. return this._sizeInBytes;
  541. },
  542. },
  543. _target: {
  544. get: function () {
  545. return this._textureTarget;
  546. },
  547. },
  548. });
  549. /**
  550. * Copy new image data into this texture, from a source {@link ImageData}, {@link HTMLImageElement}, {@link HTMLCanvasElement}, or {@link HTMLVideoElement}.
  551. * or an object with width, height, and arrayBufferView properties.
  552. *
  553. * @param {Object} source The source {@link ImageData}, {@link HTMLImageElement}, {@link HTMLCanvasElement}, or {@link HTMLVideoElement},
  554. * or an object with width, height, and arrayBufferView properties.
  555. * @param {Number} [xOffset=0] The offset in the x direction within the texture to copy into.
  556. * @param {Number} [yOffset=0] The offset in the y direction within the texture to copy into.
  557. *
  558. * @exception {DeveloperError} Cannot call copyFrom when the texture pixel format is DEPTH_COMPONENT or DEPTH_STENCIL.
  559. * @exception {DeveloperError} Cannot call copyFrom with a compressed texture pixel format.
  560. * @exception {DeveloperError} xOffset must be greater than or equal to zero.
  561. * @exception {DeveloperError} yOffset must be greater than or equal to zero.
  562. * @exception {DeveloperError} xOffset + source.width must be less than or equal to width.
  563. * @exception {DeveloperError} yOffset + source.height must be less than or equal to height.
  564. * @exception {DeveloperError} This texture was destroyed, i.e., destroy() was called.
  565. *
  566. * @example
  567. * texture.copyFrom({
  568. * width : 1,
  569. * height : 1,
  570. * arrayBufferView : new Uint8Array([255, 0, 0, 255])
  571. * });
  572. */
  573. Texture.prototype.copyFrom = function (source, xOffset, yOffset) {
  574. xOffset = defaultValue(xOffset, 0);
  575. yOffset = defaultValue(yOffset, 0);
  576. //>>includeStart('debug', pragmas.debug);
  577. Check.defined("source", source);
  578. if (PixelFormat.isDepthFormat(this._pixelFormat)) {
  579. throw new DeveloperError(
  580. "Cannot call copyFrom when the texture pixel format is DEPTH_COMPONENT or DEPTH_STENCIL."
  581. );
  582. }
  583. if (PixelFormat.isCompressedFormat(this._pixelFormat)) {
  584. throw new DeveloperError(
  585. "Cannot call copyFrom with a compressed texture pixel format."
  586. );
  587. }
  588. Check.typeOf.number.greaterThanOrEquals("xOffset", xOffset, 0);
  589. Check.typeOf.number.greaterThanOrEquals("yOffset", yOffset, 0);
  590. Check.typeOf.number.lessThanOrEquals(
  591. "xOffset + source.width",
  592. xOffset + source.width,
  593. this._width
  594. );
  595. Check.typeOf.number.lessThanOrEquals(
  596. "yOffset + source.height",
  597. yOffset + source.height,
  598. this._height
  599. );
  600. //>>includeEnd('debug');
  601. var context = this._context;
  602. var gl = context._gl;
  603. var target = this._textureTarget;
  604. gl.activeTexture(gl.TEXTURE0);
  605. gl.bindTexture(target, this._texture);
  606. var width = source.width;
  607. var height = source.height;
  608. var arrayBufferView = source.arrayBufferView;
  609. var textureWidth = this._width;
  610. var textureHeight = this._height;
  611. var internalFormat = this._internalFormat;
  612. var pixelFormat = this._pixelFormat;
  613. var pixelDatatype = this._pixelDatatype;
  614. var preMultiplyAlpha = this._preMultiplyAlpha;
  615. var flipY = this._flipY;
  616. var unpackAlignment = 4;
  617. if (defined(arrayBufferView)) {
  618. unpackAlignment = PixelFormat.alignmentInBytes(
  619. pixelFormat,
  620. pixelDatatype,
  621. width
  622. );
  623. }
  624. gl.pixelStorei(gl.UNPACK_ALIGNMENT, unpackAlignment);
  625. var uploaded = false;
  626. if (!this._initialized) {
  627. if (
  628. xOffset === 0 &&
  629. yOffset === 0 &&
  630. width === textureWidth &&
  631. height === textureHeight
  632. ) {
  633. // initialize the entire texture
  634. if (defined(arrayBufferView)) {
  635. gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
  636. gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
  637. if (flipY) {
  638. arrayBufferView = PixelFormat.flipY(
  639. arrayBufferView,
  640. pixelFormat,
  641. pixelDatatype,
  642. textureWidth,
  643. textureHeight
  644. );
  645. }
  646. gl.texImage2D(
  647. target,
  648. 0,
  649. internalFormat,
  650. textureWidth,
  651. textureHeight,
  652. 0,
  653. pixelFormat,
  654. PixelDatatype.toWebGLConstant(pixelDatatype, context),
  655. arrayBufferView
  656. );
  657. } else {
  658. // Only valid for DOM-Element uploads
  659. gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha);
  660. gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
  661. gl.texImage2D(
  662. target,
  663. 0,
  664. internalFormat,
  665. pixelFormat,
  666. PixelDatatype.toWebGLConstant(pixelDatatype, context),
  667. source
  668. );
  669. }
  670. uploaded = true;
  671. } else {
  672. gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
  673. gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
  674. // initialize the entire texture to zero
  675. var bufferView = PixelFormat.createTypedArray(
  676. pixelFormat,
  677. pixelDatatype,
  678. textureWidth,
  679. textureHeight
  680. );
  681. gl.texImage2D(
  682. target,
  683. 0,
  684. internalFormat,
  685. textureWidth,
  686. textureHeight,
  687. 0,
  688. pixelFormat,
  689. PixelDatatype.toWebGLConstant(pixelDatatype, context),
  690. bufferView
  691. );
  692. }
  693. this._initialized = true;
  694. }
  695. if (!uploaded) {
  696. if (defined(arrayBufferView)) {
  697. gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
  698. gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
  699. if (flipY) {
  700. arrayBufferView = PixelFormat.flipY(
  701. arrayBufferView,
  702. pixelFormat,
  703. pixelDatatype,
  704. width,
  705. height
  706. );
  707. }
  708. gl.texSubImage2D(
  709. target,
  710. 0,
  711. xOffset,
  712. yOffset,
  713. width,
  714. height,
  715. pixelFormat,
  716. PixelDatatype.toWebGLConstant(pixelDatatype, context),
  717. arrayBufferView
  718. );
  719. } else {
  720. // Only valid for DOM-Element uploads
  721. gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha);
  722. gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
  723. gl.texSubImage2D(
  724. target,
  725. 0,
  726. xOffset,
  727. yOffset,
  728. pixelFormat,
  729. PixelDatatype.toWebGLConstant(pixelDatatype, context),
  730. source
  731. );
  732. }
  733. }
  734. gl.bindTexture(target, null);
  735. };
  736. /**
  737. * @param {Number} [xOffset=0] The offset in the x direction within the texture to copy into.
  738. * @param {Number} [yOffset=0] The offset in the y direction within the texture to copy into.
  739. * @param {Number} [framebufferXOffset=0] optional
  740. * @param {Number} [framebufferYOffset=0] optional
  741. * @param {Number} [width=width] optional
  742. * @param {Number} [height=height] optional
  743. *
  744. * @exception {DeveloperError} Cannot call copyFromFramebuffer when the texture pixel format is DEPTH_COMPONENT or DEPTH_STENCIL.
  745. * @exception {DeveloperError} Cannot call copyFromFramebuffer when the texture pixel data type is FLOAT.
  746. * @exception {DeveloperError} Cannot call copyFromFramebuffer when the texture pixel data type is HALF_FLOAT.
  747. * @exception {DeveloperError} Cannot call copyFrom with a compressed texture pixel format.
  748. * @exception {DeveloperError} This texture was destroyed, i.e., destroy() was called.
  749. * @exception {DeveloperError} xOffset must be greater than or equal to zero.
  750. * @exception {DeveloperError} yOffset must be greater than or equal to zero.
  751. * @exception {DeveloperError} framebufferXOffset must be greater than or equal to zero.
  752. * @exception {DeveloperError} framebufferYOffset must be greater than or equal to zero.
  753. * @exception {DeveloperError} xOffset + width must be less than or equal to width.
  754. * @exception {DeveloperError} yOffset + height must be less than or equal to height.
  755. */
  756. Texture.prototype.copyFromFramebuffer = function (
  757. xOffset,
  758. yOffset,
  759. framebufferXOffset,
  760. framebufferYOffset,
  761. width,
  762. height
  763. ) {
  764. xOffset = defaultValue(xOffset, 0);
  765. yOffset = defaultValue(yOffset, 0);
  766. framebufferXOffset = defaultValue(framebufferXOffset, 0);
  767. framebufferYOffset = defaultValue(framebufferYOffset, 0);
  768. width = defaultValue(width, this._width);
  769. height = defaultValue(height, this._height);
  770. //>>includeStart('debug', pragmas.debug);
  771. if (PixelFormat.isDepthFormat(this._pixelFormat)) {
  772. throw new DeveloperError(
  773. "Cannot call copyFromFramebuffer when the texture pixel format is DEPTH_COMPONENT or DEPTH_STENCIL."
  774. );
  775. }
  776. if (this._pixelDatatype === PixelDatatype.FLOAT) {
  777. throw new DeveloperError(
  778. "Cannot call copyFromFramebuffer when the texture pixel data type is FLOAT."
  779. );
  780. }
  781. if (this._pixelDatatype === PixelDatatype.HALF_FLOAT) {
  782. throw new DeveloperError(
  783. "Cannot call copyFromFramebuffer when the texture pixel data type is HALF_FLOAT."
  784. );
  785. }
  786. if (PixelFormat.isCompressedFormat(this._pixelFormat)) {
  787. throw new DeveloperError(
  788. "Cannot call copyFrom with a compressed texture pixel format."
  789. );
  790. }
  791. Check.typeOf.number.greaterThanOrEquals("xOffset", xOffset, 0);
  792. Check.typeOf.number.greaterThanOrEquals("yOffset", yOffset, 0);
  793. Check.typeOf.number.greaterThanOrEquals(
  794. "framebufferXOffset",
  795. framebufferXOffset,
  796. 0
  797. );
  798. Check.typeOf.number.greaterThanOrEquals(
  799. "framebufferYOffset",
  800. framebufferYOffset,
  801. 0
  802. );
  803. Check.typeOf.number.lessThanOrEquals(
  804. "xOffset + width",
  805. xOffset + width,
  806. this._width
  807. );
  808. Check.typeOf.number.lessThanOrEquals(
  809. "yOffset + height",
  810. yOffset + height,
  811. this._height
  812. );
  813. //>>includeEnd('debug');
  814. var gl = this._context._gl;
  815. var target = this._textureTarget;
  816. gl.activeTexture(gl.TEXTURE0);
  817. gl.bindTexture(target, this._texture);
  818. gl.copyTexSubImage2D(
  819. target,
  820. 0,
  821. xOffset,
  822. yOffset,
  823. framebufferXOffset,
  824. framebufferYOffset,
  825. width,
  826. height
  827. );
  828. gl.bindTexture(target, null);
  829. this._initialized = true;
  830. };
  831. /**
  832. * @param {MipmapHint} [hint=MipmapHint.DONT_CARE] optional.
  833. *
  834. * @exception {DeveloperError} Cannot call generateMipmap when the texture pixel format is DEPTH_COMPONENT or DEPTH_STENCIL.
  835. * @exception {DeveloperError} Cannot call generateMipmap when the texture pixel format is a compressed format.
  836. * @exception {DeveloperError} hint is invalid.
  837. * @exception {DeveloperError} This texture's width must be a power of two to call generateMipmap().
  838. * @exception {DeveloperError} This texture's height must be a power of two to call generateMipmap().
  839. * @exception {DeveloperError} This texture was destroyed, i.e., destroy() was called.
  840. */
  841. Texture.prototype.generateMipmap = function (hint) {
  842. hint = defaultValue(hint, MipmapHint.DONT_CARE);
  843. //>>includeStart('debug', pragmas.debug);
  844. if (PixelFormat.isDepthFormat(this._pixelFormat)) {
  845. throw new DeveloperError(
  846. "Cannot call generateMipmap when the texture pixel format is DEPTH_COMPONENT or DEPTH_STENCIL."
  847. );
  848. }
  849. if (PixelFormat.isCompressedFormat(this._pixelFormat)) {
  850. throw new DeveloperError(
  851. "Cannot call generateMipmap with a compressed pixel format."
  852. );
  853. }
  854. if (this._width > 1 && !CesiumMath.isPowerOfTwo(this._width)) {
  855. throw new DeveloperError(
  856. "width must be a power of two to call generateMipmap()."
  857. );
  858. }
  859. if (this._height > 1 && !CesiumMath.isPowerOfTwo(this._height)) {
  860. throw new DeveloperError(
  861. "height must be a power of two to call generateMipmap()."
  862. );
  863. }
  864. if (!MipmapHint.validate(hint)) {
  865. throw new DeveloperError("hint is invalid.");
  866. }
  867. //>>includeEnd('debug');
  868. this._hasMipmap = true;
  869. var gl = this._context._gl;
  870. var target = this._textureTarget;
  871. gl.hint(gl.GENERATE_MIPMAP_HINT, hint);
  872. gl.activeTexture(gl.TEXTURE0);
  873. gl.bindTexture(target, this._texture);
  874. gl.generateMipmap(target);
  875. gl.bindTexture(target, null);
  876. };
  877. Texture.prototype.isDestroyed = function () {
  878. return false;
  879. };
  880. Texture.prototype.destroy = function () {
  881. this._context._gl.deleteTexture(this._texture);
  882. return destroyObject(this);
  883. };
  884. export default Texture;