ImageBitmapLoader.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import { Cache } from './Cache.js';
  2. import { Loader } from './Loader.js';
  3. const _errorMap = new WeakMap();
  4. /**
  5. * A loader for loading images as an [ImageBitmap]{@link https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap}.
  6. * An `ImageBitmap` provides an asynchronous and resource efficient pathway to prepare
  7. * textures for rendering.
  8. *
  9. * Note that {@link Texture#flipY} and {@link Texture#premultiplyAlpha} are ignored with image bitmaps.
  10. * They needs these configuration on bitmap creation unlike regular images need them on uploading to GPU.
  11. *
  12. * You need to set the equivalent options via {@link ImageBitmapLoader#setOptions} instead.
  13. *
  14. * Also note that unlike {@link FileLoader}, this loader avoids multiple concurrent requests to the same URL only if `Cache` is enabled.
  15. *
  16. * ```js
  17. * const loader = new THREE.ImageBitmapLoader();
  18. * loader.setOptions( { imageOrientation: 'flipY' } ); // set options if needed
  19. * const imageBitmap = await loader.loadAsync( 'image.png' );
  20. *
  21. * const texture = new THREE.Texture( imageBitmap );
  22. * texture.needsUpdate = true;
  23. * ```
  24. *
  25. * @augments Loader
  26. */
  27. class ImageBitmapLoader extends Loader {
  28. /**
  29. * Constructs a new image bitmap loader.
  30. *
  31. * @param {LoadingManager} [manager] - The loading manager.
  32. */
  33. constructor( manager ) {
  34. super( manager );
  35. /**
  36. * This flag can be used for type testing.
  37. *
  38. * @type {boolean}
  39. * @readonly
  40. * @default true
  41. */
  42. this.isImageBitmapLoader = true;
  43. if ( typeof createImageBitmap === 'undefined' ) {
  44. console.warn( 'THREE.ImageBitmapLoader: createImageBitmap() not supported.' );
  45. }
  46. if ( typeof fetch === 'undefined' ) {
  47. console.warn( 'THREE.ImageBitmapLoader: fetch() not supported.' );
  48. }
  49. /**
  50. * Represents the loader options.
  51. *
  52. * @type {Object}
  53. * @default {premultiplyAlpha:'none'}
  54. */
  55. this.options = { premultiplyAlpha: 'none' };
  56. /**
  57. * Used for aborting requests.
  58. *
  59. * @private
  60. * @type {AbortController}
  61. */
  62. this._abortController = new AbortController();
  63. }
  64. /**
  65. * Sets the given loader options. The structure of the object must match the `options` parameter of
  66. * [createImageBitmap]{@link https://developer.mozilla.org/en-US/docs/Web/API/Window/createImageBitmap}.
  67. *
  68. * @param {Object} options - The loader options to set.
  69. * @return {ImageBitmapLoader} A reference to this image bitmap loader.
  70. */
  71. setOptions( options ) {
  72. this.options = options;
  73. return this;
  74. }
  75. /**
  76. * Starts loading from the given URL and pass the loaded image bitmap to the `onLoad()` callback.
  77. *
  78. * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI.
  79. * @param {function(ImageBitmap)} onLoad - Executed when the loading process has been finished.
  80. * @param {onProgressCallback} onProgress - Unsupported in this loader.
  81. * @param {onErrorCallback} onError - Executed when errors occur.
  82. * @return {ImageBitmap|undefined} The image bitmap.
  83. */
  84. load( url, onLoad, onProgress, onError ) {
  85. if ( url === undefined ) url = '';
  86. if ( this.path !== undefined ) url = this.path + url;
  87. url = this.manager.resolveURL( url );
  88. const scope = this;
  89. const cached = Cache.get( `image-bitmap:${url}` );
  90. if ( cached !== undefined ) {
  91. scope.manager.itemStart( url );
  92. // If cached is a promise, wait for it to resolve
  93. if ( cached.then ) {
  94. cached.then( imageBitmap => {
  95. // check if there is an error for the cached promise
  96. if ( _errorMap.has( cached ) === true ) {
  97. if ( onError ) onError( _errorMap.get( cached ) );
  98. scope.manager.itemError( url );
  99. scope.manager.itemEnd( url );
  100. } else {
  101. if ( onLoad ) onLoad( imageBitmap );
  102. scope.manager.itemEnd( url );
  103. return imageBitmap;
  104. }
  105. } );
  106. return;
  107. }
  108. // If cached is not a promise (i.e., it's already an imageBitmap)
  109. setTimeout( function () {
  110. if ( onLoad ) onLoad( cached );
  111. scope.manager.itemEnd( url );
  112. }, 0 );
  113. return cached;
  114. }
  115. const fetchOptions = {};
  116. fetchOptions.credentials = ( this.crossOrigin === 'anonymous' ) ? 'same-origin' : 'include';
  117. fetchOptions.headers = this.requestHeader;
  118. fetchOptions.signal = ( typeof AbortSignal.any === 'function' ) ? AbortSignal.any( [ this._abortController.signal, this.manager.abortController.signal ] ) : this._abortController.signal;
  119. const promise = fetch( url, fetchOptions ).then( function ( res ) {
  120. return res.blob();
  121. } ).then( function ( blob ) {
  122. return createImageBitmap( blob, Object.assign( scope.options, { colorSpaceConversion: 'none' } ) );
  123. } ).then( function ( imageBitmap ) {
  124. Cache.add( `image-bitmap:${url}`, imageBitmap );
  125. if ( onLoad ) onLoad( imageBitmap );
  126. scope.manager.itemEnd( url );
  127. return imageBitmap;
  128. } ).catch( function ( e ) {
  129. if ( onError ) onError( e );
  130. _errorMap.set( promise, e );
  131. Cache.remove( `image-bitmap:${url}` );
  132. scope.manager.itemError( url );
  133. scope.manager.itemEnd( url );
  134. } );
  135. Cache.add( `image-bitmap:${url}`, promise );
  136. scope.manager.itemStart( url );
  137. }
  138. /**
  139. * Aborts ongoing fetch requests.
  140. *
  141. * @return {ImageBitmapLoader} A reference to this instance.
  142. */
  143. abort() {
  144. this._abortController.abort();
  145. this._abortController = new AbortController();
  146. return this;
  147. }
  148. }
  149. export { ImageBitmapLoader };
粤ICP备19079148号