cesium
Version:
CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.
298 lines (270 loc) • 9.04 kB
JavaScript
import Check from "../Core/Check.js";
import defaultValue from "../Core/defaultValue.js";
import defined from "../Core/defined.js";
import loadCRN from "../Core/loadCRN.js";
import loadImageFromTypedArray from "../Core/loadImageFromTypedArray.js";
import loadKTX from "../Core/loadKTX.js";
import RuntimeError from "../Core/RuntimeError.js";
import when from "../ThirdParty/when.js";
import GltfLoaderUtil from "./GltfLoaderUtil.js";
import ResourceLoader from "./ResourceLoader.js";
import ResourceLoaderState from "./ResourceLoaderState.js";
/**
* Loads a glTF image.
* <p>
* Implements the {@link ResourceLoader} interface.
* </p>
*
* @alias GltfImageLoader
* @constructor
* @augments ResourceLoader
*
* @param {Object} options Object with the following properties:
* @param {ResourceCache} options.resourceCache The {@link ResourceCache} (to avoid circular dependencies).
* @param {Object} options.gltf The glTF JSON.
* @param {Number} options.imageId The image ID.
* @param {Resource} options.gltfResource The {@link Resource} containing the glTF.
* @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to.
* @param {SupportedImageFormats} options.supportedImageFormats The supported image formats.
* @param {String} [options.cacheKey] The cache key of the resource.
*
* @private
*/
export default function GltfImageLoader(options) {
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
var resourceCache = options.resourceCache;
var gltf = options.gltf;
var imageId = options.imageId;
var gltfResource = options.gltfResource;
var baseResource = options.baseResource;
var supportedImageFormats = options.supportedImageFormats;
var cacheKey = options.cacheKey;
//>>includeStart('debug', pragmas.debug);
Check.typeOf.func("options.resourceCache", resourceCache);
Check.typeOf.object("options.gltf", gltf);
Check.typeOf.number("options.imageId", imageId);
Check.typeOf.object("options.gltfResource", gltfResource);
Check.typeOf.object("options.baseResource", baseResource);
Check.typeOf.object("options.supportedImageFormats", supportedImageFormats);
//>>includeEnd('debug');
var results = GltfLoaderUtil.getImageUriOrBufferView({
gltf: gltf,
imageId: imageId,
supportedImageFormats: supportedImageFormats,
});
var bufferViewId = results.bufferViewId;
var uri = results.uri;
this._resourceCache = resourceCache;
this._gltfResource = gltfResource;
this._baseResource = baseResource;
this._gltf = gltf;
this._bufferViewId = bufferViewId;
this._uri = uri;
this._cacheKey = cacheKey;
this._bufferViewLoader = undefined;
this._image = undefined;
this._state = ResourceLoaderState.UNLOADED;
this._promise = when.defer();
}
if (defined(Object.create)) {
GltfImageLoader.prototype = Object.create(ResourceLoader.prototype);
GltfImageLoader.prototype.constructor = GltfImageLoader;
}
Object.defineProperties(GltfImageLoader.prototype, {
/**
* A promise that resolves to the resource when the resource is ready.
*
* @memberof GltfImageLoader.prototype
*
* @type {Promise.<GltfImageLoader>}
* @readonly
* @private
*/
promise: {
get: function () {
return this._promise.promise;
},
},
/**
* The cache key of the resource.
*
* @memberof GltfImageLoader.prototype
*
* @type {String}
* @readonly
* @private
*/
cacheKey: {
get: function () {
return this._cacheKey;
},
},
/**
* The image.
*
* @memberof GltfImageLoader.prototype
*
* @type {Image|ImageBitmap|CompressedTextureBuffer}
* @readonly
* @private
*/
image: {
get: function () {
return this._image;
},
},
});
/**
* Loads the resource.
* @private
*/
GltfImageLoader.prototype.load = function () {
if (defined(this._bufferViewId)) {
loadFromBufferView(this);
} else {
loadFromUri(this);
}
};
function loadFromBufferView(imageLoader) {
var resourceCache = imageLoader._resourceCache;
var bufferViewLoader = resourceCache.loadBufferView({
gltf: imageLoader._gltf,
bufferViewId: imageLoader._bufferViewId,
gltfResource: imageLoader._gltfResource,
baseResource: imageLoader._baseResource,
});
imageLoader._bufferViewLoader = bufferViewLoader;
imageLoader._state = ResourceLoaderState.LOADING;
bufferViewLoader.promise
.then(function () {
if (imageLoader.isDestroyed()) {
return;
}
var typedArray = bufferViewLoader.typedArray;
return loadImageFromBufferTypedArray(typedArray).then(function (image) {
if (imageLoader.isDestroyed()) {
return;
}
// Unload everything except the image
imageLoader.unload();
imageLoader._image = image;
imageLoader._state = ResourceLoaderState.READY;
imageLoader._promise.resolve(imageLoader);
});
})
.otherwise(function (error) {
if (imageLoader.isDestroyed()) {
return;
}
handleError(imageLoader, error, "Failed to load embedded image");
});
}
function loadFromUri(imageLoader) {
var baseResource = imageLoader._baseResource;
var uri = imageLoader._uri;
var resource = baseResource.getDerivedResource({
url: uri,
});
imageLoader._state = ResourceLoaderState.LOADING;
loadImageFromUri(resource)
.then(function (image) {
if (imageLoader.isDestroyed()) {
return;
}
// Unload everything except the image
imageLoader.unload();
imageLoader._image = image;
imageLoader._state = ResourceLoaderState.READY;
imageLoader._promise.resolve(imageLoader);
})
.otherwise(function (error) {
if (imageLoader.isDestroyed()) {
return;
}
handleError(imageLoader, error, "Failed to load image: " + uri);
});
}
function handleError(imageLoader, error, errorMessage) {
imageLoader.unload();
imageLoader._state = ResourceLoaderState.FAILED;
imageLoader._promise.reject(imageLoader.getError(errorMessage, error));
}
function getMimeTypeFromTypedArray(typedArray) {
var header = typedArray.subarray(0, 2);
var webpHeaderRIFFChars = typedArray.subarray(0, 4);
var webpHeaderWEBPChars = typedArray.subarray(8, 12);
if (header[0] === 0xff && header[1] === 0xd8) {
// See https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format
return "image/jpeg";
} else if (header[0] === 0x89 && header[1] === 0x50) {
// See http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html
return "image/png";
} else if (header[0] === 0xab && header[1] === 0x4b) {
// See http://github.khronos.org/KTX-Specification/#_identifier
return "image/ktx";
} else if (header[0] === 0x48 && header[1] === 0x78) {
// See https://github.com/BinomialLLC/crunch/blob/671a0648c8a440b4397f1d96ea5cf5700f830417/inc/crn_decomp.h#L268
return "image/crn";
} else if (header[0] === 0x73 && header[1] === 0x42) {
// See https://github.com/BinomialLLC/basis_universal/blob/ed135f03a05de315dd7ec7c1b8ef0589099b3e52/spec/basis_spec.txt#L125
return "image/basis";
} else if (
// See https://developers.google.com/speed/webp/docs/riff_container#webp_file_header
webpHeaderRIFFChars[0] === 0x52 &&
webpHeaderRIFFChars[1] === 0x49 &&
webpHeaderRIFFChars[2] === 0x46 &&
webpHeaderRIFFChars[3] === 0x46 &&
webpHeaderWEBPChars[0] === 0x57 &&
webpHeaderWEBPChars[1] === 0x45 &&
webpHeaderWEBPChars[2] === 0x42 &&
webpHeaderWEBPChars[3] === 0x50
) {
return "image/webp";
}
throw new RuntimeError("Image format is not recognized");
}
function loadImageFromBufferTypedArray(typedArray) {
var mimeType = getMimeTypeFromTypedArray(typedArray);
if (mimeType === "image/ktx") {
// Resolves to a CompressedTextureBuffer
return loadKTX(typedArray);
} else if (mimeType === "image/crn") {
// Resolves to a CompressedTextureBuffer
return loadCRN(typedArray);
}
// Resolves to an Image or ImageBitmap
return GltfImageLoader._loadImageFromTypedArray({
uint8Array: typedArray,
format: mimeType,
flipY: false,
});
}
var ktxRegex = /(^data:image\/ktx)|(\.ktx$)/i;
var crnRegex = /(^data:image\/crn)|(\.crn$)/i;
function loadImageFromUri(resource) {
var uri = resource.url;
if (ktxRegex.test(uri)) {
// Resolves to a CompressedTextureBuffer
return loadKTX(resource);
} else if (crnRegex.test(uri)) {
// Resolves to a CompressedTextureBuffer
return loadCRN(resource);
}
// Resolves to an ImageBitmap or Image
return resource.fetchImage();
}
/**
* Unloads the resource.
* @private
*/
GltfImageLoader.prototype.unload = function () {
if (defined(this._bufferViewLoader)) {
this._resourceCache.unload(this._bufferViewLoader);
}
this._bufferViewLoader = undefined;
this._uri = undefined; // Free in case the uri is a data uri
this._image = undefined;
this._gltf = undefined;
};
//Exposed for testing
GltfImageLoader._loadImageFromTypedArray = loadImageFromTypedArray;