import {
|
BufferLoader,
|
clone,
|
CompressedTextureBuffer,
|
GltfBufferViewLoader,
|
GltfImageLoader,
|
FeatureDetection,
|
Resource,
|
ResourceCache,
|
when,
|
} from "../../Source/Cesium.js";
|
import createContext from "../createContext.js";
|
import dataUriToBuffer from "../dataUriToBuffer.js";
|
import pollToPromise from "../pollToPromise.js";
|
|
describe("Scene/GltfImageLoader", function () {
|
var image = new Image();
|
image.src =
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=";
|
|
var pngBuffer = dataUriToBuffer(
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII="
|
);
|
var jpgBuffer = dataUriToBuffer(
|
"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAP//////////////////////////////////////////////////////////////////////////////////////wAALCAABAAEBAREA/8QAJgABAAAAAAAAAAAAAAAAAAAAAxABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQAAPwBH/9k"
|
);
|
|
var webpBuffer = dataUriToBuffer(
|
"data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA"
|
);
|
|
var gifBuffer = dataUriToBuffer(
|
"data:image/gif;base64,R0lGODdhBAAEAIAAAP///////ywAAAAABAAEAAACBISPCQUAOw=="
|
);
|
|
var ktx2BasisBuffer;
|
var ktx2BasisMipmapBuffer;
|
|
var gltfUri = "https://example.com/model.glb";
|
var gltfResource = new Resource({
|
url: gltfUri,
|
});
|
|
var gltf = {
|
buffers: [
|
{
|
uri: "external.bin",
|
byteLength: 0, // updated in getGltf
|
},
|
],
|
bufferViews: [
|
{
|
buffer: 0,
|
byteOffset: 0,
|
byteLength: 0, // updated in getGltf
|
},
|
],
|
images: [
|
{
|
mimeType: "image/png",
|
bufferView: 0,
|
},
|
{
|
uri: "image.png",
|
},
|
{
|
mimeType: "image/ktx2",
|
bufferView: 0,
|
},
|
{
|
uri: "image.ktx2",
|
},
|
],
|
};
|
|
var context;
|
|
function getGltf(imageBuffer) {
|
var clonedGltf = clone(gltf, true);
|
clonedGltf.buffers[0].byteLength = imageBuffer.byteLength;
|
clonedGltf.bufferViews[0].byteLength = imageBuffer.byteLength;
|
return clonedGltf;
|
}
|
|
beforeAll(function () {
|
context = createContext();
|
var ktx2BasisBufferPromise = Resource.fetchArrayBuffer({
|
url: "./Data/Images/Green4x4_ETC1S.ktx2",
|
}).then(function (arrayBuffer) {
|
ktx2BasisBuffer = new Uint8Array(arrayBuffer);
|
});
|
var ktx2BasisMipmapBufferPromise = Resource.fetchArrayBuffer({
|
url: "./Data/Images/Green4x4Mipmap_ETC1S.ktx2",
|
}).then(function (arrayBuffer) {
|
ktx2BasisMipmapBuffer = new Uint8Array(arrayBuffer);
|
});
|
|
return when.all([ktx2BasisBufferPromise, ktx2BasisMipmapBufferPromise]);
|
});
|
|
afterAll(function () {
|
context.destroyForSpecs();
|
});
|
|
afterEach(function () {
|
ResourceCache.clearForSpecs();
|
});
|
|
it("throws if resourceCache is undefined", function () {
|
expect(function () {
|
return new GltfImageLoader({
|
resourceCache: undefined,
|
gltf: gltf,
|
imageId: 0,
|
gltfResource: gltfResource,
|
baseResource: gltfResource,
|
});
|
}).toThrowDeveloperError();
|
});
|
|
it("throws if gltf is undefined", function () {
|
expect(function () {
|
return new GltfImageLoader({
|
resourceCache: ResourceCache,
|
gltf: undefined,
|
imageId: 0,
|
gltfResource: gltfResource,
|
baseResource: gltfResource,
|
});
|
}).toThrowDeveloperError();
|
});
|
|
it("throws if imageId is undefined", function () {
|
expect(function () {
|
return new GltfImageLoader({
|
resourceCache: ResourceCache,
|
gltf: gltf,
|
imageId: undefined,
|
gltfResource: gltfResource,
|
baseResource: gltfResource,
|
});
|
}).toThrowDeveloperError();
|
});
|
|
it("throws if gltfResource is undefined", function () {
|
expect(function () {
|
return new GltfImageLoader({
|
resourceCache: ResourceCache,
|
gltf: gltf,
|
imageId: 0,
|
gltfResource: undefined,
|
baseResource: gltfResource,
|
});
|
}).toThrowDeveloperError();
|
});
|
|
it("throws if baseResource is undefined", function () {
|
expect(function () {
|
return new GltfImageLoader({
|
resourceCache: ResourceCache,
|
gltf: gltf,
|
imageId: 0,
|
gltfResource: gltfResource,
|
baseResource: undefined,
|
});
|
}).toThrowDeveloperError();
|
});
|
|
it("rejects promise if buffer view fails to load", function () {
|
var error = new Error("404 Not Found");
|
spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue(
|
when.reject(error)
|
);
|
|
var imageLoader = new GltfImageLoader({
|
resourceCache: ResourceCache,
|
gltf: getGltf(pngBuffer),
|
imageId: 0,
|
gltfResource: gltfResource,
|
baseResource: gltfResource,
|
});
|
|
imageLoader.load();
|
|
return imageLoader.promise
|
.then(function (imageLoader) {
|
fail();
|
})
|
.otherwise(function (runtimeError) {
|
expect(runtimeError.message).toBe(
|
"Failed to load embedded image\nFailed to load buffer view\nFailed to load external buffer: https://example.com/external.bin\n404 Not Found"
|
);
|
});
|
});
|
|
it("rejects promise if image format is not recognized", function () {
|
spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue(
|
when.resolve(gifBuffer)
|
);
|
|
var imageLoader = new GltfImageLoader({
|
resourceCache: ResourceCache,
|
gltf: getGltf(gifBuffer),
|
imageId: 0,
|
gltfResource: gltfResource,
|
baseResource: gltfResource,
|
});
|
|
imageLoader.load();
|
|
return imageLoader.promise
|
.then(function (imageLoader) {
|
fail();
|
})
|
.otherwise(function (runtimeError) {
|
expect(runtimeError.message).toBe(
|
"Failed to load embedded image\nImage format is not recognized"
|
);
|
});
|
});
|
|
it("rejects promise if uri fails to load", function () {
|
var error = new Error("404 Not Found");
|
spyOn(Resource.prototype, "fetchImage").and.returnValue(when.reject(error));
|
|
var imageLoader = new GltfImageLoader({
|
resourceCache: ResourceCache,
|
gltf: getGltf(pngBuffer),
|
imageId: 1,
|
gltfResource: gltfResource,
|
baseResource: gltfResource,
|
});
|
|
imageLoader.load();
|
|
return imageLoader.promise
|
.then(function (imageLoader) {
|
fail();
|
})
|
.otherwise(function (runtimeError) {
|
expect(runtimeError.message).toBe(
|
"Failed to load image: image.png\n404 Not Found"
|
);
|
});
|
});
|
|
function loadsFromBufferView(imageBuffer) {
|
spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue(
|
when.resolve(imageBuffer)
|
);
|
|
var imageLoader = new GltfImageLoader({
|
resourceCache: ResourceCache,
|
gltf: getGltf(imageBuffer),
|
imageId: 0,
|
gltfResource: gltfResource,
|
baseResource: gltfResource,
|
});
|
|
imageLoader.load();
|
|
return imageLoader.promise.then(function (imageLoader) {
|
expect(imageLoader.image.width).toBe(1);
|
expect(imageLoader.image.height).toBe(1);
|
});
|
}
|
|
it("loads PNG from buffer view", function () {
|
return loadsFromBufferView(pngBuffer);
|
});
|
|
it("loads JPEG from buffer view", function () {
|
return loadsFromBufferView(jpgBuffer);
|
});
|
|
it("loads WebP from buffer view", function () {
|
return pollToPromise(function () {
|
FeatureDetection.supportsWebP.initialize();
|
return FeatureDetection.supportsWebP.initialized;
|
}).then(function () {
|
if (!FeatureDetection.supportsWebP()) {
|
return;
|
}
|
return loadsFromBufferView(webpBuffer);
|
});
|
});
|
|
it("loads KTX2/Basis from buffer view", function () {
|
if (!context.supportsBasis) {
|
return;
|
}
|
|
spyOn(BufferLoader, "_fetchArrayBuffer").and.returnValue(
|
when.resolve(ktx2BasisBuffer)
|
);
|
|
var imageLoader = new GltfImageLoader({
|
resourceCache: ResourceCache,
|
gltf: getGltf(ktx2BasisBuffer),
|
imageId: 2,
|
gltfResource: gltfResource,
|
baseResource: gltfResource,
|
});
|
|
imageLoader.load();
|
|
return imageLoader.promise.then(function (imageLoader) {
|
expect(imageLoader.image instanceof CompressedTextureBuffer).toBe(true);
|
expect(imageLoader.image.width).toBe(4);
|
expect(imageLoader.image.height).toBe(4);
|
expect(imageLoader.mipLevels).toBeUndefined();
|
});
|
});
|
|
it("loads KTX2/Basis with mipmap from buffer view", function () {
|
if (!context.supportsBasis) {
|
return;
|
}
|
|
spyOn(BufferLoader, "_fetchArrayBuffer").and.returnValue(
|
when.resolve(ktx2BasisMipmapBuffer)
|
);
|
|
var imageLoader = new GltfImageLoader({
|
resourceCache: ResourceCache,
|
gltf: getGltf(ktx2BasisMipmapBuffer),
|
imageId: 2,
|
gltfResource: gltfResource,
|
baseResource: gltfResource,
|
});
|
|
imageLoader.load();
|
|
return imageLoader.promise.then(function (imageLoader) {
|
expect(imageLoader.image instanceof CompressedTextureBuffer).toBe(true);
|
expect(imageLoader.image.width).toBe(4);
|
expect(imageLoader.image.height).toBe(4);
|
expect(imageLoader.mipLevels.length).toBe(2);
|
});
|
});
|
|
it("loads from uri", function () {
|
spyOn(Resource.prototype, "fetchImage").and.returnValue(
|
when.resolve(image)
|
);
|
|
var imageLoader = new GltfImageLoader({
|
resourceCache: ResourceCache,
|
gltf: clone(gltf, true),
|
imageId: 1,
|
gltfResource: gltfResource,
|
baseResource: gltfResource,
|
});
|
|
imageLoader.load();
|
|
return imageLoader.promise.then(function (imageLoader) {
|
expect(imageLoader.image.width).toBe(1);
|
expect(imageLoader.image.height).toBe(1);
|
});
|
});
|
|
it("loads KTX2/Basis from uri ", function () {
|
if (!context.supportsBasis) {
|
return;
|
}
|
|
var baseResource = new Resource({
|
url: "./Data/Images/",
|
});
|
|
var clonedGltf = clone(gltf, true);
|
clonedGltf.images[3].uri = "Green4x4_ETC1S.ktx2";
|
|
var imageLoader = new GltfImageLoader({
|
resourceCache: ResourceCache,
|
gltf: clonedGltf,
|
imageId: 3,
|
gltfResource: gltfResource,
|
baseResource: baseResource,
|
});
|
|
imageLoader.load();
|
|
return imageLoader.promise.then(function (imageLoader) {
|
expect(imageLoader.image instanceof CompressedTextureBuffer).toBe(true);
|
expect(imageLoader.image.width).toBe(4);
|
expect(imageLoader.image.height).toBe(4);
|
expect(imageLoader.mipLevels).toBeUndefined();
|
});
|
});
|
|
it("destroys image loader", function () {
|
spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue(
|
when.resolve(pngBuffer)
|
);
|
|
var unloadBufferView = spyOn(
|
GltfBufferViewLoader.prototype,
|
"unload"
|
).and.callThrough();
|
|
var imageLoader = new GltfImageLoader({
|
resourceCache: ResourceCache,
|
gltf: getGltf(pngBuffer),
|
imageId: 0,
|
gltfResource: gltfResource,
|
baseResource: gltfResource,
|
});
|
expect(imageLoader.image).not.toBeDefined();
|
|
imageLoader.load();
|
|
return imageLoader.promise.then(function (imageLoader) {
|
expect(imageLoader.image).toBeDefined();
|
expect(imageLoader.isDestroyed()).toBe(false);
|
|
imageLoader.destroy();
|
|
expect(imageLoader.image).not.toBeDefined();
|
expect(imageLoader.isDestroyed()).toBe(true);
|
expect(unloadBufferView).toHaveBeenCalled();
|
});
|
});
|
|
function resolveBufferViewAfterDestroy(reject) {
|
var deferredPromise = when.defer();
|
spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue(
|
deferredPromise.promise
|
);
|
|
// Load a copy of the buffer view into the cache so that the buffer view
|
// promise resolves even if the image loader is destroyed
|
var bufferViewLoaderCopy = ResourceCache.loadBufferView({
|
gltf: getGltf(pngBuffer),
|
bufferViewId: 0,
|
gltfResource: gltfResource,
|
baseResource: gltfResource,
|
});
|
|
var imageLoader = new GltfImageLoader({
|
resourceCache: ResourceCache,
|
gltf: getGltf(pngBuffer),
|
imageId: 0,
|
gltfResource: gltfResource,
|
baseResource: gltfResource,
|
});
|
|
expect(imageLoader.image).not.toBeDefined();
|
|
imageLoader.load();
|
imageLoader.destroy();
|
|
if (reject) {
|
deferredPromise.reject(new Error());
|
} else {
|
deferredPromise.resolve(pngBuffer);
|
}
|
|
expect(imageLoader.image).not.toBeDefined();
|
expect(imageLoader.isDestroyed()).toBe(true);
|
|
ResourceCache.unload(bufferViewLoaderCopy);
|
}
|
|
it("handles resolving buffer view after destroy", function () {
|
resolveBufferViewAfterDestroy(false);
|
});
|
|
it("handles rejecting buffer view after destroy", function () {
|
resolveBufferViewAfterDestroy(true);
|
});
|
|
function resolveImageFromTypedArrayAfterDestroy(reject) {
|
spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue(
|
when.resolve(pngBuffer)
|
);
|
|
var deferredPromise = when.defer();
|
spyOn(GltfImageLoader, "_loadImageFromTypedArray").and.returnValue(
|
deferredPromise.promise
|
);
|
|
// Load a copy of the buffer view into the cache so that the buffer view
|
// promise resolves even if the image loader is destroyed
|
var bufferViewLoaderCopy = ResourceCache.loadBufferView({
|
gltf: getGltf(pngBuffer),
|
bufferViewId: 0,
|
gltfResource: gltfResource,
|
baseResource: gltfResource,
|
});
|
|
var imageLoader = new GltfImageLoader({
|
resourceCache: ResourceCache,
|
gltf: getGltf(pngBuffer),
|
imageId: 0,
|
gltfResource: gltfResource,
|
baseResource: gltfResource,
|
});
|
|
expect(imageLoader.image).not.toBeDefined();
|
|
imageLoader.load();
|
imageLoader.destroy();
|
|
if (reject) {
|
deferredPromise.reject(new Error());
|
} else {
|
deferredPromise.resolve(image);
|
}
|
|
expect(imageLoader.image).not.toBeDefined();
|
expect(imageLoader.isDestroyed()).toBe(true);
|
|
ResourceCache.unload(bufferViewLoaderCopy);
|
}
|
|
it("handles resolving image from typed array after destroy", function () {
|
resolveImageFromTypedArrayAfterDestroy(false);
|
});
|
|
it("handles rejecting image from typed array after destroy", function () {
|
resolveImageFromTypedArrayAfterDestroy(true);
|
});
|
|
function resolveUriAfterDestroy(reject) {
|
var deferredPromise = when.defer();
|
spyOn(Resource.prototype, "fetchImage").and.returnValue(
|
deferredPromise.promise
|
);
|
|
var imageLoader = new GltfImageLoader({
|
resourceCache: ResourceCache,
|
gltf: getGltf(pngBuffer),
|
imageId: 1,
|
gltfResource: gltfResource,
|
baseResource: gltfResource,
|
});
|
|
expect(imageLoader.image).not.toBeDefined();
|
|
imageLoader.load();
|
imageLoader.destroy();
|
|
if (reject) {
|
deferredPromise.reject(new Error());
|
} else {
|
deferredPromise.resolve(image);
|
}
|
|
expect(imageLoader.image).not.toBeDefined();
|
expect(imageLoader.isDestroyed()).toBe(true);
|
}
|
|
it("handles resolving uri after destroy", function () {
|
resolveUriAfterDestroy(false);
|
});
|
|
it("handles rejecting uri after destroy", function () {
|
resolveUriAfterDestroy(true);
|
});
|
});
|