From 96cbc181262b0cacba60bee3894dc221c237f693 Mon Sep 17 00:00:00 2001 From: Shehata Date: Mon, 18 Feb 2019 10:56:53 -0500 Subject: [PATCH 01/16] Add feature detection for createImageBitmap --- Source/Core/FeatureDetection.js | 71 +++++++++++++++++++++++++++++- Specs/Core/FeatureDetectionSpec.js | 22 +++++++++ 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/Source/Core/FeatureDetection.js b/Source/Core/FeatureDetection.js index 6d0d02346d1c..941f7b8a9958 100644 --- a/Source/Core/FeatureDetection.js +++ b/Source/Core/FeatureDetection.js @@ -2,13 +2,11 @@ define([ './defaultValue', './defined', './Fullscreen', - './RuntimeError', '../ThirdParty/when' ], function( defaultValue, defined, Fullscreen, - RuntimeError, when) { 'use strict'; /*global CanvasPixelArray*/ @@ -255,6 +253,71 @@ define([ } } + var supportsCreateImageBitmapResult; + function supportsCreateImageBitmap() { + if (!defined(supportsCreateImageBitmapResult)) { + supportsCreateImageBitmapResult = defined(window.createImageBitmap); + } + + return supportsCreateImageBitmapResult; + } + + var supportsImageBitmapOptionsResult; + var supportsImageBitmapOptionsPromise; + function supportsImageBitmapOptions() { + // Until the HTML folks figure out what to do about this, we need to actually try loading an image to + // know if this browser supports passing options to the createImageBitmap function. + // https://github.com/whatwg/html/pull/4248 + if (defined(supportsImageBitmapOptionsPromise)) { + return supportsImageBitmapOptionsPromise.promise; + } + + supportsImageBitmapOptionsPromise = when.defer(); + + if (!supportsCreateImageBitmap()) { + supportsImageBitmapOptionsResult = false; + supportsImageBitmapOptionsPromise.resolve(supportsImageBitmapOptionsResult); + return supportsImageBitmapOptionsPromise.promise; + } + + var imageDataUri = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWP4////fwAJ+wP9CNHoHgAAAABJRU5ErkJggg=='; + fetch(imageDataUri) + .then(function(response) { + return response.blob(); + }) + .then(function(blob) { + return createImageBitmap(blob, { + imageOrientation: 'flipY' + }); + }) + .then(function(imageBitmap) { + supportsImageBitmapOptionsResult = true; + supportsImageBitmapOptionsPromise.resolve(supportsImageBitmapOptionsResult); + }) + .catch(function() { + supportsImageBitmapOptionsResult = false; + supportsImageBitmapOptionsPromise.resolve(supportsImageBitmapOptionsResult); + }); + + return supportsImageBitmapOptionsPromise.promise; + } + + function supportsImageBitmapOptionsSync() { + if (!defined(supportsImageBitmapOptionsPromise)) { + supportsImageBitmapOptions(); + } + + return supportsImageBitmapOptionsResult; + } + + var supportsFetchApiResult; + function supportsFetchApi() { + if (!defined(supportsFetchApiResult)) { + supportsFetchApiResult = defined(window.fetch); + } + return supportsFetchApiResult; + } + /** * A set of functions to detect whether the current browser supports * various features. @@ -280,6 +343,10 @@ define([ supportsImageRenderingPixelated: supportsImageRenderingPixelated, supportsWebP: supportsWebP, supportsWebPSync: supportsWebPSync, + supportsCreateImageBitmap: supportsCreateImageBitmap, + supportsImageBitmapOptions: supportsImageBitmapOptions, + supportsImageBitmapOptionsSync: supportsImageBitmapOptionsSync, + supportsFetchApi : supportsFetchApi, imageRenderingValue: imageRenderingValue, typedArrayTypes: typedArrayTypes }; diff --git a/Specs/Core/FeatureDetectionSpec.js b/Specs/Core/FeatureDetectionSpec.js index 18c3eabf52b2..b2f84ab7a69c 100644 --- a/Specs/Core/FeatureDetectionSpec.js +++ b/Specs/Core/FeatureDetectionSpec.js @@ -123,4 +123,26 @@ defineSuite([ expect(FeatureDetection.supportsWebPSync()).toEqual(supportsWebP); }); }); + + it('detects fetch API support', function() { + var supportsFetchApi = FeatureDetection.supportsFetchApi(); + expect(typeof supportsFetchApi).toEqual('boolean'); + }); + + it('detects createImageBitmap support', function() { + var supportsCreateImageBitmap = FeatureDetection.supportsCreateImageBitmap(); + expect(typeof supportsCreateImageBitmap).toEqual('boolean'); + }); + + it('detects ImageBitmapOptions support', function() { + if (FeatureDetection.supportsCreateImageBitmap()) { + expect(FeatureDetection.supportsImageBitmapOptionsSync()).not.toBeDefined(); + } + + return FeatureDetection.supportsImageBitmapOptions() + .then(function(supportsImageBitmapOptions) { + expect(typeof supportsImageBitmapOptions).toEqual('boolean'); + expect(FeatureDetection.supportsImageBitmapOptionsSync()).toEqual(supportsImageBitmapOptions); + }); + }); }); From b772d527599c787968ba2d5b3e652811b0e3b82f Mon Sep 17 00:00:00 2001 From: Shehata Date: Mon, 18 Feb 2019 11:38:48 -0500 Subject: [PATCH 02/16] Use image bitmap and flip during fetch --- Source/Core/Resource.js | 84 +++++++++++++++---- Source/Core/loadImageFromTypedArray.js | 25 +++++- .../GoogleEarthEnterpriseImageryProvider.js | 5 +- Source/Scene/Model.js | 9 +- Source/Widgets/Viewer/Viewer.js | 6 ++ 5 files changed, 107 insertions(+), 22 deletions(-) diff --git a/Source/Core/Resource.js b/Source/Core/Resource.js index 28a752a2a5c1..6600f8ae9065 100644 --- a/Source/Core/Resource.js +++ b/Source/Core/Resource.js @@ -11,6 +11,7 @@ define([ './deprecationWarning', './DeveloperError', './freezeObject', + './FeatureDetection', './getAbsoluteUri', './getBaseUri', './getExtensionFromUri', @@ -39,6 +40,7 @@ define([ deprecationWarning, DeveloperError, freezeObject, + FeatureDetection, getAbsoluteUri, getBaseUri, getExtensionFromUri, @@ -250,6 +252,7 @@ define([ * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. + * @param {Boolean} [options.flipImage] Whether to vertically flip the image during fetch and decode. Only applies when requesting an image and the browser supports createImageBitmap. * @param {Resource~RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. @@ -336,6 +339,14 @@ define([ this.retryAttempts = defaultValue(options.retryAttempts, 0); this._retryCount = 0; + /** + * Whether to vertically flip the image during fetch and decode. Only applies when requesting + * an image and the browser supports createImageBitmap. + * + * @type {Boolean} + */ + this.flipImage = defaultValue(options.flipImage, true); + var uri = new Uri(options.url); parseQuery(uri, this, true, true); @@ -620,6 +631,7 @@ define([ * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). These will be combined with those of the current instance. * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. + * @param {Boolean} [options.flipImage] Whether to vertically flip the image during fetch and decode. Only applies when requesting an image and the browser supports createImageBitmap. * @param {Resource~RetryCallback} [options.retryCallback] The function to call when loading the resource fails. * @param {Number} [options.retryAttempts] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. @@ -664,6 +676,9 @@ define([ if (defined(options.retryAttempts)) { resource.retryAttempts = options.retryAttempts; } + if (defined(options.flipImage)) { + resource.flipImage = options.flipImage; + } return resource; }; @@ -714,6 +729,7 @@ define([ result.retryCallback = this.retryCallback; result.retryAttempts = this.retryAttempts; result._retryCount = 0; + result.flipImage = this.flipImage; result.request = this.request.clone(); return result; @@ -771,6 +787,7 @@ define([ * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. + * @param {Boolean} [options.flipImage] Whether to vertically flip the image during fetch and decode. Only applies when requesting an image and the browser supports createImageBitmap. * @param {Resource~RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. @@ -815,6 +832,7 @@ define([ * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. + * @param {Boolean} [options.flipImage] Whether to vertically flip the image during fetch and decode. Only applies when requesting an image and the browser supports createImageBitmap. * @param {Resource~RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. @@ -851,6 +869,7 @@ define([ */ Resource.prototype.fetchImage = function (preferBlob) { preferBlob = defaultValue(preferBlob, false); + var flipImage = this.flipImage; checkAndResetRequest(this.request); @@ -860,7 +879,7 @@ define([ // 3. It's a blob URI // 4. It doesn't have request headers and we preferBlob is false if (!xhrBlobSupported || this.isDataUri || this.isBlobUri || (!this.hasHeaders && !preferBlob)) { - return fetchImage(this, true); + return fetchImage(this); } var blobPromise = this.fetchBlob(); @@ -878,7 +897,8 @@ define([ generatedBlob = blob; var blobUrl = window.URL.createObjectURL(blob); generatedBlobResource = new Resource({ - url: blobUrl + url: blobUrl, + flipImage: flipImage }); return fetchImage(generatedBlobResource); @@ -908,6 +928,7 @@ define([ request.url = resource.url; request.requestFunction = function() { var url = resource.url; + var flipImage = resource.flipImage; var crossOrigin = false; // data URIs can't have crossorigin set. @@ -917,7 +938,7 @@ define([ var deferred = when.defer(); - Resource._Implementations.createImage(url, crossOrigin, deferred); + Resource._Implementations.createImage(url, crossOrigin, deferred, flipImage); return deferred.promise; }; @@ -958,6 +979,7 @@ define([ * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. + * @param {Boolean} [options.flipImage] Whether to vertically flip the image during fetch and decode. Only applies when requesting an image and the browser supports createImageBitmap. * @param {Resource~RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. @@ -1378,6 +1400,7 @@ define([ * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. + * @param {Boolean} [options.flipImage] Whether to vertically flip the image during fetch and decode. Only applies when requesting an image and the browser supports createImageBitmap. * @param {Resource~RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. @@ -1755,26 +1778,51 @@ define([ */ Resource._Implementations = {}; - Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - var image = new Image(); + Resource._Implementations.createImage = function(url, crossOrigin, deferred, flipImage) { + var supportsBitmapOptions = FeatureDetection.supportsImageBitmapOptionsSync(); - image.onload = function() { - deferred.resolve(image); - }; + if (FeatureDetection.supportsFetchApi() && FeatureDetection.supportsCreateImageBitmap() && defined(supportsBitmapOptions)) { + fetch(url, { + credentials: (crossOrigin && TrustedServers.contains(url)) ? 'include' : 'same-origin' + }) + .then(function(response) { + // Catch error here https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful + return response.blob(); + }) + .then(function(blob) { + if (!supportsBitmapOptions) { + return createImageBitmap(blob); + } - image.onerror = function(e) { - deferred.reject(e); - }; + return createImageBitmap(blob, { + imageOrientation: flipImage ? 'flipY' : 'none' + }); + }) + .then(function (imageBitmap) { + deferred.resolve(imageBitmap); + }) + .catch(deferred.reject); + } else { + var image = new Image(); - if (crossOrigin) { - if (TrustedServers.contains(url)) { - image.crossOrigin = 'use-credentials'; - } else { - image.crossOrigin = ''; + image.onload = function() { + deferred.resolve(image); + }; + + image.onerror = function(e) { + deferred.reject(e); + }; + + if (crossOrigin) { + if (TrustedServers.contains(url)) { + image.crossOrigin = 'use-credentials'; + } else { + image.crossOrigin = ''; + } } - } - image.src = url; + image.src = url; + } }; function decodeResponse(loadWithHttpResponse, responseType) { diff --git a/Source/Core/loadImageFromTypedArray.js b/Source/Core/loadImageFromTypedArray.js index fa2df18dc4d7..d64f5f781475 100644 --- a/Source/Core/loadImageFromTypedArray.js +++ b/Source/Core/loadImageFromTypedArray.js @@ -1,17 +1,27 @@ define([ '../ThirdParty/when', './Check', + './defined', + './defaultValue', + './FeatureDetection', './Resource' ], function( when, Check, + defined, + defaultValue, + FeatureDetection, Resource) { 'use strict'; /** * @private */ - function loadImageFromTypedArray(uint8Array, format, request) { + function loadImageFromTypedArray(options) { + var uint8Array = options.uint8Array; + var format = options.format; + var request = options.request; + var flipY = defaultValue(options.flipY, true); //>>includeStart('debug', pragmas.debug); Check.typeOf.object('uint8Array', uint8Array); Check.typeOf.string('format', format); @@ -21,6 +31,19 @@ define([ type : format }); + // We can avoid the extra fetch when createImageBitmap is supported. + var supportsBitmapOptions = FeatureDetection.supportsImageBitmapOptionsSync(); + + if (FeatureDetection.supportsCreateImageBitmap() && defined(supportsBitmapOptions)) { + if (supportsBitmapOptions) { + return when(createImageBitmap(blob, { + imageOrientation: flipY ? 'flipY' : 'none' + })); + } + + return when(createImageBitmap(blob)); + } + var blobUrl = window.URL.createObjectURL(blob); var resource = new Resource({ url: blobUrl, diff --git a/Source/Scene/GoogleEarthEnterpriseImageryProvider.js b/Source/Scene/GoogleEarthEnterpriseImageryProvider.js index 5e71d409ef50..38e37358383d 100644 --- a/Source/Scene/GoogleEarthEnterpriseImageryProvider.js +++ b/Source/Scene/GoogleEarthEnterpriseImageryProvider.js @@ -488,7 +488,10 @@ define([ return invalidImage; } - return loadImageFromTypedArray(a, type); + return loadImageFromTypedArray({ + uint8Array: a, + format: type + }); }); }; diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 5f22e191f742..e7d81bdac386 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -1714,7 +1714,8 @@ define([ ++model._loadResources.pendingTextureLoads; var imageResource = model._resource.getDerivedResource({ - url : uri + url : uri, + flipImage : false }); var promise; @@ -2325,7 +2326,11 @@ define([ ++model._loadResources.pendingTextureLoads; } else { var onload = getOnImageCreatedFromTypedArray(loadResources, gltfTexture); - loadImageFromTypedArray(loadResources.getBuffer(bufferView), gltfTexture.mimeType) + loadImageFromTypedArray({ + uint8Array: loadResources.getBuffer(bufferView), + format: gltfTexture.mimeType, + flipY: false + }) .then(onload).otherwise(onerror); ++loadResources.pendingBufferViewToImage; } diff --git a/Source/Widgets/Viewer/Viewer.js b/Source/Widgets/Viewer/Viewer.js index d9664a7bb540..5c286670fb43 100644 --- a/Source/Widgets/Viewer/Viewer.js +++ b/Source/Widgets/Viewer/Viewer.js @@ -10,6 +10,7 @@ define([ '../../Core/DeveloperError', '../../Core/Event', '../../Core/EventHelper', + '../../Core/FeatureDetection', '../../Core/HeadingPitchRange', '../../Core/isArray', '../../Core/Matrix4', @@ -60,6 +61,7 @@ define([ DeveloperError, Event, EventHelper, + FeatureDetection, HeadingPitchRange, isArray, Matrix4, @@ -361,6 +363,10 @@ define([ } //>>includeEnd('debug'); + // We kick off this asynchronous test here so we can start using + // createImageBitmap if it's supported as soon as possible. + FeatureDetection.supportsImageBitmapOptions(); + container = getElement(container); options = defaultValue(options, defaultValue.EMPTY_OBJECT); From dca9ed1accb55fcd753552dbbc69185bf6e2d0b9 Mon Sep 17 00:00:00 2001 From: Shehata Date: Mon, 18 Feb 2019 11:42:26 -0500 Subject: [PATCH 03/16] Add tests for image bitmap scenarios --- Specs/Core/ResourceSpec.js | 141 ++++++++++++++++++++-- Specs/Core/loadImageFromTypedArraySpec.js | 79 +++++++++++- Specs/Renderer/TextureSpec.js | 104 +++++++++++----- 3 files changed, 283 insertions(+), 41 deletions(-) diff --git a/Specs/Core/ResourceSpec.js b/Specs/Core/ResourceSpec.js index cc9bad6ded5a..1357aabbdfb1 100644 --- a/Specs/Core/ResourceSpec.js +++ b/Specs/Core/ResourceSpec.js @@ -2,24 +2,36 @@ defineSuite([ 'Core/Resource', 'Core/defaultValue', 'Core/DefaultProxy', + 'Core/FeatureDetection', 'Core/queryToObject', 'Core/Request', 'Core/RequestErrorEvent', 'Core/RequestScheduler', + 'Core/TrustedServers', + 'Specs/createCanvas', 'ThirdParty/Uri', 'ThirdParty/when' ], function( Resource, defaultValue, DefaultProxy, + FeatureDetection, queryToObject, Request, RequestErrorEvent, RequestScheduler, + TrustedServers, + createCanvas, Uri, when) { 'use strict'; + var dataUri = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIW2Nk+M/wHwAEBgIA5agATwAAAABJRU5ErkJggg=='; + + beforeAll(function() { + return FeatureDetection.supportsImageBitmapOptions(); + }); + it('Constructor sets correct properties', function() { var proxy = new DefaultProxy('/proxy/'); var request = new Request(); @@ -1122,26 +1134,141 @@ defineSuite([ }); }); - describe('fetchImage', function() { + it('can load an image preferring blob', function() { + return Resource.fetchImage('./Data/Images/Green.png', true).then(function(loadedImage) { + expect(loadedImage.width).toEqual(1); + expect(loadedImage.height).toEqual(1); + }); + }); - var dataUri = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIW2Nk+M/wHwAEBgIA5agATwAAAABJRU5ErkJggg=='; + it('can load an image from a data URI', function() { + return Resource.fetchImage(dataUri).then(function(loadedImage) { + expect(loadedImage.width).toEqual(1); + expect(loadedImage.height).toEqual(1); + }); + }); - it('can load an image', function() { + describe('fetchImage with ImageBitmap', function() { + if (!FeatureDetection.supportsCreateImageBitmap()) { + return; + } + + var canvas; + beforeAll(function() { + canvas = createCanvas(1, 2); + }); + + afterAll(function() { + document.body.removeChild(canvas); + }); + + function getColorAtPixel(image, x, y) { + var context = canvas.getContext('2d'); + context.drawImage(image, 0, 0, image.width, image.height); + var imageData = context.getImageData(0, 0, 1, 1); + return [imageData.data[0], imageData.data[1], imageData.data[2], imageData.data[3]]; + } + + it('can load and decode an image', function() { return Resource.fetchImage('./Data/Images/Green.png').then(function(loadedImage) { expect(loadedImage.width).toEqual(1); expect(loadedImage.height).toEqual(1); + expect(loadedImage instanceof ImageBitmap); }); }); - it('can load an image preferring blob', function() { - return Resource.fetchImage('./Data/Images/Green.png', true).then(function(loadedImage) { + it('does not call createImageBitmap when ImageBitmapOptions support is not ready', function() { + spyOn(FeatureDetection, 'supportsImageBitmapOptionsSync').and.returnValue(undefined); + spyOn(window, 'createImageBitmap').and.callThrough(); + + return Resource.fetchImage('./Data/Images/Green.png').then(function(loadedImage) { expect(loadedImage.width).toEqual(1); expect(loadedImage.height).toEqual(1); + expect(window.createImageBitmap).not.toHaveBeenCalled(); + }); + }); + + it('correctly flips image when ImageBitmapOptions are supported', function() { + return Resource.fetchImage({ + url: './Data/Images/BlueOverRed.png', + flipImage: true + }).then(function(loadedImage) { + if (FeatureDetection.supportsImageBitmapOptionsSync()) { + expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([255, 0, 0, 255]); + } else { + expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([0, 0, 255, 255]); + } + }); + }); + + it('correctly loads image without flip when ImageBitmapOptions are supported', function() { + return Resource.fetchImage({ + url: './Data/Images/BlueOverRed.png', + flipImage: false + }).then(function(loadedImage) { + if (FeatureDetection.supportsImageBitmapOptionsSync()) { + expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([0, 0, 255, 255]); + } else { + expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([0, 0, 255, 255]); + } }); }); - it('can load an image from a data URI', function() { - return Resource.fetchImage(dataUri).then(function(loadedImage) { + it('does not pass options when ImageBitmapOptions are not supported', function() { + spyOn(FeatureDetection, 'supportsImageBitmapOptionsSync').and.returnValue(false); + spyOn(window, 'createImageBitmap').and.callThrough(); + + return Resource.fetchImage('./Data/Images/Green.png').then(function(loadedImage) { + expect(window.createImageBitmap).toHaveBeenCalledWith(new Blob()); + }); + }); + + it('rejects the promise when the image errors', function() { + return Resource.fetchImage('http://example.invalid/testuri.png') + .then(function() { + fail('expected promise to reject'); + }) + .otherwise(function(error) { + expect(error).toBeInstanceOf(TypeError); + }); + }); + + it('does not set credentials for untrusted cross-origin images using fetch API', function() { + var fetchSpy = spyOn(window, 'fetch').and.callThrough(); + var url = './Data/Images/Green.png'; + + return Resource.fetchImage(url) + .then(function() { + expect(fetchSpy).toHaveBeenCalledWith(url, { + credentials: 'same-origin' + }); + }); + }); + + it('sets credentials for trusted cross-origin images using fetch API', function() { + var fetchSpy = spyOn(window, 'fetch').and.callThrough(); + spyOn(TrustedServers, 'contains').and.returnValue(true); + var url = 'http://example.invalid/testuri.png'; + + return Resource.fetchImage(url) + .otherwise(function() { + expect(fetchSpy).toHaveBeenCalledWith(url, { + credentials: 'include' + }); + }); + }); + }); + + describe('fetchImage without ImageBitmap', function() { + beforeAll(function() { + // Force it to use the Image constructor since these specs all test + // specific functionality of this code path. For example, the crossOrigin + // restriction does not apply to images loaded with ImageBitmap. + spyOn(FeatureDetection, 'supportsCreateImageBitmap').and.returnValue(false); + }); + + it('can load an image', function() { + return Resource.fetchImage('./Data/Images/Green.png').then(function(loadedImage) { expect(loadedImage.width).toEqual(1); expect(loadedImage.height).toEqual(1); }); diff --git a/Specs/Core/loadImageFromTypedArraySpec.js b/Specs/Core/loadImageFromTypedArraySpec.js index 43e705f6ee49..e21b800bdc38 100644 --- a/Specs/Core/loadImageFromTypedArraySpec.js +++ b/Specs/Core/loadImageFromTypedArraySpec.js @@ -1,24 +1,89 @@ defineSuite([ 'Core/loadImageFromTypedArray', + 'Core/FeatureDetection', 'Core/Resource' ], function( loadImageFromTypedArray, + FeatureDetection, Resource) { 'use strict'; + beforeAll(function() { + return FeatureDetection.supportsImageBitmapOptions(); + }); + it('can load an image', function() { return Resource.fetchArrayBuffer('./Data/Images/Blue10x10.png').then(function(arrayBuffer) { - var arr = new Uint8Array(arrayBuffer); - return loadImageFromTypedArray(arr, 'image/png').then(function(image) { + var options = { + uint8Array: new Uint8Array(arrayBuffer), + format: 'image/png' + }; + + return loadImageFromTypedArray(options).then(function(image) { + expect(image.width).toEqual(10); + expect(image.height).toEqual(10); + }); + }); + }); + + it('flips image when ImageBitmapOptions are supported', function() { + var options = { + uint8Array: new Uint8Array([67, 101, 115, 105, 117, 109]), // This is an invalid PNG. + format: 'image/png', + flipY: true + }; + spyOn(window, 'createImageBitmap'); + var blob = new Blob([options.uint8Array], { + type : options.format + }); + + return loadImageFromTypedArray(options).then(function() { + if (FeatureDetection.supportsImageBitmapOptionsSync()) { + expect(window.createImageBitmap).toHaveBeenCalledWith(blob, { + imageOrientation: 'flipY' + }); + } else { + expect(window.createImageBitmap).toHaveBeenCalledWith(blob); + } + + options.flipY = false; + window.createImageBitmap.calls.reset(); + return loadImageFromTypedArray(options); + }) + .then(function() { + if (FeatureDetection.supportsImageBitmapOptionsSync()) { + expect(window.createImageBitmap).toHaveBeenCalledWith(blob, { + imageOrientation: 'none' + }); + } else { + expect(window.createImageBitmap).toHaveBeenCalledWith(blob); + } + }); + }); + + it('can load an image when ImageBitmap is not supported', function() { + spyOn(FeatureDetection, 'supportsCreateImageBitmap').and.returnValue(false); + spyOn(window, 'createImageBitmap').and.callThrough(); + return Resource.fetchArrayBuffer('./Data/Images/Blue10x10.png').then(function(arrayBuffer) { + var options = { + uint8Array: new Uint8Array(arrayBuffer), + format: 'image/png' + }; + + return loadImageFromTypedArray(options).then(function(image) { expect(image.width).toEqual(10); expect(image.height).toEqual(10); + expect(window.createImageBitmap).not.toHaveBeenCalled(); }); }); }); it('can not load an invalid image', function() { - var notApng = new Uint8Array([67, 101, 115, 105, 117, 109]); - return loadImageFromTypedArray(notApng, 'image/png').then(function(image) { + var options = { + uint8Array: new Uint8Array([67, 101, 115, 105, 117, 109]), // This is an invalid PNG. + format: 'image/png' + }; + return loadImageFromTypedArray(options).then(function(image) { fail('should not be called'); }).otherwise(function() { }); @@ -26,13 +91,15 @@ defineSuite([ it('Throws without array', function() { expect(function() { - loadImageFromTypedArray(); + loadImageFromTypedArray({}); }).toThrowDeveloperError(); }); it('Throws without format', function() { expect(function() { - loadImageFromTypedArray(new Uint8Array()); + loadImageFromTypedArray({ + uint8Array: new Uint8Array() + }); }).toThrowDeveloperError(); }); }); diff --git a/Specs/Renderer/TextureSpec.js b/Specs/Renderer/TextureSpec.js index ad0bb76976b0..cb8b269c34c8 100644 --- a/Specs/Renderer/TextureSpec.js +++ b/Specs/Renderer/TextureSpec.js @@ -2,6 +2,7 @@ defineSuite([ 'Renderer/Texture', 'Core/Cartesian2', 'Core/Color', + 'Core/FeatureDetection', 'Core/loadKTX', 'Core/PixelFormat', 'Core/Resource', @@ -18,6 +19,7 @@ defineSuite([ Texture, Cartesian2, Color, + FeatureDetection, loadKTX, PixelFormat, Resource, @@ -37,6 +39,7 @@ defineSuite([ var blueImage; var blueAlphaImage; var blueOverRedImage; + var blueOverRedUnflippedImage; var red16x16Image; var greenDXTImage; @@ -55,34 +58,43 @@ defineSuite([ beforeAll(function() { context = createContext(); - - var promises = []; - promises.push(Resource.fetchImage('./Data/Images/Green.png').then(function(image) { - greenImage = image; - })); - promises.push(Resource.fetchImage('./Data/Images/Blue.png').then(function(image) { - blueImage = image; - })); - promises.push(Resource.fetchImage('./Data/Images/BlueAlpha.png').then(function(image) { - blueAlphaImage = image; - })); - promises.push(Resource.fetchImage('./Data/Images/BlueOverRed.png').then(function(image) { - blueOverRedImage = image; - })); - promises.push(Resource.fetchImage('./Data/Images/Red16x16.png').then(function(image) { - red16x16Image = image; - })); - promises.push(loadKTX('./Data/Images/Green4x4DXT1.ktx').then(function(image) { - greenDXTImage = image; - })); - promises.push(loadKTX('./Data/Images/Green4x4PVR.ktx').then(function(image) { - greenPVRImage = image; - })); - promises.push(loadKTX('./Data/Images/Green4x4ETC1.ktx').then(function(image) { - greenETC1Image = image; - })); - - return when.all(promises); + return FeatureDetection.supportsImageBitmapOptions() + .then(function() { + var promises = []; + promises.push(Resource.fetchImage('./Data/Images/Green.png').then(function(image) { + greenImage = image; + })); + promises.push(Resource.fetchImage('./Data/Images/Blue.png').then(function(image) { + blueImage = image; + })); + promises.push(Resource.fetchImage('./Data/Images/BlueAlpha.png').then(function(image) { + blueAlphaImage = image; + })); + promises.push(Resource.fetchImage('./Data/Images/BlueOverRed.png').then(function(image) { + blueOverRedImage = image; + })); + // Turn off the default flipping. + promises.push(Resource.fetchImage({ + url: './Data/Images/BlueOverRed.png', + flipImage: false + }).then(function(image) { + blueOverRedUnflippedImage = image; + })); + promises.push(Resource.fetchImage('./Data/Images/Red16x16.png').then(function(image) { + red16x16Image = image; + })); + promises.push(loadKTX('./Data/Images/Green4x4DXT1.ktx').then(function(image) { + greenDXTImage = image; + })); + promises.push(loadKTX('./Data/Images/Green4x4PVR.ktx').then(function(image) { + greenPVRImage = image; + })); + promises.push(loadKTX('./Data/Images/Green4x4ETC1.ktx').then(function(image) { + greenETC1Image = image; + })); + + return when.all(promises); + }); }); afterAll(function() { @@ -187,6 +199,42 @@ defineSuite([ }).contextToRender([0, 0, 255, 255]); }); + it('can set flip texture only if ImageBitmapOptions is not supported', function() { + var topColor = new Color(0.0, 0.0, 1.0, 1.0); + var bottomColor = new Color(1.0, 0.0, 0.0, 1.0); + if (FeatureDetection.supportsImageBitmapOptionsSync()) { + // When imageBitmapOptions is supported, flipY on texture upload is ignored. + bottomColor = topColor; + } + + texture = new Texture({ + context : context, + source : blueOverRedUnflippedImage, + pixelFormat : PixelFormat.RGBA, + flipY : false + }); + + expect({ + context : context, + fragmentShader : fs, + uniformMap : uniformMap + }).contextToRender(topColor.toBytes()); + + // Flip the texture. + texture = new Texture({ + context : context, + source : blueOverRedUnflippedImage, + pixelFormat : PixelFormat.RGBA, + flipY : true + }); + + expect({ + context : context, + fragmentShader : fs, + uniformMap : uniformMap + }).contextToRender(bottomColor.toBytes()); + }); + it('draws the expected floating-point texture color', function() { if (!context.floatingPointTexture) { return; From 824ceb12e6a38208c44609a034365c78cc55296d Mon Sep 17 00:00:00 2001 From: Shehata Date: Mon, 18 Feb 2019 11:52:43 -0500 Subject: [PATCH 04/16] Update imagery specs to for imagebitmap --- .../ArcGisMapServerImageryProviderSpec.js | 20 ++- Specs/Scene/BillboardCollectionSpec.js | 46 +++-- Specs/Scene/BingMapsImageryProviderSpec.js | 14 +- ...oogleEarthEnterpriseImageryProviderSpec.js | 9 +- .../GoogleEarthEnterpriseMapsProviderSpec.js | 16 +- Specs/Scene/MapboxImageryProviderSpec.js | 22 ++- Specs/Scene/SingleTileImageryProviderSpec.js | 12 +- Specs/Scene/TextureAtlasSpec.js | 164 ++++++++++-------- Specs/Scene/UrlTemplateImageryProviderSpec.js | 48 ++--- .../Scene/WebMapServiceImageryProviderSpec.js | 24 ++- .../WebMapTileServiceImageryProviderSpec.js | 12 +- .../createOpenStreetMapImageryProviderSpec.js | 24 ++- ...createTileMapServiceImageryProviderSpec.js | 20 ++- Specs/isImageOrImageBitmap.js | 19 ++ 14 files changed, 291 insertions(+), 159 deletions(-) create mode 100644 Specs/isImageOrImageBitmap.js diff --git a/Specs/Scene/ArcGisMapServerImageryProviderSpec.js b/Specs/Scene/ArcGisMapServerImageryProviderSpec.js index e9a237bb2b5e..1d0742fe497c 100644 --- a/Specs/Scene/ArcGisMapServerImageryProviderSpec.js +++ b/Specs/Scene/ArcGisMapServerImageryProviderSpec.js @@ -6,6 +6,7 @@ defineSuite([ 'Core/Cartographic', 'Core/DefaultProxy', 'Core/defined', + 'Core/FeatureDetection', 'Core/GeographicTilingScheme', 'Core/getAbsoluteUri', 'Core/objectToQuery', @@ -22,6 +23,7 @@ defineSuite([ 'Scene/ImageryProvider', 'Scene/ImageryState', 'Specs/pollToPromise', + 'Specs/isImageOrImageBitmap', 'ThirdParty/Uri' ], function( ArcGisMapServerImageryProvider, @@ -31,6 +33,7 @@ defineSuite([ Cartographic, DefaultProxy, defined, + FeatureDetection, GeographicTilingScheme, getAbsoluteUri, objectToQuery, @@ -47,9 +50,14 @@ defineSuite([ ImageryProvider, ImageryState, pollToPromise, + isImageOrImageBitmap, Uri) { 'use strict'; + beforeAll(function() { + return FeatureDetection.supportsImageBitmapOptions(); + }); + beforeEach(function() { RequestScheduler.clearForSpecs(); }); @@ -225,7 +233,7 @@ defineSuite([ }; return provider.requestImage(0, 0, 0).then(function(image) { - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -302,7 +310,7 @@ defineSuite([ }; return provider.requestImage(0, 0, 0).then(function(image) { - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -355,7 +363,7 @@ defineSuite([ }; return provider.requestImage(0, 0, 0).then(function(image) { - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -419,7 +427,7 @@ defineSuite([ }; return provider.requestImage(0, 0, 0).then(function(image) { - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -475,7 +483,7 @@ defineSuite([ }; return provider.requestImage(0, 0, 0).then(function(image) { - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -609,7 +617,7 @@ defineSuite([ return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; }).then(function() { - expect(imagery.image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(imagery.image)).toBe(true); expect(tries).toEqual(2); imagery.releaseReference(); }); diff --git a/Specs/Scene/BillboardCollectionSpec.js b/Specs/Scene/BillboardCollectionSpec.js index dcfc161d223a..9afcdfa9058c 100644 --- a/Specs/Scene/BillboardCollectionSpec.js +++ b/Specs/Scene/BillboardCollectionSpec.js @@ -6,7 +6,9 @@ defineSuite([ 'Core/Cartesian3', 'Core/CesiumTerrainProvider', 'Core/Color', + 'Core/createGuid', 'Core/DistanceDisplayCondition', + 'Core/FeatureDetection', 'Core/Math', 'Core/NearFarScalar', 'Core/OrthographicOffCenterFrustum', @@ -31,7 +33,9 @@ defineSuite([ Cartesian3, CesiumTerrainProvider, Color, + createGuid, DistanceDisplayCondition, + FeatureDetection, CesiumMath, NearFarScalar, OrthographicOffCenterFrustum, @@ -65,19 +69,23 @@ defineSuite([ context = scene.context; camera = scene.camera; - return when.join( - Resource.fetchImage('./Data/Images/Green2x2.png').then(function(result) { - greenImage = result; - }), - Resource.fetchImage('./Data/Images/Blue2x2.png').then(function(result) { - blueImage = result; - }), - Resource.fetchImage('./Data/Images/White2x2.png').then(function(result) { - whiteImage = result; - }), - Resource.fetchImage('./Data/Images/Blue10x10.png').then(function(result) { - largeBlueImage = result; - })); + return FeatureDetection.supportsImageBitmapOptions() + .then(function() { + return when.join( + Resource.fetchImage('./Data/Images/Green2x2.png').then(function(result) { + greenImage = result; + }), + Resource.fetchImage('./Data/Images/Blue2x2.png').then(function(result) { + blueImage = result; + }), + Resource.fetchImage('./Data/Images/White2x2.png').then(function(result) { + whiteImage = result; + }), + Resource.fetchImage('./Data/Images/Blue10x10.png').then(function(result) { + largeBlueImage = result; + }) + ); + }); }); afterAll(function() { @@ -174,7 +182,7 @@ defineSuite([ expect(b.horizontalOrigin).toEqual(HorizontalOrigin.LEFT); expect(b.verticalOrigin).toEqual(VerticalOrigin.BOTTOM); expect(b.scale).toEqual(2.0); - expect(b.image).toEqual(greenImage.src); + expect(b.image).toEqual(b._imageId); expect(b.color.red).toEqual(1.0); expect(b.color.green).toEqual(2.0); expect(b.color.blue).toEqual(3.0); @@ -221,7 +229,7 @@ defineSuite([ expect(b.horizontalOrigin).toEqual(HorizontalOrigin.LEFT); expect(b.verticalOrigin).toEqual(VerticalOrigin.BOTTOM); expect(b.scale).toEqual(2.0); - expect(b.image).toEqual(greenImage.src); + expect(b.image).toEqual(b._imageId); expect(b.color.red).toEqual(1.0); expect(b.color.green).toEqual(2.0); expect(b.color.blue).toEqual(3.0); @@ -822,7 +830,7 @@ defineSuite([ expect(scene).toRender([0, 255, 0, 255]); - b.setImage(largeBlueImage.src, largeBlueImage); + b.setImage(createGuid(), largeBlueImage); expect(scene).toRender([0, 0, 255, 255]); }); @@ -834,8 +842,10 @@ defineSuite([ expect(scene).toRender([0, 255, 0, 255]); - billboards.textureAtlas.addImage(largeBlueImage.src, largeBlueImage); - b.setImageSubRegion(largeBlueImage.src, new BoundingRectangle(5.0, 5.0, 1.0, 1.0)); + var guid = createGuid(); + + billboards.textureAtlas.addImage(guid, largeBlueImage); + b.setImageSubRegion(guid, new BoundingRectangle(5.0, 5.0, 1.0, 1.0)); expect(scene).toRender([0, 0, 255, 255]); }); diff --git a/Specs/Scene/BingMapsImageryProviderSpec.js b/Specs/Scene/BingMapsImageryProviderSpec.js index d6ad4385659b..31fa49304401 100644 --- a/Specs/Scene/BingMapsImageryProviderSpec.js +++ b/Specs/Scene/BingMapsImageryProviderSpec.js @@ -3,6 +3,7 @@ defineSuite([ 'Core/appendForwardSlash', 'Core/DefaultProxy', 'Core/defined', + 'Core/FeatureDetection', 'Core/queryToObject', 'Core/RequestScheduler', 'Core/Resource', @@ -14,12 +15,14 @@ defineSuite([ 'Scene/ImageryProvider', 'Scene/ImageryState', 'Specs/pollToPromise', + 'Specs/isImageOrImageBitmap', 'ThirdParty/Uri' ], function( BingMapsImageryProvider, appendForwardSlash, DefaultProxy, defined, + FeatureDetection, queryToObject, RequestScheduler, Resource, @@ -31,9 +34,14 @@ defineSuite([ ImageryProvider, ImageryState, pollToPromise, + isImageOrImageBitmap, Uri) { 'use strict'; + beforeAll(function() { + return FeatureDetection.supportsImageBitmapOptions(); + }); + beforeEach(function() { RequestScheduler.clearForSpecs(); }); @@ -362,7 +370,7 @@ defineSuite([ }); return provider.requestImage(0, 0, 0).then(function(image) { - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -393,7 +401,7 @@ defineSuite([ }); return provider.requestImage(0, 0, 0).then(function(image) { - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -482,7 +490,7 @@ defineSuite([ return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; }).then(function() { - expect(imagery.image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(imagery.image)).toBe(true); expect(tries).toEqual(2); imagery.releaseReference(); }); diff --git a/Specs/Scene/GoogleEarthEnterpriseImageryProviderSpec.js b/Specs/Scene/GoogleEarthEnterpriseImageryProviderSpec.js index 26d201f4baa1..7fde0a564c6c 100644 --- a/Specs/Scene/GoogleEarthEnterpriseImageryProviderSpec.js +++ b/Specs/Scene/GoogleEarthEnterpriseImageryProviderSpec.js @@ -4,6 +4,7 @@ defineSuite([ 'Core/DefaultProxy', 'Core/defaultValue', 'Core/defined', + 'Core/FeatureDetection', 'Core/GeographicTilingScheme', 'Core/GoogleEarthEnterpriseMetadata', 'Core/GoogleEarthEnterpriseTileInformation', @@ -16,6 +17,7 @@ defineSuite([ 'Scene/ImageryProvider', 'Scene/ImageryState', 'Specs/pollToPromise', + 'Specs/isImageOrImageBitmap', 'ThirdParty/Uri', 'ThirdParty/when' ], function( @@ -24,6 +26,7 @@ defineSuite([ DefaultProxy, defaultValue, defined, + FeatureDetection, GeographicTilingScheme, GoogleEarthEnterpriseMetadata, GoogleEarthEnterpriseTileInformation, @@ -36,6 +39,7 @@ defineSuite([ ImageryProvider, ImageryState, pollToPromise, + isImageOrImageBitmap, Uri, when) { 'use strict'; @@ -46,6 +50,7 @@ defineSuite([ beforeAll(function() { decodeGoogleEarthEnterpriseData.passThroughDataForTesting = true; + return FeatureDetection.supportsImageBitmapOptions(); }); afterAll(function() { @@ -224,7 +229,7 @@ defineSuite([ installFakeImageRequest('http://fake.fake.invalid/flatfile?f1-03-i.1'); return imageryProvider.requestImage(0, 0, 0).then(function(image) { - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -294,7 +299,7 @@ defineSuite([ return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; }).then(function() { - expect(imagery.image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(imagery.image)).toBe(true); expect(tries).toEqual(2); imagery.releaseReference(); }); diff --git a/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js b/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js index 4915de445d52..40496cff3975 100644 --- a/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js +++ b/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js @@ -1,6 +1,7 @@ defineSuite([ 'Scene/GoogleEarthEnterpriseMapsProvider', 'Core/DefaultProxy', + 'Core/FeatureDetection', 'Core/GeographicTilingScheme', 'Core/Rectangle', 'Core/RequestScheduler', @@ -10,10 +11,12 @@ defineSuite([ 'Scene/ImageryLayer', 'Scene/ImageryProvider', 'Scene/ImageryState', - 'Specs/pollToPromise' + 'Specs/pollToPromise', + 'Specs/isImageOrImageBitmap' ], function( GoogleEarthEnterpriseMapsProvider, DefaultProxy, + FeatureDetection, GeographicTilingScheme, Rectangle, RequestScheduler, @@ -23,9 +26,14 @@ defineSuite([ ImageryLayer, ImageryProvider, ImageryState, - pollToPromise) { + pollToPromise, + isImageOrImageBitmap) { 'use strict'; + beforeAll(function() { + return FeatureDetection.supportsImageBitmapOptions(); + }); + afterEach(function() { Resource._Implementations.createImage = Resource._DefaultImplementations.createImage; Resource._Implementations.loadWithXhr = Resource._DefaultImplementations.loadWithXhr; @@ -191,7 +199,7 @@ defineSuite([ }; return provider.requestImage(0, 0, 0).then(function(image) { - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -327,7 +335,7 @@ defineSuite([ return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; }).then(function() { - expect(imagery.image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(imagery.image)).toBe(true); expect(tries).toEqual(2); imagery.releaseReference(); }); diff --git a/Specs/Scene/MapboxImageryProviderSpec.js b/Specs/Scene/MapboxImageryProviderSpec.js index f5800da3d40f..eef7fbc1dd49 100644 --- a/Specs/Scene/MapboxImageryProviderSpec.js +++ b/Specs/Scene/MapboxImageryProviderSpec.js @@ -1,6 +1,7 @@ defineSuite([ 'Scene/MapboxImageryProvider', 'Core/DefaultProxy', + 'Core/FeatureDetection', 'Core/Math', 'Core/Rectangle', 'Core/RequestScheduler', @@ -10,10 +11,12 @@ defineSuite([ 'Scene/ImageryLayer', 'Scene/ImageryProvider', 'Scene/ImageryState', - 'Specs/pollToPromise' + 'Specs/pollToPromise', + 'Specs/isImageOrImageBitmap' ], function( MapboxImageryProvider, DefaultProxy, + FeatureDetection, CesiumMath, Rectangle, RequestScheduler, @@ -23,9 +26,14 @@ defineSuite([ ImageryLayer, ImageryProvider, ImageryState, - pollToPromise) { + pollToPromise, + isImageOrImageBitmap) { 'use strict'; + beforeAll(function() { + return FeatureDetection.supportsImageBitmapOptions(); + }); + beforeEach(function() { RequestScheduler.clearForSpecs(); }); @@ -103,7 +111,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -126,7 +134,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -155,7 +163,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -187,7 +195,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -271,7 +279,7 @@ defineSuite([ return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; }).then(function() { - expect(imagery.image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(imagery.image)).toBe(true); expect(tries).toEqual(2); imagery.releaseReference(); }); diff --git a/Specs/Scene/SingleTileImageryProviderSpec.js b/Specs/Scene/SingleTileImageryProviderSpec.js index bd060548b22a..1482014aed4d 100644 --- a/Specs/Scene/SingleTileImageryProviderSpec.js +++ b/Specs/Scene/SingleTileImageryProviderSpec.js @@ -2,6 +2,7 @@ defineSuite([ 'Scene/SingleTileImageryProvider', 'Core/DefaultProxy', 'Core/Ellipsoid', + 'Core/FeatureDetection', 'Core/GeographicTilingScheme', 'Core/Rectangle', 'Core/Resource', @@ -10,11 +11,13 @@ defineSuite([ 'Scene/ImageryProvider', 'Scene/ImageryState', 'Specs/pollToPromise', + 'Specs/isImageOrImageBitmap', 'ThirdParty/when' ], function( SingleTileImageryProvider, DefaultProxy, Ellipsoid, + FeatureDetection, GeographicTilingScheme, Rectangle, Resource, @@ -23,9 +26,14 @@ defineSuite([ ImageryProvider, ImageryState, pollToPromise, + isImageOrImageBitmap, when) { 'use strict'; + beforeAll(function() { + return FeatureDetection.supportsImageBitmapOptions(); + }); + afterEach(function() { Resource._Implementations.createImage = Resource._DefaultImplementations.createImage; }); @@ -150,7 +158,7 @@ defineSuite([ return provider.ready; }).then(function() { return when(provider.requestImage(0, 0, 0), function(image) { - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -216,7 +224,7 @@ defineSuite([ return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; }).then(function() { - expect(imagery.image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(imagery.image)).toBe(true); expect(tries).toEqual(2); imagery.releaseReference(); }); diff --git a/Specs/Scene/TextureAtlasSpec.js b/Specs/Scene/TextureAtlasSpec.js index def26465a7ef..ef47f4b813b5 100644 --- a/Specs/Scene/TextureAtlasSpec.js +++ b/Specs/Scene/TextureAtlasSpec.js @@ -2,6 +2,8 @@ defineSuite([ 'Scene/TextureAtlas', 'Core/BoundingRectangle', 'Core/Cartesian2', + 'Core/createGuid', + 'Core/FeatureDetection', 'Core/Math', 'Core/PixelFormat', 'Core/Resource', @@ -11,6 +13,8 @@ defineSuite([ TextureAtlas, BoundingRectangle, Cartesian2, + createGuid, + FeatureDetection, CesiumMath, PixelFormat, Resource, @@ -26,29 +30,39 @@ defineSuite([ var bigRedImage; var bigBlueImage; var bigGreenImage; + var guidArray; beforeAll(function() { scene = createScene(); - - return when.join( - Resource.fetchImage('./Data/Images/Green.png').then(function(image) { - greenImage = image; - }), - Resource.fetchImage('./Data/Images/Green1x4.png').then(function(image) { - tallGreenImage = image; - }), - Resource.fetchImage('./Data/Images/Blue.png').then(function(image) { - blueImage = image; - }), - Resource.fetchImage('./Data/Images/Red16x16.png').then(function(image) { - bigRedImage = image; - }), - Resource.fetchImage('./Data/Images/Blue10x10.png').then(function(image) { - bigBlueImage = image; - }), - Resource.fetchImage('./Data/Images/Green4x4.png').then(function(image) { - bigGreenImage = image; - })); + // Create enough guid's to use throughout these tests. + guidArray = []; + for (var i = 0; i < 4; ++i) { + guidArray.push(createGuid()); + } + + return FeatureDetection.supportsImageBitmapOptions() + .then(function() { + return when.join( + Resource.fetchImage('./Data/Images/Green.png').then(function(image) { + greenImage = image; + }), + Resource.fetchImage('./Data/Images/Green1x4.png').then(function(image) { + tallGreenImage = image; + }), + Resource.fetchImage('./Data/Images/Blue.png').then(function(image) { + blueImage = image; + }), + Resource.fetchImage('./Data/Images/Red16x16.png').then(function(image) { + bigRedImage = image; + }), + Resource.fetchImage('./Data/Images/Blue10x10.png').then(function(image) { + bigBlueImage = image; + }), + Resource.fetchImage('./Data/Images/Green4x4.png').then(function(image) { + bigGreenImage = image; + }) + ); + }); }); afterAll(function() { @@ -87,7 +101,7 @@ defineSuite([ initialSize : new Cartesian2(1, 1) }); - return atlas.addImage(greenImage.src, greenImage).then(function(index) { + return atlas.addImage(guidArray[0], greenImage).then(function(index) { expect(index).toEqual(0); expect(atlas.numberOfImages).toEqual(1); @@ -115,7 +129,7 @@ defineSuite([ initialSize : new Cartesian2(1, 1) }); - return atlas.addImage(greenImage.src, greenImage).then(function(index) { + return atlas.addImage(guidArray[0], greenImage).then(function(index) { var texture = atlas.texture; var coords = atlas.textureCoordinates[index]; @@ -128,7 +142,7 @@ defineSuite([ context : scene.context }); - return atlas.addImage(greenImage.src, greenImage).then(function(index) { + return atlas.addImage(guidArray[0], greenImage).then(function(index) { expect(index).toEqual(0); expect(atlas.numberOfImages).toEqual(1); @@ -155,7 +169,7 @@ defineSuite([ context : scene.context }); - return atlas.addImage(greenImage.src, greenImage).then(function(index) { + return atlas.addImage(guidArray[0], greenImage).then(function(index) { var texture = atlas.texture; var coords = atlas.textureCoordinates[index]; @@ -170,7 +184,7 @@ defineSuite([ initialSize : new Cartesian2(1.0, 5.0) }); - return atlas.addImage(tallGreenImage.src, tallGreenImage).then(function(index) { + return atlas.addImage(guidArray[0], tallGreenImage).then(function(index) { expect(index).toEqual(0); expect(atlas.numberOfImages).toEqual(1); @@ -197,7 +211,7 @@ defineSuite([ initialSize : new Cartesian2(1.0, 5.0) }); - return atlas.addImage(tallGreenImage.src, tallGreenImage).then(function(index) { + return atlas.addImage(guidArray[0], tallGreenImage).then(function(index) { var texture = atlas.texture; var coords = atlas.textureCoordinates[index]; @@ -213,8 +227,8 @@ defineSuite([ }); var promises = []; - promises.push(atlas.addImage(greenImage.src, greenImage)); - promises.push(atlas.addImage(blueImage.src, blueImage)); + promises.push(atlas.addImage(guidArray[0], greenImage)); + promises.push(atlas.addImage(guidArray[1], blueImage)); return when.all(promises, function(indices) { var greenIndex = indices[0]; @@ -250,8 +264,8 @@ defineSuite([ }); var promises = []; - promises.push(atlas.addImage(greenImage.src, greenImage)); - promises.push(atlas.addImage(blueImage.src, blueImage)); + promises.push(atlas.addImage(guidArray[0], greenImage)); + promises.push(atlas.addImage(guidArray[1], blueImage)); return when.all(promises, function(indices) { var greenIndex = indices[0]; @@ -274,10 +288,10 @@ defineSuite([ }); var promises = []; - promises.push(atlas.addImage(greenImage.src, greenImage)); - promises.push(atlas.addImage(blueImage.src, blueImage)); - promises.push(atlas.addImage(bigRedImage.src, bigRedImage)); - promises.push(atlas.addImage(bigBlueImage.src, bigBlueImage)); + promises.push(atlas.addImage(guidArray[0], greenImage)); + promises.push(atlas.addImage(guidArray[1], blueImage)); + promises.push(atlas.addImage(guidArray[2], bigRedImage)); + promises.push(atlas.addImage(guidArray[3], bigBlueImage)); return when.all(promises, function(indices) { var greenIndex = indices.shift(); @@ -307,10 +321,10 @@ defineSuite([ }); var promises = []; - promises.push(atlas.addImage(greenImage.src, greenImage)); - promises.push(atlas.addImage(blueImage.src, blueImage)); - promises.push(atlas.addImage(bigRedImage.src, bigRedImage)); - promises.push(atlas.addImage(bigBlueImage.src, bigBlueImage)); + promises.push(atlas.addImage(guidArray[0], greenImage)); + promises.push(atlas.addImage(guidArray[1], blueImage)); + promises.push(atlas.addImage(guidArray[2], bigRedImage)); + promises.push(atlas.addImage(guidArray[3], bigBlueImage)); return when.all(promises, function(indices) { var greenIndex = indices.shift(); @@ -361,10 +375,10 @@ defineSuite([ }); var promises = []; - promises.push(atlas.addImage(greenImage.src, greenImage)); - promises.push(atlas.addImage(blueImage.src, blueImage)); - promises.push(atlas.addImage(bigRedImage.src, bigRedImage)); - promises.push(atlas.addImage(bigBlueImage.src, bigBlueImage)); + promises.push(atlas.addImage(guidArray[0], greenImage)); + promises.push(atlas.addImage(guidArray[1], blueImage)); + promises.push(atlas.addImage(guidArray[2], bigRedImage)); + promises.push(atlas.addImage(guidArray[3], bigBlueImage)); return when.all(promises, function(indices) { var greenIndex = indices.shift(); @@ -394,7 +408,7 @@ defineSuite([ initialSize : new Cartesian2(1, 1) }); - return atlas.addImage(blueImage.src, blueImage).then(function(blueIndex) { + return atlas.addImage(guidArray[0], blueImage).then(function(blueIndex) { expect(atlas.numberOfImages).toEqual(1); var texture = atlas.texture; @@ -412,7 +426,7 @@ defineSuite([ expect(coordinates[blueIndex].height).toEqual(1.0 / atlasHeight); //Add the big green image - return atlas.addImage(bigGreenImage.src, bigGreenImage).then(function(greenIndex) { + return atlas.addImage(guidArray[1], bigGreenImage).then(function(greenIndex) { expect(atlas.numberOfImages).toEqual(2); var texture = atlas.texture; @@ -445,7 +459,7 @@ defineSuite([ initialSize : new Cartesian2(1, 1) }); - return atlas.addImage(blueImage.src, blueImage).then(function(blueIndex) { + return atlas.addImage(guidArray[0], blueImage).then(function(blueIndex) { expect(atlas.numberOfImages).toEqual(1); var texture = atlas.texture; @@ -454,7 +468,7 @@ defineSuite([ var blueCoords = coordinates[blueIndex]; expectToRender(texture, blueCoords, [0, 0, 255, 255]); - return atlas.addImage(bigGreenImage.src, bigGreenImage).then(function(greenIndex) { + return atlas.addImage(guidArray[1], bigGreenImage).then(function(greenIndex) { expect(atlas.numberOfImages).toEqual(2); var texture = atlas.texture; @@ -476,7 +490,7 @@ defineSuite([ initialSize : new Cartesian2(1, 1) }); - return atlas.addImage(bigRedImage.src, bigRedImage).then(function(index) { + return atlas.addImage(guidArray[0], bigRedImage).then(function(index) { expect(atlas.numberOfImages).toEqual(1); var texture = atlas.texture; @@ -501,7 +515,7 @@ defineSuite([ initialSize : new Cartesian2(1, 1) }); - return atlas.addImage(bigRedImage.src, bigRedImage).then(function(index) { + return atlas.addImage(guidArray[0], bigRedImage).then(function(index) { var texture = atlas.texture; var coords = atlas.textureCoordinates[index]; @@ -516,8 +530,8 @@ defineSuite([ initialSize : new Cartesian2(2, 2) }); - var greenPromise = atlas.addImage(greenImage.src, greenImage); - var bluePromise = atlas.addImage(blueImage.src, blueImage); + var greenPromise = atlas.addImage(guidArray[0], greenImage); + var bluePromise = atlas.addImage(guidArray[1], blueImage); return when.all([greenPromise, bluePromise], function(indices) { var greenIndex = indices.shift(); @@ -552,8 +566,8 @@ defineSuite([ initialSize : new Cartesian2(2, 2) }); - var greenPromise = atlas.addImage(greenImage.src, greenImage); - var bluePromise = atlas.addImage(blueImage.src, blueImage); + var greenPromise = atlas.addImage(guidArray[0], greenImage); + var bluePromise = atlas.addImage(guidArray[1], blueImage); return when.all([greenPromise, bluePromise], function(indices) { var greenIndex = indices.shift(); @@ -577,7 +591,7 @@ defineSuite([ initialSize : new Cartesian2(1.0, 1.0) }); - return atlas.addImage(tallGreenImage.src, tallGreenImage).then(function(index) { + return atlas.addImage(guidArray[0], tallGreenImage).then(function(index) { expect(atlas.numberOfImages).toEqual(1); var texture = atlas.texture; @@ -602,7 +616,7 @@ defineSuite([ initialSize : new Cartesian2(1.0, 1.0) }); - return atlas.addImage(tallGreenImage.src, tallGreenImage).then(function(index) { + return atlas.addImage(guidArray[0], tallGreenImage).then(function(index) { var texture = atlas.texture; var coords = atlas.textureCoordinates[index]; @@ -617,9 +631,9 @@ defineSuite([ initialSize : new Cartesian2(1, 1) }); - var bluePromise = atlas.addImage(blueImage.src, blueImage); - var bigGreenPromise = atlas.addImage(bigGreenImage.src, bigGreenImage); - var bigRedPromise = atlas.addImage(bigRedImage.src, bigRedImage); + var bluePromise = atlas.addImage(guidArray[0], blueImage); + var bigGreenPromise = atlas.addImage(guidArray[1], bigGreenImage); + var bigRedPromise = atlas.addImage(guidArray[2], bigRedImage); return when.all([bluePromise, bigGreenPromise, bigRedPromise], function(indices) { var blueIndex = indices.shift(); @@ -644,13 +658,13 @@ defineSuite([ initialSize : new Cartesian2(4, 4) }); - return atlas.addImage(blueImage.src, blueImage).then(function(blueIndex) { + return atlas.addImage(guidArray[0], blueImage).then(function(blueIndex) { expect(blueIndex).toEqual(0); - return atlas.addImage(greenImage.src, greenImage).then(function(greenIndex) { + return atlas.addImage(guidArray[1], greenImage).then(function(greenIndex) { expect(greenIndex).toEqual(1); - return atlas.addImage(blueImage.src, blueImage).then(function(index) { + return atlas.addImage(guidArray[0], blueImage).then(function(index) { expect(index).toEqual(blueIndex); expect(atlas.numberOfImages).toEqual(2); @@ -675,12 +689,14 @@ defineSuite([ initialSize : new Cartesian2(1, 1) }); - atlas.addImage(greenImage.src, greenImage); + var id = guidArray[0]; - var promise1 = atlas.addSubRegion(greenImage.src, new BoundingRectangle(0.0, 0.0, 0.5, 0.5)); - var promise2 = atlas.addSubRegion(greenImage.src, new BoundingRectangle(0.0, 0.5, 0.5, 0.5)); - var promise3 = atlas.addSubRegion(greenImage.src, new BoundingRectangle(0.5, 0.0, 0.5, 0.5)); - var promise4 = atlas.addSubRegion(greenImage.src, new BoundingRectangle(0.5, 0.5, 0.5, 0.5)); + atlas.addImage(id, greenImage); + + var promise1 = atlas.addSubRegion(id, new BoundingRectangle(0.0, 0.0, 0.5, 0.5)); + var promise2 = atlas.addSubRegion(id, new BoundingRectangle(0.0, 0.5, 0.5, 0.5)); + var promise3 = atlas.addSubRegion(id, new BoundingRectangle(0.5, 0.0, 0.5, 0.5)); + var promise4 = atlas.addSubRegion(id, new BoundingRectangle(0.5, 0.5, 0.5, 0.5)); return when.all([promise1, promise2, promise3, promise4], function(indices) { var index1 = indices.shift(); @@ -723,12 +739,14 @@ defineSuite([ initialSize : new Cartesian2(1, 1) }); - atlas.addImage(greenImage.src, greenImage); + var id = guidArray[0]; + + atlas.addImage(id, greenImage); - var promise1 = atlas.addSubRegion(greenImage.src, new BoundingRectangle(0.0, 0.0, 0.5, 0.5)); - var promise2 = atlas.addSubRegion(greenImage.src, new BoundingRectangle(0.0, 0.5, 0.5, 0.5)); - var promise3 = atlas.addSubRegion(greenImage.src, new BoundingRectangle(0.5, 0.0, 0.5, 0.5)); - var promise4 = atlas.addSubRegion(greenImage.src, new BoundingRectangle(0.5, 0.5, 0.5, 0.5)); + var promise1 = atlas.addSubRegion(id, new BoundingRectangle(0.0, 0.0, 0.5, 0.5)); + var promise2 = atlas.addSubRegion(id, new BoundingRectangle(0.0, 0.5, 0.5, 0.5)); + var promise3 = atlas.addSubRegion(id, new BoundingRectangle(0.5, 0.0, 0.5, 0.5)); + var promise4 = atlas.addSubRegion(id, new BoundingRectangle(0.5, 0.5, 0.5, 0.5)); return when.all([promise1, promise2, promise3, promise4], function(indices) { var index1 = indices.shift(); @@ -738,7 +756,7 @@ defineSuite([ expect(atlas.numberOfImages).toEqual(5); - return atlas.addImage(blueImage.src, blueImage).then(function(blueIndex) { + return atlas.addImage(guidArray[1], blueImage).then(function(blueIndex) { expect(atlas.numberOfImages).toEqual(6); var coordinates = atlas.textureCoordinates; @@ -819,11 +837,11 @@ defineSuite([ var guid1 = atlas.guid; - return atlas.addImage(greenImage.src, greenImage).then(function(index) { + return atlas.addImage(guidArray[0], greenImage).then(function(index) { var guid2 = atlas.guid; expect(guid1).not.toEqual(guid2); - return atlas.addSubRegion(greenImage.src, new BoundingRectangle(0.0, 0.0, 0.5, 0.5)).then(function(index) { + return atlas.addSubRegion(guidArray[0], new BoundingRectangle(0.0, 0.0, 0.5, 0.5)).then(function(index) { var guid3 = atlas.guid; expect(guid2).not.toEqual(guid3); }); diff --git a/Specs/Scene/UrlTemplateImageryProviderSpec.js b/Specs/Scene/UrlTemplateImageryProviderSpec.js index 24070350b008..8158ccc3797a 100644 --- a/Specs/Scene/UrlTemplateImageryProviderSpec.js +++ b/Specs/Scene/UrlTemplateImageryProviderSpec.js @@ -2,6 +2,7 @@ defineSuite([ 'Scene/UrlTemplateImageryProvider', 'Core/DefaultProxy', 'Core/Ellipsoid', + 'Core/FeatureDetection', 'Core/GeographicTilingScheme', 'Core/Math', 'Core/Rectangle', @@ -15,11 +16,13 @@ defineSuite([ 'Scene/ImageryProvider', 'Scene/ImageryState', 'Specs/pollToPromise', + 'Specs/isImageOrImageBitmap', 'ThirdParty/when' ], function( UrlTemplateImageryProvider, DefaultProxy, Ellipsoid, + FeatureDetection, GeographicTilingScheme, CesiumMath, Rectangle, @@ -33,9 +36,14 @@ defineSuite([ ImageryProvider, ImageryState, pollToPromise, + isImageOrImageBitmap, when) { 'use strict'; + beforeAll(function() { + return FeatureDetection.supportsImageBitmapOptions(); + }); + beforeEach(function() { RequestScheduler.clearForSpecs(); }); @@ -117,7 +125,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -164,7 +172,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -226,7 +234,7 @@ defineSuite([ return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; }).then(function() { - expect(imagery.image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(imagery.image)).toBe(true); expect(tries).toEqual(2); imagery.releaseReference(); }); @@ -252,7 +260,7 @@ defineSuite([ return provider.requestImage(3, 1, 2).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -281,7 +289,7 @@ defineSuite([ return provider.requestImage(3, 1, 2).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -310,7 +318,7 @@ defineSuite([ return provider.requestImage(3, 1, 2).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -339,7 +347,7 @@ defineSuite([ return provider.requestImage(12, 10, 5).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -362,7 +370,7 @@ defineSuite([ return provider.requestImage(3, 1, 2).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -385,7 +393,7 @@ defineSuite([ return provider.requestImage(3, 1, 2).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -408,7 +416,7 @@ defineSuite([ return provider.requestImage(3, 1, 2).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -431,7 +439,7 @@ defineSuite([ return provider.requestImage(3, 1, 2).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -454,7 +462,7 @@ defineSuite([ return provider.requestImage(3, 1, 2).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -476,7 +484,7 @@ defineSuite([ return provider.requestImage(3, 0, 2).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -498,7 +506,7 @@ defineSuite([ return provider.requestImage(0, 1, 2).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -520,7 +528,7 @@ defineSuite([ return provider.requestImage(1, 1, 2).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -550,7 +558,7 @@ defineSuite([ return provider.requestImage(1, 1, 2).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -572,7 +580,7 @@ defineSuite([ return provider.requestImage(3, 1, 2).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -595,7 +603,7 @@ defineSuite([ return provider.requestImage(3, 1, 2).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -618,7 +626,7 @@ defineSuite([ return provider.requestImage(3, 1, 2).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -646,7 +654,7 @@ defineSuite([ return provider.requestImage(3, 1, 2).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); diff --git a/Specs/Scene/WebMapServiceImageryProviderSpec.js b/Specs/Scene/WebMapServiceImageryProviderSpec.js index 887388d89fa2..94d59764d672 100644 --- a/Specs/Scene/WebMapServiceImageryProviderSpec.js +++ b/Specs/Scene/WebMapServiceImageryProviderSpec.js @@ -5,6 +5,7 @@ defineSuite([ 'Core/ClockStep', 'Core/DefaultProxy', 'Core/Ellipsoid', + 'Core/FeatureDetection', 'Core/GeographicTilingScheme', 'Core/JulianDate', 'Core/Math', @@ -23,6 +24,7 @@ defineSuite([ 'Scene/ImageryProvider', 'Scene/ImageryState', 'Specs/pollToPromise', + 'Specs/isImageOrImageBitmap', 'ThirdParty/Uri' ], function( WebMapServiceImageryProvider, @@ -31,6 +33,7 @@ defineSuite([ ClockStep, DefaultProxy, Ellipsoid, + FeatureDetection, GeographicTilingScheme, JulianDate, CesiumMath, @@ -49,9 +52,14 @@ defineSuite([ ImageryProvider, ImageryState, pollToPromise, + isImageOrImageBitmap, Uri) { 'use strict'; + beforeAll(function() { + return FeatureDetection.supportsImageBitmapOptions(); + }); + beforeEach(function() { RequestScheduler.clearForSpecs(); }); @@ -412,7 +420,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -452,7 +460,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -495,7 +503,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -535,7 +543,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -578,7 +586,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -621,7 +629,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -664,7 +672,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -822,7 +830,7 @@ defineSuite([ return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; }).then(function() { - expect(imagery.image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(imagery.image)).toBe(true); expect(tries).toEqual(2); imagery.releaseReference(); }); diff --git a/Specs/Scene/WebMapTileServiceImageryProviderSpec.js b/Specs/Scene/WebMapTileServiceImageryProviderSpec.js index 0eb664879f7b..d06d28f6322d 100644 --- a/Specs/Scene/WebMapTileServiceImageryProviderSpec.js +++ b/Specs/Scene/WebMapTileServiceImageryProviderSpec.js @@ -4,6 +4,7 @@ defineSuite([ 'Core/ClockStep', 'Core/Credit', 'Core/DefaultProxy', + 'Core/FeatureDetection', 'Core/GeographicTilingScheme', 'Core/JulianDate', 'Core/objectToQuery', @@ -19,6 +20,7 @@ defineSuite([ 'Scene/ImageryProvider', 'Scene/ImageryState', 'Specs/pollToPromise', + 'Specs/isImageOrImageBitmap', 'ThirdParty/Uri' ], function( WebMapTileServiceImageryProvider, @@ -26,6 +28,7 @@ defineSuite([ ClockStep, Credit, DefaultProxy, + FeatureDetection, GeographicTilingScheme, JulianDate, objectToQuery, @@ -41,9 +44,14 @@ defineSuite([ ImageryProvider, ImageryState, pollToPromise, + isImageOrImageBitmap, Uri) { 'use strict'; + beforeAll(function() { + return FeatureDetection.supportsImageBitmapOptions(); + }); + beforeEach(function() { RequestScheduler.clearForSpecs(); }); @@ -364,7 +372,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -414,7 +422,7 @@ defineSuite([ return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; }).then(function() { - expect(imagery.image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(imagery.image)).toBe(true); expect(tries).toEqual(2); imagery.releaseReference(); }); diff --git a/Specs/Scene/createOpenStreetMapImageryProviderSpec.js b/Specs/Scene/createOpenStreetMapImageryProviderSpec.js index 86064b000f57..3d6c327a4f58 100644 --- a/Specs/Scene/createOpenStreetMapImageryProviderSpec.js +++ b/Specs/Scene/createOpenStreetMapImageryProviderSpec.js @@ -1,6 +1,7 @@ defineSuite([ 'Scene/createOpenStreetMapImageryProvider', 'Core/DefaultProxy', + 'Core/FeatureDetection', 'Core/Math', 'Core/Rectangle', 'Core/RequestScheduler', @@ -10,10 +11,12 @@ defineSuite([ 'Scene/ImageryLayer', 'Scene/ImageryState', 'Scene/UrlTemplateImageryProvider', - 'Specs/pollToPromise' + 'Specs/pollToPromise', + 'Specs/isImageOrImageBitmap' ], function( createOpenStreetMapImageryProvider, DefaultProxy, + FeatureDetection, CesiumMath, Rectangle, RequestScheduler, @@ -23,9 +26,14 @@ defineSuite([ ImageryLayer, ImageryState, UrlTemplateImageryProvider, - pollToPromise) { + pollToPromise, + isImageOrImageBitmap) { 'use strict'; + beforeAll(function() { + return FeatureDetection.supportsImageBitmapOptions(); + }); + beforeEach(function() { RequestScheduler.clearForSpecs(); }); @@ -72,7 +80,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -94,7 +102,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -116,7 +124,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -144,7 +152,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -193,7 +201,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -256,7 +264,7 @@ defineSuite([ return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; }).then(function() { - expect(imagery.image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(imagery.image)).toBe(true); expect(tries).toEqual(2); imagery.releaseReference(); }); diff --git a/Specs/Scene/createTileMapServiceImageryProviderSpec.js b/Specs/Scene/createTileMapServiceImageryProviderSpec.js index f68f4651d4d2..f32f7866adf0 100644 --- a/Specs/Scene/createTileMapServiceImageryProviderSpec.js +++ b/Specs/Scene/createTileMapServiceImageryProviderSpec.js @@ -3,6 +3,7 @@ defineSuite([ 'Core/Cartesian2', 'Core/Cartographic', 'Core/DefaultProxy', + 'Core/FeatureDetection', 'Core/GeographicProjection', 'Core/GeographicTilingScheme', 'Core/getAbsoluteUri', @@ -17,12 +18,14 @@ defineSuite([ 'Scene/ImageryState', 'Scene/UrlTemplateImageryProvider', 'Specs/pollToPromise', + 'Specs/isImageOrImageBitmap', 'ThirdParty/when' ], function( createTileMapServiceImageryProvider, Cartesian2, Cartographic, DefaultProxy, + FeatureDetection, GeographicProjection, GeographicTilingScheme, getAbsoluteUri, @@ -37,9 +40,14 @@ defineSuite([ ImageryState, UrlTemplateImageryProvider, pollToPromise, + isImageOrImageBitmap, when) { 'use strict'; + beforeAll(function() { + return FeatureDetection.supportsImageBitmapOptions(); + }); + beforeEach(function() { RequestScheduler.clearForSpecs(); }); @@ -215,7 +223,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -237,7 +245,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -260,7 +268,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -287,7 +295,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -362,7 +370,7 @@ defineSuite([ return provider.requestImage(0, 0, 0).then(function(image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); - expect(image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(image)).toBe(true); }); }); }); @@ -422,7 +430,7 @@ defineSuite([ return pollToPromise(function() { return imagery.state === ImageryState.RECEIVED; }).then(function() { - expect(imagery.image).toBeInstanceOf(Image); + expect(isImageOrImageBitmap(imagery.image)).toBe(true); expect(tries).toEqual(2); imagery.releaseReference(); }); diff --git a/Specs/isImageOrImageBitmap.js b/Specs/isImageOrImageBitmap.js new file mode 100644 index 000000000000..810cf452548c --- /dev/null +++ b/Specs/isImageOrImageBitmap.js @@ -0,0 +1,19 @@ +define([ + 'Core/FeatureDetection' + ], function( + FeatureDetection) { + 'use strict'; + + function isImageOrImageBitmap(image) { + // Many ImageryProvider specs will test if the requested image + // succeeded by checking its instance. Since this may be an Image + // or ImageBitmap, we abstract this check here. + if (FeatureDetection.supportsCreateImageBitmap()) { + return image instanceof ImageBitmap; + } + + return image instanceof Image; + } + + return isImageOrImageBitmap; +}); From e2108db3fbaf99bceb03011ecdb5bffb354f7816 Mon Sep 17 00:00:00 2001 From: Shehata Date: Mon, 18 Feb 2019 12:32:18 -0500 Subject: [PATCH 05/16] Remove unnecessary comment [ci skip] --- Source/Core/Resource.js | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Core/Resource.js b/Source/Core/Resource.js index 6600f8ae9065..397d1ab39e27 100644 --- a/Source/Core/Resource.js +++ b/Source/Core/Resource.js @@ -1786,7 +1786,6 @@ define([ credentials: (crossOrigin && TrustedServers.contains(url)) ? 'include' : 'same-origin' }) .then(function(response) { - // Catch error here https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful return response.blob(); }) .then(function(blob) { From 7336d9684dff660a63c0a69a8e72dedcca8296c2 Mon Sep 17 00:00:00 2001 From: Shehata Date: Mon, 18 Feb 2019 12:38:21 -0500 Subject: [PATCH 06/16] Update CHANGES [ci skip] --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 4c81820e8f41..81b74c33a539 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,7 @@ Change Log ##### Additions :tada: * `czm_materialInput.aspect` was added as an angle in radians between 0 and 2pi (east, north, west to south). +* Add support for decoding images while fetching using `createImageBitmap` to greatly speed up texture upload and decrease frame drops when loading models with large textures. `Resource` now has a `flipImage` option to vertically flip an image during fetch & decode when `ImageBitmapOptions` are supported. [#7579](https://github.com/AnalyticalGraphicsInc/cesium/pull/7579) ##### Fixes :wrench: * Fixed an issue where models would cause a crash on load if some primitives were Draco encoded and others were not. [#7383](https://github.com/AnalyticalGraphicsInc/cesium/issues/7383) From e31eae334a4f8ddafe520ff59c0d2bf2ba16a33f Mon Sep 17 00:00:00 2001 From: Shehata Date: Mon, 18 Feb 2019 13:03:23 -0500 Subject: [PATCH 07/16] Make ImageBitmapOptions spec more robust --- Specs/Core/FeatureDetectionSpec.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Specs/Core/FeatureDetectionSpec.js b/Specs/Core/FeatureDetectionSpec.js index b2f84ab7a69c..faf287f39748 100644 --- a/Specs/Core/FeatureDetectionSpec.js +++ b/Specs/Core/FeatureDetectionSpec.js @@ -135,10 +135,6 @@ defineSuite([ }); it('detects ImageBitmapOptions support', function() { - if (FeatureDetection.supportsCreateImageBitmap()) { - expect(FeatureDetection.supportsImageBitmapOptionsSync()).not.toBeDefined(); - } - return FeatureDetection.supportsImageBitmapOptions() .then(function(supportsImageBitmapOptions) { expect(typeof supportsImageBitmapOptions).toEqual('boolean'); From 3fe7b79d575424021dfcf2492d0014d23de2b6e2 Mon Sep 17 00:00:00 2001 From: Shehata Date: Tue, 19 Feb 2019 09:53:36 -0500 Subject: [PATCH 08/16] Use fetchBlob & remove flipImage in Resource --- CHANGES.md | 2 +- Source/Core/FeatureDetection.js | 11 +-- Source/Core/Resource.js | 79 ++++++++----------- Source/Scene/Model.js | 7 +- Specs/Core/FeatureDetectionSpec.js | 5 -- Specs/Core/ResourceSpec.js | 31 +------- Specs/Core/VRTheWorldTerrainProviderSpec.js | 15 +++- Specs/Renderer/TextureSpec.js | 4 +- .../ArcGisMapServerImageryProviderSpec.js | 12 +-- Specs/Scene/BingMapsImageryProviderSpec.js | 4 +- .../DiscardMissingTileImagePolicySpec.js | 10 ++- .../GoogleEarthEnterpriseMapsProviderSpec.js | 8 +- 12 files changed, 77 insertions(+), 111 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 81b74c33a539..e5c3a928ab79 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,7 +8,7 @@ Change Log ##### Additions :tada: * `czm_materialInput.aspect` was added as an angle in radians between 0 and 2pi (east, north, west to south). -* Add support for decoding images while fetching using `createImageBitmap` to greatly speed up texture upload and decrease frame drops when loading models with large textures. `Resource` now has a `flipImage` option to vertically flip an image during fetch & decode when `ImageBitmapOptions` are supported. [#7579](https://github.com/AnalyticalGraphicsInc/cesium/pull/7579) +* Add support for decoding images while fetching using `createImageBitmap` to greatly speed up texture upload and decrease frame drops when loading models with large textures. `Resource.fetchImage` now has a `flipY` option to vertically flip an image during fetch & decode when `ImageBitmapOptions` are supported since an `ImageBitmap` cannot be vertically flipped during texture upload. [#7579](https://github.com/AnalyticalGraphicsInc/cesium/pull/7579) ##### Fixes :wrench: * Fixed an issue where models would cause a crash on load if some primitives were Draco encoded and others were not. [#7383](https://github.com/AnalyticalGraphicsInc/cesium/issues/7383) diff --git a/Source/Core/FeatureDetection.js b/Source/Core/FeatureDetection.js index 941f7b8a9958..6f3647a7b76b 100644 --- a/Source/Core/FeatureDetection.js +++ b/Source/Core/FeatureDetection.js @@ -281,6 +281,8 @@ define([ } var imageDataUri = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWP4////fwAJ+wP9CNHoHgAAAABJRU5ErkJggg=='; + // Use the fetch API here to avoid a circular dependency from including Resource here. + // `fetch` is supported everywhere createImageBitmap is. fetch(imageDataUri) .then(function(response) { return response.blob(); @@ -310,14 +312,6 @@ define([ return supportsImageBitmapOptionsResult; } - var supportsFetchApiResult; - function supportsFetchApi() { - if (!defined(supportsFetchApiResult)) { - supportsFetchApiResult = defined(window.fetch); - } - return supportsFetchApiResult; - } - /** * A set of functions to detect whether the current browser supports * various features. @@ -346,7 +340,6 @@ define([ supportsCreateImageBitmap: supportsCreateImageBitmap, supportsImageBitmapOptions: supportsImageBitmapOptions, supportsImageBitmapOptionsSync: supportsImageBitmapOptionsSync, - supportsFetchApi : supportsFetchApi, imageRenderingValue: imageRenderingValue, typedArrayTypes: typedArrayTypes }; diff --git a/Source/Core/Resource.js b/Source/Core/Resource.js index 397d1ab39e27..8416e32464fe 100644 --- a/Source/Core/Resource.js +++ b/Source/Core/Resource.js @@ -252,7 +252,6 @@ define([ * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. - * @param {Boolean} [options.flipImage] Whether to vertically flip the image during fetch and decode. Only applies when requesting an image and the browser supports createImageBitmap. * @param {Resource~RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. @@ -339,14 +338,6 @@ define([ this.retryAttempts = defaultValue(options.retryAttempts, 0); this._retryCount = 0; - /** - * Whether to vertically flip the image during fetch and decode. Only applies when requesting - * an image and the browser supports createImageBitmap. - * - * @type {Boolean} - */ - this.flipImage = defaultValue(options.flipImage, true); - var uri = new Uri(options.url); parseQuery(uri, this, true, true); @@ -631,7 +622,6 @@ define([ * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). These will be combined with those of the current instance. * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. - * @param {Boolean} [options.flipImage] Whether to vertically flip the image during fetch and decode. Only applies when requesting an image and the browser supports createImageBitmap. * @param {Resource~RetryCallback} [options.retryCallback] The function to call when loading the resource fails. * @param {Number} [options.retryAttempts] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. @@ -676,9 +666,6 @@ define([ if (defined(options.retryAttempts)) { resource.retryAttempts = options.retryAttempts; } - if (defined(options.flipImage)) { - resource.flipImage = options.flipImage; - } return resource; }; @@ -729,7 +716,6 @@ define([ result.retryCallback = this.retryCallback; result.retryAttempts = this.retryAttempts; result._retryCount = 0; - result.flipImage = this.flipImage; result.request = this.request.clone(); return result; @@ -787,7 +773,6 @@ define([ * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. - * @param {Boolean} [options.flipImage] Whether to vertically flip the image during fetch and decode. Only applies when requesting an image and the browser supports createImageBitmap. * @param {Resource~RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. @@ -832,7 +817,6 @@ define([ * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. - * @param {Boolean} [options.flipImage] Whether to vertically flip the image during fetch and decode. Only applies when requesting an image and the browser supports createImageBitmap. * @param {Resource~RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. @@ -845,10 +829,13 @@ define([ /** * Asynchronously loads the given image resource. Returns a promise that will resolve to - * an {@link Image} once loaded, or reject if the image failed to load. + * an {@link https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap|ImageBitmap} if the browser supports `createImageBitmap` or otherwise an + * {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement|Image} once loaded, or reject if the image failed to load. * - * @param {Boolean} [preferBlob = false] If true, we will load the image via a blob. - * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. + * @param {String|Object} options An object with the following properties. + * @param {Boolean} [options.preferBlob=false] If true, we will load the image via a blob. + * @param {Boolean} [options.flipY=true] If true, image will be vertially flipped during decode. Only applies if the browser supports `createImageBitmap`. + * @returns {Promise.|Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * * @example @@ -867,9 +854,10 @@ define([ * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ - Resource.prototype.fetchImage = function (preferBlob) { - preferBlob = defaultValue(preferBlob, false); - var flipImage = this.flipImage; + Resource.prototype.fetchImage = function (options) { + options = defaultValue(options, {}); + var preferBlob = defaultValue(options.preferBlob, false); + var flipY = defaultValue(options.flipY, true); checkAndResetRequest(this.request); @@ -879,7 +867,7 @@ define([ // 3. It's a blob URI // 4. It doesn't have request headers and we preferBlob is false if (!xhrBlobSupported || this.isDataUri || this.isBlobUri || (!this.hasHeaders && !preferBlob)) { - return fetchImage(this); + return fetchImage(this, flipY); } var blobPromise = this.fetchBlob(); @@ -897,11 +885,10 @@ define([ generatedBlob = blob; var blobUrl = window.URL.createObjectURL(blob); generatedBlobResource = new Resource({ - url: blobUrl, - flipImage: flipImage + url: blobUrl }); - return fetchImage(generatedBlobResource); + return fetchImage(generatedBlobResource, flipY); }) .then(function(image) { if (!defined(image)) { @@ -923,12 +910,11 @@ define([ }); }; - function fetchImage(resource) { + function fetchImage(resource, flipY) { var request = resource.request; request.url = resource.url; request.requestFunction = function() { var url = resource.url; - var flipImage = resource.flipImage; var crossOrigin = false; // data URIs can't have crossorigin set. @@ -938,7 +924,7 @@ define([ var deferred = when.defer(); - Resource._Implementations.createImage(url, crossOrigin, deferred, flipImage); + Resource._Implementations.createImage(url, crossOrigin, deferred, flipY); return deferred.promise; }; @@ -962,7 +948,7 @@ define([ request.state = RequestState.UNISSUED; request.deferred = undefined; - return fetchImage(resource); + return fetchImage(resource, flipY); } return when.reject(e); @@ -979,7 +965,7 @@ define([ * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. - * @param {Boolean} [options.flipImage] Whether to vertically flip the image during fetch and decode. Only applies when requesting an image and the browser supports createImageBitmap. + * @param {Boolean} [options.flipY = true] Whether to vertically flip the image during fetch and decode. Only applies when requesting an image and the browser supports createImageBitmap. * @param {Resource~RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. @@ -988,7 +974,10 @@ define([ */ Resource.fetchImage = function (options) { var resource = new Resource(options); - return resource.fetchImage(options.preferBlob); + return resource.fetchImage({ + flipY: options.flipY, + preferBlob: options.preferBlob + }); }; /** @@ -1400,7 +1389,6 @@ define([ * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {DefaultProxy} [options.proxy] A proxy to be used when loading the resource. - * @param {Boolean} [options.flipImage] Whether to vertically flip the image during fetch and decode. Only applies when requesting an image and the browser supports createImageBitmap. * @param {Resource~RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. @@ -1778,29 +1766,24 @@ define([ */ Resource._Implementations = {}; - Resource._Implementations.createImage = function(url, crossOrigin, deferred, flipImage) { + Resource._Implementations.createImage = function(url, crossOrigin, deferred, flipY) { var supportsBitmapOptions = FeatureDetection.supportsImageBitmapOptionsSync(); - if (FeatureDetection.supportsFetchApi() && FeatureDetection.supportsCreateImageBitmap() && defined(supportsBitmapOptions)) { - fetch(url, { - credentials: (crossOrigin && TrustedServers.contains(url)) ? 'include' : 'same-origin' + if (FeatureDetection.supportsCreateImageBitmap() && defined(supportsBitmapOptions)) { + Resource.fetchBlob({ + url: url }) - .then(function(response) { - return response.blob(); - }) .then(function(blob) { if (!supportsBitmapOptions) { - return createImageBitmap(blob); + return when(createImageBitmap(blob)); } - return createImageBitmap(blob, { - imageOrientation: flipImage ? 'flipY' : 'none' - }); - }) - .then(function (imageBitmap) { - deferred.resolve(imageBitmap); + return when(createImageBitmap(blob, { + imageOrientation: flipY ? 'flipY' : 'none' + })); }) - .catch(deferred.reject); + .then(deferred.resolve) + .otherwise(deferred.reject); } else { var image = new Image(); diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index e7d81bdac386..aa1bbffb94c1 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -1714,8 +1714,7 @@ define([ ++model._loadResources.pendingTextureLoads; var imageResource = model._resource.getDerivedResource({ - url : uri, - flipImage : false + url : uri }); var promise; @@ -1724,7 +1723,9 @@ define([ } else if (crnRegex.test(uri)) { promise = loadCRN(imageResource); } else { - promise = imageResource.fetchImage(); + promise = imageResource.fetchImage({ + flipY: false + }); } promise.then(imageLoad(model, id, imageId)).otherwise(ModelUtility.getFailedLoadFunction(model, 'image', imageResource.url)); } diff --git a/Specs/Core/FeatureDetectionSpec.js b/Specs/Core/FeatureDetectionSpec.js index faf287f39748..a1dd850c005b 100644 --- a/Specs/Core/FeatureDetectionSpec.js +++ b/Specs/Core/FeatureDetectionSpec.js @@ -124,11 +124,6 @@ defineSuite([ }); }); - it('detects fetch API support', function() { - var supportsFetchApi = FeatureDetection.supportsFetchApi(); - expect(typeof supportsFetchApi).toEqual('boolean'); - }); - it('detects createImageBitmap support', function() { var supportsCreateImageBitmap = FeatureDetection.supportsCreateImageBitmap(); expect(typeof supportsCreateImageBitmap).toEqual('boolean'); diff --git a/Specs/Core/ResourceSpec.js b/Specs/Core/ResourceSpec.js index 1357aabbdfb1..3a27fd7380ce 100644 --- a/Specs/Core/ResourceSpec.js +++ b/Specs/Core/ResourceSpec.js @@ -1191,7 +1191,7 @@ defineSuite([ it('correctly flips image when ImageBitmapOptions are supported', function() { return Resource.fetchImage({ url: './Data/Images/BlueOverRed.png', - flipImage: true + flipY: true }).then(function(loadedImage) { if (FeatureDetection.supportsImageBitmapOptionsSync()) { expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([255, 0, 0, 255]); @@ -1204,7 +1204,7 @@ defineSuite([ it('correctly loads image without flip when ImageBitmapOptions are supported', function() { return Resource.fetchImage({ url: './Data/Images/BlueOverRed.png', - flipImage: false + flipY: false }).then(function(loadedImage) { if (FeatureDetection.supportsImageBitmapOptionsSync()) { expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([0, 0, 255, 255]); @@ -1229,32 +1229,7 @@ defineSuite([ fail('expected promise to reject'); }) .otherwise(function(error) { - expect(error).toBeInstanceOf(TypeError); - }); - }); - - it('does not set credentials for untrusted cross-origin images using fetch API', function() { - var fetchSpy = spyOn(window, 'fetch').and.callThrough(); - var url = './Data/Images/Green.png'; - - return Resource.fetchImage(url) - .then(function() { - expect(fetchSpy).toHaveBeenCalledWith(url, { - credentials: 'same-origin' - }); - }); - }); - - it('sets credentials for trusted cross-origin images using fetch API', function() { - var fetchSpy = spyOn(window, 'fetch').and.callThrough(); - spyOn(TrustedServers, 'contains').and.returnValue(true); - var url = 'http://example.invalid/testuri.png'; - - return Resource.fetchImage(url) - .otherwise(function() { - expect(fetchSpy).toHaveBeenCalledWith(url, { - credentials: 'include' - }); + expect(error).toBeInstanceOf(RequestErrorEvent); }); }); }); diff --git a/Specs/Core/VRTheWorldTerrainProviderSpec.js b/Specs/Core/VRTheWorldTerrainProviderSpec.js index 00b0f4f6b953..d1e6d47b6efb 100644 --- a/Specs/Core/VRTheWorldTerrainProviderSpec.js +++ b/Specs/Core/VRTheWorldTerrainProviderSpec.js @@ -1,6 +1,7 @@ defineSuite([ 'Core/VRTheWorldTerrainProvider', 'Core/DefaultProxy', + 'Core/FeatureDetection', 'Core/GeographicTilingScheme', 'Core/HeightmapTerrainData', 'Core/Math', @@ -13,6 +14,7 @@ defineSuite([ ], function( VRTheWorldTerrainProvider, DefaultProxy, + FeatureDetection, GeographicTilingScheme, HeightmapTerrainData, CesiumMath, @@ -24,9 +26,16 @@ defineSuite([ when) { 'use strict'; + var imageUrl = 'Data/Images/Red16x16.png'; + beforeEach(function() { RequestScheduler.clearForSpecs(); Resource._Implementations.loadWithXhr = function(url, responseType, method, data, headers, deferred, overrideMimeType) { + if (url === imageUrl) { + Resource._DefaultImplementations.loadWithXhr(url, responseType, method, data, headers, deferred, overrideMimeType); + return; + } + setTimeout(function() { var parser = new DOMParser(); var xmlString = @@ -60,6 +69,10 @@ defineSuite([ Resource._Implementations.loadWithXhr = Resource._DefaultImplementations.loadWithXhr; }); + beforeAll(function() { + return FeatureDetection.supportsImageBitmapOptions(); + }); + function createRequest() { return new Request({ throttleByServer : true @@ -239,7 +252,7 @@ defineSuite([ expect(url.indexOf('.tif?cesium=true')).toBeGreaterThanOrEqualTo(0); // Just return any old image. - Resource._DefaultImplementations.createImage('Data/Images/Red16x16.png', crossOrigin, deferred); + Resource._DefaultImplementations.createImage(imageUrl, crossOrigin, deferred); }; var terrainProvider = new VRTheWorldTerrainProvider({ diff --git a/Specs/Renderer/TextureSpec.js b/Specs/Renderer/TextureSpec.js index cb8b269c34c8..99b3dc68e268 100644 --- a/Specs/Renderer/TextureSpec.js +++ b/Specs/Renderer/TextureSpec.js @@ -76,7 +76,7 @@ defineSuite([ // Turn off the default flipping. promises.push(Resource.fetchImage({ url: './Data/Images/BlueOverRed.png', - flipImage: false + flipY: false }).then(function(image) { blueOverRedUnflippedImage = image; })); @@ -199,7 +199,7 @@ defineSuite([ }).contextToRender([0, 0, 255, 255]); }); - it('can set flip texture only if ImageBitmapOptions is not supported', function() { + it('can flip texture only if ImageBitmapOptions is not supported', function() { var topColor = new Color(0.0, 0.0, 1.0, 1.0); var bottomColor = new Color(1.0, 0.0, 0.0, 1.0); if (FeatureDetection.supportsImageBitmapOptionsSync()) { diff --git a/Specs/Scene/ArcGisMapServerImageryProviderSpec.js b/Specs/Scene/ArcGisMapServerImageryProviderSpec.js index 1d0742fe497c..095df47274da 100644 --- a/Specs/Scene/ArcGisMapServerImageryProviderSpec.js +++ b/Specs/Scene/ArcGisMapServerImageryProviderSpec.js @@ -214,8 +214,8 @@ defineSuite([ expect(provider.hasAlphaChannel).toBeDefined(); Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url)) { - // load blob url normally + if (/^blob:/.test(url) || FeatureDetection.supportsImageBitmapOptionsSync()) { + // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { expect(url).toEqual(getAbsoluteUri(baseUrl + 'tile/0/0/0')); @@ -291,8 +291,8 @@ defineSuite([ expect(provider.usingPrecachedTiles).toEqual(true); Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url)) { - // load blob url normally + if (/^blob:/.test(url) || FeatureDetection.supportsImageBitmapOptionsSync()) { + // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { expect(url).toEqual(getAbsoluteUri(baseUrl + 'tile/0/0/0')); @@ -464,8 +464,8 @@ defineSuite([ expect(provider.hasAlphaChannel).toBeDefined(); Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url)) { - // load blob url normally + if (/^blob:/.test(url) || FeatureDetection.supportsImageBitmapOptionsSync()) { + // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { expect(url).toEqual(expectedTileUrl); diff --git a/Specs/Scene/BingMapsImageryProviderSpec.js b/Specs/Scene/BingMapsImageryProviderSpec.js index 31fa49304401..f652ea83c201 100644 --- a/Specs/Scene/BingMapsImageryProviderSpec.js +++ b/Specs/Scene/BingMapsImageryProviderSpec.js @@ -181,8 +181,8 @@ defineSuite([ function installFakeImageRequest(expectedUrl, expectedParams, proxy) { Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url)) { - // load blob url normally + if (/^blob:/.test(url) || FeatureDetection.supportsImageBitmapOptionsSync()) { + // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { if (defined(expectedUrl)) { diff --git a/Specs/Scene/DiscardMissingTileImagePolicySpec.js b/Specs/Scene/DiscardMissingTileImagePolicySpec.js index 2c5215941a31..f18218944fbf 100644 --- a/Specs/Scene/DiscardMissingTileImagePolicySpec.js +++ b/Specs/Scene/DiscardMissingTileImagePolicySpec.js @@ -1,12 +1,14 @@ defineSuite([ 'Scene/DiscardMissingTileImagePolicy', 'Core/Cartesian2', + 'Core/FeatureDetection', 'Core/Resource', 'Specs/pollToPromise', 'ThirdParty/when' ], function( DiscardMissingTileImagePolicy, Cartesian2, + FeatureDetection, Resource, pollToPromise, when) { @@ -17,6 +19,10 @@ defineSuite([ Resource._Implementations.loadWithXhr = Resource._DefaultImplementations.loadWithXhr; }); + beforeAll(function() { + return FeatureDetection.supportsImageBitmapOptions(); + }); + describe('construction', function() { it('throws if missingImageUrl is not provided', function() { function constructWithoutMissingImageUrl() { @@ -40,8 +46,8 @@ defineSuite([ var missingImageUrl = 'http://some.host.invalid/missingImage.png'; spyOn(Resource._Implementations, 'createImage').and.callFake(function(url, crossOrigin, deferred) { - if (/^blob:/.test(url)) { - // load blob url normally + if (/^blob:/.test(url) || FeatureDetection.supportsImageBitmapOptionsSync()) { + // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { expect(url).toEqual(missingImageUrl); diff --git a/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js b/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js index 40496cff3975..1ac41204a074 100644 --- a/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js +++ b/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js @@ -180,8 +180,8 @@ defineSuite([ expect(provider.credit).toBeInstanceOf(Object); Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url)) { - // load blob url normally + if (/^blob:/.test(url) || FeatureDetection.supportsImageBitmapOptionsSync()) { + // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { expect(url).toEqual('http://example.invalid/query?request=ImageryMaps&channel=1234&version=1&x=0&y=0&z=1'); @@ -298,8 +298,8 @@ defineSuite([ }); Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url)) { - // load blob url normally + if (/^blob:/.test(url) || FeatureDetection.supportsImageBitmapOptionsSync()) { + // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else if (tries === 2) { // Succeed after 2 tries From 5cdd9e1fbabf98e6b010eb008757308831120d06 Mon Sep 17 00:00:00 2001 From: Shehata Date: Tue, 19 Feb 2019 10:54:23 -0500 Subject: [PATCH 09/16] Fix imagery specs on Firefox --- Specs/Scene/ArcGisMapServerImageryProviderSpec.js | 6 +++--- Specs/Scene/BingMapsImageryProviderSpec.js | 2 +- Specs/Scene/DiscardMissingTileImagePolicySpec.js | 2 +- Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Specs/Scene/ArcGisMapServerImageryProviderSpec.js b/Specs/Scene/ArcGisMapServerImageryProviderSpec.js index 095df47274da..7ba901a13cd8 100644 --- a/Specs/Scene/ArcGisMapServerImageryProviderSpec.js +++ b/Specs/Scene/ArcGisMapServerImageryProviderSpec.js @@ -214,7 +214,7 @@ defineSuite([ expect(provider.hasAlphaChannel).toBeDefined(); Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url) || FeatureDetection.supportsImageBitmapOptionsSync()) { + if (/^blob:/.test(url) || FeatureDetection.supportsCreateImageBitmap()) { // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { @@ -291,7 +291,7 @@ defineSuite([ expect(provider.usingPrecachedTiles).toEqual(true); Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url) || FeatureDetection.supportsImageBitmapOptionsSync()) { + if (/^blob:/.test(url) || FeatureDetection.supportsCreateImageBitmap()) { // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { @@ -464,7 +464,7 @@ defineSuite([ expect(provider.hasAlphaChannel).toBeDefined(); Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url) || FeatureDetection.supportsImageBitmapOptionsSync()) { + if (/^blob:/.test(url) || FeatureDetection.supportsCreateImageBitmap()) { // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { diff --git a/Specs/Scene/BingMapsImageryProviderSpec.js b/Specs/Scene/BingMapsImageryProviderSpec.js index f652ea83c201..faf273881718 100644 --- a/Specs/Scene/BingMapsImageryProviderSpec.js +++ b/Specs/Scene/BingMapsImageryProviderSpec.js @@ -181,7 +181,7 @@ defineSuite([ function installFakeImageRequest(expectedUrl, expectedParams, proxy) { Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url) || FeatureDetection.supportsImageBitmapOptionsSync()) { + if (/^blob:/.test(url) || FeatureDetection.supportsCreateImageBitmap()) { // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { diff --git a/Specs/Scene/DiscardMissingTileImagePolicySpec.js b/Specs/Scene/DiscardMissingTileImagePolicySpec.js index f18218944fbf..85b207a0eeaf 100644 --- a/Specs/Scene/DiscardMissingTileImagePolicySpec.js +++ b/Specs/Scene/DiscardMissingTileImagePolicySpec.js @@ -46,7 +46,7 @@ defineSuite([ var missingImageUrl = 'http://some.host.invalid/missingImage.png'; spyOn(Resource._Implementations, 'createImage').and.callFake(function(url, crossOrigin, deferred) { - if (/^blob:/.test(url) || FeatureDetection.supportsImageBitmapOptionsSync()) { + if (/^blob:/.test(url) || FeatureDetection.supportsCreateImageBitmap()) { // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { diff --git a/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js b/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js index 1ac41204a074..3c5c7325ea24 100644 --- a/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js +++ b/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js @@ -180,7 +180,7 @@ defineSuite([ expect(provider.credit).toBeInstanceOf(Object); Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url) || FeatureDetection.supportsImageBitmapOptionsSync()) { + if (/^blob:/.test(url) || FeatureDetection.supportsCreateImageBitmap()) { // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { @@ -298,7 +298,7 @@ defineSuite([ }); Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url) || FeatureDetection.supportsImageBitmapOptionsSync()) { + if (/^blob:/.test(url) || FeatureDetection.supportsCreateImageBitmap()) { // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else if (tries === 2) { From 112c34574667abae78b476613848112a020eda22 Mon Sep 17 00:00:00 2001 From: Shehata Date: Tue, 19 Feb 2019 17:34:09 -0500 Subject: [PATCH 10/16] Refactor imageBitmapOptions check --- CHANGES.md | 3 + Source/Core/FeatureDetection.js | 52 ------- Source/Core/IonResource.js | 9 +- Source/Core/Resource.js | 100 +++++++++++--- Source/Core/loadImageFromTypedArray.js | 22 +-- Source/Scene/DiscardMissingTileImagePolicy.js | 4 +- Source/Scene/ImageryProvider.js | 4 +- Source/Widgets/Viewer/Viewer.js | 6 - Specs/Core/FeatureDetectionSpec.js | 8 -- Specs/Core/IonResourceSpec.js | 14 +- Specs/Core/ResourceSpec.js | 35 ++--- Specs/Core/VRTheWorldTerrainProviderSpec.js | 6 - Specs/Core/loadImageFromTypedArraySpec.js | 50 +++---- Specs/Renderer/TextureSpec.js | 129 +++++++++--------- .../ArcGisMapServerImageryProviderSpec.js | 7 +- Specs/Scene/BillboardCollectionSpec.js | 33 ++--- Specs/Scene/BingMapsImageryProviderSpec.js | 4 - .../DiscardMissingTileImagePolicySpec.js | 21 ++- ...oogleEarthEnterpriseImageryProviderSpec.js | 3 - .../GoogleEarthEnterpriseMapsProviderSpec.js | 4 - Specs/Scene/MapboxImageryProviderSpec.js | 6 - Specs/Scene/SingleTileImageryProviderSpec.js | 6 - Specs/Scene/TextureAtlasSpec.js | 45 +++--- Specs/Scene/UrlTemplateImageryProviderSpec.js | 6 - .../Scene/WebMapServiceImageryProviderSpec.js | 6 - .../WebMapTileServiceImageryProviderSpec.js | 6 - .../createOpenStreetMapImageryProviderSpec.js | 6 - ...createTileMapServiceImageryProviderSpec.js | 6 - 28 files changed, 277 insertions(+), 324 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e5c3a928ab79..6fade07e29d5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,9 @@ Change Log * `czm_materialInput.aspect` was added as an angle in radians between 0 and 2pi (east, north, west to south). * Add support for decoding images while fetching using `createImageBitmap` to greatly speed up texture upload and decrease frame drops when loading models with large textures. `Resource.fetchImage` now has a `flipY` option to vertically flip an image during fetch & decode when `ImageBitmapOptions` are supported since an `ImageBitmap` cannot be vertically flipped during texture upload. [#7579](https://github.com/AnalyticalGraphicsInc/cesium/pull/7579) +##### Deprecated :hourglass_flowing_sand: +* `Resource.fetchImage` now takes an options object. Use `resource.fetchImage({ preferBlob: true })` instead of `resource.fetchImage(true)`. The previous function definition will no longer work in 1.56. [#7579](https://github.com/AnalyticalGraphicsInc/cesium/pull/7579) + ##### Fixes :wrench: * Fixed an issue where models would cause a crash on load if some primitives were Draco encoded and others were not. [#7383](https://github.com/AnalyticalGraphicsInc/cesium/issues/7383) * Fixed Node.js support for the `Resource` class and any functionality using it internally. diff --git a/Source/Core/FeatureDetection.js b/Source/Core/FeatureDetection.js index 6f3647a7b76b..cd0d56800b13 100644 --- a/Source/Core/FeatureDetection.js +++ b/Source/Core/FeatureDetection.js @@ -262,56 +262,6 @@ define([ return supportsCreateImageBitmapResult; } - var supportsImageBitmapOptionsResult; - var supportsImageBitmapOptionsPromise; - function supportsImageBitmapOptions() { - // Until the HTML folks figure out what to do about this, we need to actually try loading an image to - // know if this browser supports passing options to the createImageBitmap function. - // https://github.com/whatwg/html/pull/4248 - if (defined(supportsImageBitmapOptionsPromise)) { - return supportsImageBitmapOptionsPromise.promise; - } - - supportsImageBitmapOptionsPromise = when.defer(); - - if (!supportsCreateImageBitmap()) { - supportsImageBitmapOptionsResult = false; - supportsImageBitmapOptionsPromise.resolve(supportsImageBitmapOptionsResult); - return supportsImageBitmapOptionsPromise.promise; - } - - var imageDataUri = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWP4////fwAJ+wP9CNHoHgAAAABJRU5ErkJggg=='; - // Use the fetch API here to avoid a circular dependency from including Resource here. - // `fetch` is supported everywhere createImageBitmap is. - fetch(imageDataUri) - .then(function(response) { - return response.blob(); - }) - .then(function(blob) { - return createImageBitmap(blob, { - imageOrientation: 'flipY' - }); - }) - .then(function(imageBitmap) { - supportsImageBitmapOptionsResult = true; - supportsImageBitmapOptionsPromise.resolve(supportsImageBitmapOptionsResult); - }) - .catch(function() { - supportsImageBitmapOptionsResult = false; - supportsImageBitmapOptionsPromise.resolve(supportsImageBitmapOptionsResult); - }); - - return supportsImageBitmapOptionsPromise.promise; - } - - function supportsImageBitmapOptionsSync() { - if (!defined(supportsImageBitmapOptionsPromise)) { - supportsImageBitmapOptions(); - } - - return supportsImageBitmapOptionsResult; - } - /** * A set of functions to detect whether the current browser supports * various features. @@ -338,8 +288,6 @@ define([ supportsWebP: supportsWebP, supportsWebPSync: supportsWebPSync, supportsCreateImageBitmap: supportsCreateImageBitmap, - supportsImageBitmapOptions: supportsImageBitmapOptions, - supportsImageBitmapOptionsSync: supportsImageBitmapOptionsSync, imageRenderingValue: imageRenderingValue, typedArrayTypes: typedArrayTypes }; diff --git a/Source/Core/IonResource.js b/Source/Core/IonResource.js index b0800cf4453b..b474f26ca857 100644 --- a/Source/Core/IonResource.js +++ b/Source/Core/IonResource.js @@ -164,8 +164,13 @@ define([ return result; }; - IonResource.prototype.fetchImage = function (preferBlob, allowCrossOrigin) { - return Resource.prototype.fetchImage.call(this, this._isExternal ? preferBlob : true, allowCrossOrigin); + IonResource.prototype.fetchImage = function (options) { + if (!this._isExternal) { + options = defaultValue(options, {}); + options.preferBlob = true; + } + + return Resource.prototype.fetchImage.call(this, options); }; IonResource.prototype._makeRequest = function(options) { diff --git a/Source/Core/Resource.js b/Source/Core/Resource.js index 8416e32464fe..6b315d048367 100644 --- a/Source/Core/Resource.js +++ b/Source/Core/Resource.js @@ -376,6 +376,49 @@ define([ }); }; + var supportsImageBitmapOptionsPromise; + /** + * A helper function to check whether createImageBitmap supports passing ImageBitmapOptions. + * + * @returns {Promise} A promise that resolves to true if this browser supports creating an ImageBitmap with options. + * + * @private + */ + Resource.supportsImageBitmapOptions = function() { + // Until the HTML folks figure out what to do about this, we need to actually try loading an image to + // know if this browser supports passing options to the createImageBitmap function. + // https://github.com/whatwg/html/pull/4248 + if (defined(supportsImageBitmapOptionsPromise)) { + return supportsImageBitmapOptionsPromise.promise; + } + + supportsImageBitmapOptionsPromise = when.defer(); + + if (!FeatureDetection.supportsCreateImageBitmap()) { + supportsImageBitmapOptionsPromise.resolve(false); + return supportsImageBitmapOptionsPromise.promise; + } + + var imageDataUri = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWP4////fwAJ+wP9CNHoHgAAAABJRU5ErkJggg=='; + + Resource.fetchBlob({ + url : imageDataUri + }) + .then(function(blob) { + return when(createImageBitmap(blob, { + imageOrientation: 'flipY' + })); + }) + .then(function(imageBitmap) { + supportsImageBitmapOptionsPromise.resolve(true); + }) + .otherwise(function() { + supportsImageBitmapOptionsPromise.resolve(false); + }); + + return supportsImageBitmapOptionsPromise.promise; + }; + defineProperties(Resource, { /** * Returns true if blobs are supported. @@ -855,6 +898,12 @@ define([ * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ Resource.prototype.fetchImage = function (options) { + if (defined(options) && typeof options === 'boolean') { + deprecationWarning('fetchImage-parameter-change', 'fetchImage now takes an options object in CesiumJS 1.55. Use resource.fetchImage({ preferBlob: true }) instead of resource.fetchImage(true).'); + options = { + preferBlob : options + }; + } options = defaultValue(options, {}); var preferBlob = defaultValue(options.preferBlob, false); var flipY = defaultValue(options.flipY, true); @@ -875,6 +924,15 @@ define([ return; } + if (FeatureDetection.supportsCreateImageBitmap()) { + return blobPromise + .then(function(blob) { + var deferred = when.defer(); + Resource._Implementations.createImageBitmapFromBlob(blob, deferred, flipY); + return deferred; + }); + } + var generatedBlobResource; var generatedBlob; return blobPromise @@ -882,6 +940,7 @@ define([ if (!defined(blob)) { return; } + generatedBlob = blob; var blobUrl = window.URL.createObjectURL(blob); generatedBlobResource = new Resource({ @@ -1767,24 +1826,7 @@ define([ Resource._Implementations = {}; Resource._Implementations.createImage = function(url, crossOrigin, deferred, flipY) { - var supportsBitmapOptions = FeatureDetection.supportsImageBitmapOptionsSync(); - - if (FeatureDetection.supportsCreateImageBitmap() && defined(supportsBitmapOptions)) { - Resource.fetchBlob({ - url: url - }) - .then(function(blob) { - if (!supportsBitmapOptions) { - return when(createImageBitmap(blob)); - } - - return when(createImageBitmap(blob, { - imageOrientation: flipY ? 'flipY' : 'none' - })); - }) - .then(deferred.resolve) - .otherwise(deferred.reject); - } else { + if (!FeatureDetection.supportsCreateImageBitmap()) { var image = new Image(); image.onload = function() { @@ -1804,9 +1846,31 @@ define([ } image.src = url; + } else { + // createImageBitmap will block the main thread during decode if a blob is not supplied. + Resource.fetchBlob({ + url: url + }).then(function(blob) { + Resource._Implementations.createImageBitmapFromBlob(blob, deferred, flipY); + }).otherwise(deferred.reject); } }; + Resource._Implementations.createImageBitmapFromBlob = function(blob, deferred, flipY) { + Resource.supportsImageBitmapOptions() + .then(function(supportsBitmapOptions) { + if (!supportsBitmapOptions) { + return when(createImageBitmap(blob)); + } + + return when(createImageBitmap(blob, { + imageOrientation: flipY ? 'flipY' : 'none' + })); + }) + .then(deferred.resolve) + .otherwise(deferred.reject); + }; + function decodeResponse(loadWithHttpResponse, responseType) { switch (responseType) { case 'text': diff --git a/Source/Core/loadImageFromTypedArray.js b/Source/Core/loadImageFromTypedArray.js index d64f5f781475..987fc07ae461 100644 --- a/Source/Core/loadImageFromTypedArray.js +++ b/Source/Core/loadImageFromTypedArray.js @@ -31,17 +31,19 @@ define([ type : format }); - // We can avoid the extra fetch when createImageBitmap is supported. - var supportsBitmapOptions = FeatureDetection.supportsImageBitmapOptionsSync(); + // Avoid an extra fetch by just calling createImageBitmap here directly on the blob + // instead of sending it to Resource as a blob URL. + if (FeatureDetection.supportsCreateImageBitmap()) { + return Resource.supportsImageBitmapOptions() + .then(function(supportsBitmapOptions) { + if (supportsBitmapOptions) { + return when(createImageBitmap(blob, { + imageOrientation: flipY ? 'flipY' : 'none' + })); + } - if (FeatureDetection.supportsCreateImageBitmap() && defined(supportsBitmapOptions)) { - if (supportsBitmapOptions) { - return when(createImageBitmap(blob, { - imageOrientation: flipY ? 'flipY' : 'none' - })); - } - - return when(createImageBitmap(blob)); + return when(createImageBitmap(blob)); + }); } var blobUrl = window.URL.createObjectURL(blob); diff --git a/Source/Scene/DiscardMissingTileImagePolicy.js b/Source/Scene/DiscardMissingTileImagePolicy.js index ef4c66a18dfb..a8da9e33f071 100644 --- a/Source/Scene/DiscardMissingTileImagePolicy.js +++ b/Source/Scene/DiscardMissingTileImagePolicy.js @@ -89,7 +89,9 @@ define([ that._isReady = true; } - when(resource.fetchImage(true), success, failure); + when(resource.fetchImage({ + preferBlob : true + }), success, failure); } /** diff --git a/Source/Scene/ImageryProvider.js b/Source/Scene/ImageryProvider.js index 61f49b663a56..55d85bb7bfda 100644 --- a/Source/Scene/ImageryProvider.js +++ b/Source/Scene/ImageryProvider.js @@ -344,7 +344,9 @@ define([ } else if (crnRegex.test(resource)) { return loadCRN(resource); } else if (defined(imageryProvider.tileDiscardPolicy)) { - return resource.fetchImage(true); + return resource.fetchImage({ + preferBlob : true + }); } return resource.fetchImage(); diff --git a/Source/Widgets/Viewer/Viewer.js b/Source/Widgets/Viewer/Viewer.js index 5c286670fb43..d9664a7bb540 100644 --- a/Source/Widgets/Viewer/Viewer.js +++ b/Source/Widgets/Viewer/Viewer.js @@ -10,7 +10,6 @@ define([ '../../Core/DeveloperError', '../../Core/Event', '../../Core/EventHelper', - '../../Core/FeatureDetection', '../../Core/HeadingPitchRange', '../../Core/isArray', '../../Core/Matrix4', @@ -61,7 +60,6 @@ define([ DeveloperError, Event, EventHelper, - FeatureDetection, HeadingPitchRange, isArray, Matrix4, @@ -363,10 +361,6 @@ define([ } //>>includeEnd('debug'); - // We kick off this asynchronous test here so we can start using - // createImageBitmap if it's supported as soon as possible. - FeatureDetection.supportsImageBitmapOptions(); - container = getElement(container); options = defaultValue(options, defaultValue.EMPTY_OBJECT); diff --git a/Specs/Core/FeatureDetectionSpec.js b/Specs/Core/FeatureDetectionSpec.js index a1dd850c005b..2ab1238f288e 100644 --- a/Specs/Core/FeatureDetectionSpec.js +++ b/Specs/Core/FeatureDetectionSpec.js @@ -128,12 +128,4 @@ defineSuite([ var supportsCreateImageBitmap = FeatureDetection.supportsCreateImageBitmap(); expect(typeof supportsCreateImageBitmap).toEqual('boolean'); }); - - it('detects ImageBitmapOptions support', function() { - return FeatureDetection.supportsImageBitmapOptions() - .then(function(supportsImageBitmapOptions) { - expect(typeof supportsImageBitmapOptions).toEqual('boolean'); - expect(FeatureDetection.supportsImageBitmapOptionsSync()).toEqual(supportsImageBitmapOptions); - }); - }); }); diff --git a/Specs/Core/IonResourceSpec.js b/Specs/Core/IonResourceSpec.js index 0baf9e621260..f5f5e2f1d72b 100644 --- a/Specs/Core/IonResourceSpec.js +++ b/Specs/Core/IonResourceSpec.js @@ -225,8 +225,10 @@ defineSuite([ var fetchImage = spyOn(Resource.prototype, 'fetchImage'); var endpointResource = IonResource._createEndpointResource(assetId); var resource = new IonResource(endpoint, endpointResource); - resource.fetchImage(false, true); - expect(fetchImage).toHaveBeenCalledWith(true, true); + resource.fetchImage(); + expect(fetchImage).toHaveBeenCalledWith({ + preferBlob : true + }); }); it('Calls base fetchImage with no changes for external assets', function() { @@ -240,8 +242,12 @@ defineSuite([ var fetchImage = spyOn(Resource.prototype, 'fetchImage'); var endpointResource = IonResource._createEndpointResource(assetId); var resource = new IonResource(externalEndpoint, endpointResource); - resource.fetchImage(false, true); - expect(fetchImage).toHaveBeenCalledWith(false, true); + resource.fetchImage({ + preferBlob : false + }); + expect(fetchImage).toHaveBeenCalledWith({ + preferBlob : false + }); }); describe('retryCallback', function() { diff --git a/Specs/Core/ResourceSpec.js b/Specs/Core/ResourceSpec.js index 3a27fd7380ce..cac4e31efe6f 100644 --- a/Specs/Core/ResourceSpec.js +++ b/Specs/Core/ResourceSpec.js @@ -28,10 +28,6 @@ defineSuite([ var dataUri = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIW2Nk+M/wHwAEBgIA5agATwAAAABJRU5ErkJggg=='; - beforeAll(function() { - return FeatureDetection.supportsImageBitmapOptions(); - }); - it('Constructor sets correct properties', function() { var proxy = new DefaultProxy('/proxy/'); var request = new Request(); @@ -1177,23 +1173,17 @@ defineSuite([ }); }); - it('does not call createImageBitmap when ImageBitmapOptions support is not ready', function() { - spyOn(FeatureDetection, 'supportsImageBitmapOptionsSync').and.returnValue(undefined); - spyOn(window, 'createImageBitmap').and.callThrough(); - - return Resource.fetchImage('./Data/Images/Green.png').then(function(loadedImage) { - expect(loadedImage.width).toEqual(1); - expect(loadedImage.height).toEqual(1); - expect(window.createImageBitmap).not.toHaveBeenCalled(); - }); - }); - it('correctly flips image when ImageBitmapOptions are supported', function() { + var loadedImage; + return Resource.fetchImage({ url: './Data/Images/BlueOverRed.png', flipY: true - }).then(function(loadedImage) { - if (FeatureDetection.supportsImageBitmapOptionsSync()) { + }).then(function(image) { + loadedImage = image; + return Resource.supportsImageBitmapOptions(); + }).then(function(supportsImageBitmapOptions) { + if (supportsImageBitmapOptions) { expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([255, 0, 0, 255]); } else { expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([0, 0, 255, 255]); @@ -1202,11 +1192,16 @@ defineSuite([ }); it('correctly loads image without flip when ImageBitmapOptions are supported', function() { + var loadedImage; + return Resource.fetchImage({ url: './Data/Images/BlueOverRed.png', flipY: false - }).then(function(loadedImage) { - if (FeatureDetection.supportsImageBitmapOptionsSync()) { + }).then(function(image) { + loadedImage = image; + return Resource.supportsImageBitmapOptions(); + }).then(function(supportsImageBitmapOptions) { + if (supportsImageBitmapOptions) { expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([0, 0, 255, 255]); } else { expect(getColorAtPixel(loadedImage, 0, 0)).toEqual([0, 0, 255, 255]); @@ -1215,7 +1210,7 @@ defineSuite([ }); it('does not pass options when ImageBitmapOptions are not supported', function() { - spyOn(FeatureDetection, 'supportsImageBitmapOptionsSync').and.returnValue(false); + spyOn(Resource, 'supportsImageBitmapOptions').and.returnValue(when.resolve(false)); spyOn(window, 'createImageBitmap').and.callThrough(); return Resource.fetchImage('./Data/Images/Green.png').then(function(loadedImage) { diff --git a/Specs/Core/VRTheWorldTerrainProviderSpec.js b/Specs/Core/VRTheWorldTerrainProviderSpec.js index d1e6d47b6efb..f47a1d52b56a 100644 --- a/Specs/Core/VRTheWorldTerrainProviderSpec.js +++ b/Specs/Core/VRTheWorldTerrainProviderSpec.js @@ -1,7 +1,6 @@ defineSuite([ 'Core/VRTheWorldTerrainProvider', 'Core/DefaultProxy', - 'Core/FeatureDetection', 'Core/GeographicTilingScheme', 'Core/HeightmapTerrainData', 'Core/Math', @@ -14,7 +13,6 @@ defineSuite([ ], function( VRTheWorldTerrainProvider, DefaultProxy, - FeatureDetection, GeographicTilingScheme, HeightmapTerrainData, CesiumMath, @@ -69,10 +67,6 @@ defineSuite([ Resource._Implementations.loadWithXhr = Resource._DefaultImplementations.loadWithXhr; }); - beforeAll(function() { - return FeatureDetection.supportsImageBitmapOptions(); - }); - function createRequest() { return new Request({ throttleByServer : true diff --git a/Specs/Core/loadImageFromTypedArraySpec.js b/Specs/Core/loadImageFromTypedArraySpec.js index e21b800bdc38..052e3f2d5d06 100644 --- a/Specs/Core/loadImageFromTypedArraySpec.js +++ b/Specs/Core/loadImageFromTypedArraySpec.js @@ -8,10 +8,6 @@ defineSuite([ Resource) { 'use strict'; - beforeAll(function() { - return FeatureDetection.supportsImageBitmapOptions(); - }); - it('can load an image', function() { return Resource.fetchArrayBuffer('./Data/Images/Blue10x10.png').then(function(arrayBuffer) { var options = { @@ -36,29 +32,33 @@ defineSuite([ var blob = new Blob([options.uint8Array], { type : options.format }); + var supportsImageBitmapOptions; - return loadImageFromTypedArray(options).then(function() { - if (FeatureDetection.supportsImageBitmapOptionsSync()) { - expect(window.createImageBitmap).toHaveBeenCalledWith(blob, { - imageOrientation: 'flipY' - }); - } else { - expect(window.createImageBitmap).toHaveBeenCalledWith(blob); - } + return loadImageFromTypedArray(options) + .then(Resource.supportsImageBitmapOptions) + .then(function(result) { + supportsImageBitmapOptions = result; + if (supportsImageBitmapOptions) { + expect(window.createImageBitmap).toHaveBeenCalledWith(blob, { + imageOrientation: 'flipY' + }); + } else { + expect(window.createImageBitmap).toHaveBeenCalledWith(blob); + } - options.flipY = false; - window.createImageBitmap.calls.reset(); - return loadImageFromTypedArray(options); - }) - .then(function() { - if (FeatureDetection.supportsImageBitmapOptionsSync()) { - expect(window.createImageBitmap).toHaveBeenCalledWith(blob, { - imageOrientation: 'none' - }); - } else { - expect(window.createImageBitmap).toHaveBeenCalledWith(blob); - } - }); + options.flipY = false; + window.createImageBitmap.calls.reset(); + return loadImageFromTypedArray(options); + }) + .then(function() { + if (supportsImageBitmapOptions) { + expect(window.createImageBitmap).toHaveBeenCalledWith(blob, { + imageOrientation: 'none' + }); + } else { + expect(window.createImageBitmap).toHaveBeenCalledWith(blob); + } + }); }); it('can load an image when ImageBitmap is not supported', function() { diff --git a/Specs/Renderer/TextureSpec.js b/Specs/Renderer/TextureSpec.js index 99b3dc68e268..26e31c931cdf 100644 --- a/Specs/Renderer/TextureSpec.js +++ b/Specs/Renderer/TextureSpec.js @@ -58,43 +58,40 @@ defineSuite([ beforeAll(function() { context = createContext(); - return FeatureDetection.supportsImageBitmapOptions() - .then(function() { - var promises = []; - promises.push(Resource.fetchImage('./Data/Images/Green.png').then(function(image) { - greenImage = image; - })); - promises.push(Resource.fetchImage('./Data/Images/Blue.png').then(function(image) { - blueImage = image; - })); - promises.push(Resource.fetchImage('./Data/Images/BlueAlpha.png').then(function(image) { - blueAlphaImage = image; - })); - promises.push(Resource.fetchImage('./Data/Images/BlueOverRed.png').then(function(image) { - blueOverRedImage = image; - })); - // Turn off the default flipping. - promises.push(Resource.fetchImage({ - url: './Data/Images/BlueOverRed.png', - flipY: false - }).then(function(image) { - blueOverRedUnflippedImage = image; - })); - promises.push(Resource.fetchImage('./Data/Images/Red16x16.png').then(function(image) { - red16x16Image = image; - })); - promises.push(loadKTX('./Data/Images/Green4x4DXT1.ktx').then(function(image) { - greenDXTImage = image; - })); - promises.push(loadKTX('./Data/Images/Green4x4PVR.ktx').then(function(image) { - greenPVRImage = image; - })); - promises.push(loadKTX('./Data/Images/Green4x4ETC1.ktx').then(function(image) { - greenETC1Image = image; - })); - - return when.all(promises); - }); + var promises = []; + promises.push(Resource.fetchImage('./Data/Images/Green.png').then(function(image) { + greenImage = image; + })); + promises.push(Resource.fetchImage('./Data/Images/Blue.png').then(function(image) { + blueImage = image; + })); + promises.push(Resource.fetchImage('./Data/Images/BlueAlpha.png').then(function(image) { + blueAlphaImage = image; + })); + promises.push(Resource.fetchImage('./Data/Images/BlueOverRed.png').then(function(image) { + blueOverRedImage = image; + })); + // Turn off the default flipping. + promises.push(Resource.fetchImage({ + url: './Data/Images/BlueOverRed.png', + flipY: false + }).then(function(image) { + blueOverRedUnflippedImage = image; + })); + promises.push(Resource.fetchImage('./Data/Images/Red16x16.png').then(function(image) { + red16x16Image = image; + })); + promises.push(loadKTX('./Data/Images/Green4x4DXT1.ktx').then(function(image) { + greenDXTImage = image; + })); + promises.push(loadKTX('./Data/Images/Green4x4PVR.ktx').then(function(image) { + greenPVRImage = image; + })); + promises.push(loadKTX('./Data/Images/Green4x4ETC1.ktx').then(function(image) { + greenETC1Image = image; + })); + + return when.all(promises); }); afterAll(function() { @@ -202,37 +199,41 @@ defineSuite([ it('can flip texture only if ImageBitmapOptions is not supported', function() { var topColor = new Color(0.0, 0.0, 1.0, 1.0); var bottomColor = new Color(1.0, 0.0, 0.0, 1.0); - if (FeatureDetection.supportsImageBitmapOptionsSync()) { - // When imageBitmapOptions is supported, flipY on texture upload is ignored. - bottomColor = topColor; - } - texture = new Texture({ - context : context, - source : blueOverRedUnflippedImage, - pixelFormat : PixelFormat.RGBA, - flipY : false - }); + return Resource.supportsImageBitmapOptions() + .then(function(supportsImageBitmapOptions) { + if (supportsImageBitmapOptions) { + // When imageBitmapOptions is supported, flipY on texture upload is ignored. + bottomColor = topColor; + } - expect({ - context : context, - fragmentShader : fs, - uniformMap : uniformMap - }).contextToRender(topColor.toBytes()); + texture = new Texture({ + context : context, + source : blueOverRedUnflippedImage, + pixelFormat : PixelFormat.RGBA, + flipY : false + }); - // Flip the texture. - texture = new Texture({ - context : context, - source : blueOverRedUnflippedImage, - pixelFormat : PixelFormat.RGBA, - flipY : true - }); + expect({ + context : context, + fragmentShader : fs, + uniformMap : uniformMap + }).contextToRender(topColor.toBytes()); - expect({ - context : context, - fragmentShader : fs, - uniformMap : uniformMap - }).contextToRender(bottomColor.toBytes()); + // Flip the texture. + texture = new Texture({ + context : context, + source : blueOverRedUnflippedImage, + pixelFormat : PixelFormat.RGBA, + flipY : true + }); + + expect({ + context : context, + fragmentShader : fs, + uniformMap : uniformMap + }).contextToRender(bottomColor.toBytes()); + }); }); it('draws the expected floating-point texture color', function() { diff --git a/Specs/Scene/ArcGisMapServerImageryProviderSpec.js b/Specs/Scene/ArcGisMapServerImageryProviderSpec.js index 7ba901a13cd8..9cc36109f138 100644 --- a/Specs/Scene/ArcGisMapServerImageryProviderSpec.js +++ b/Specs/Scene/ArcGisMapServerImageryProviderSpec.js @@ -55,7 +55,9 @@ defineSuite([ 'use strict'; beforeAll(function() { - return FeatureDetection.supportsImageBitmapOptions(); + // This suite spies on requests. The test below needs to make a request to a data URI. + // We run it here to avoid interfering with the tests. + return Resource.supportsImageBitmapOptions(); }); beforeEach(function() { @@ -214,8 +216,7 @@ defineSuite([ expect(provider.hasAlphaChannel).toBeDefined(); Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url) || FeatureDetection.supportsCreateImageBitmap()) { - // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. + if (/^blob:/.test(url)) { Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { expect(url).toEqual(getAbsoluteUri(baseUrl + 'tile/0/0/0')); diff --git a/Specs/Scene/BillboardCollectionSpec.js b/Specs/Scene/BillboardCollectionSpec.js index 9afcdfa9058c..5669be5e5666 100644 --- a/Specs/Scene/BillboardCollectionSpec.js +++ b/Specs/Scene/BillboardCollectionSpec.js @@ -8,7 +8,6 @@ defineSuite([ 'Core/Color', 'Core/createGuid', 'Core/DistanceDisplayCondition', - 'Core/FeatureDetection', 'Core/Math', 'Core/NearFarScalar', 'Core/OrthographicOffCenterFrustum', @@ -35,7 +34,6 @@ defineSuite([ Color, createGuid, DistanceDisplayCondition, - FeatureDetection, CesiumMath, NearFarScalar, OrthographicOffCenterFrustum, @@ -69,23 +67,20 @@ defineSuite([ context = scene.context; camera = scene.camera; - return FeatureDetection.supportsImageBitmapOptions() - .then(function() { - return when.join( - Resource.fetchImage('./Data/Images/Green2x2.png').then(function(result) { - greenImage = result; - }), - Resource.fetchImage('./Data/Images/Blue2x2.png').then(function(result) { - blueImage = result; - }), - Resource.fetchImage('./Data/Images/White2x2.png').then(function(result) { - whiteImage = result; - }), - Resource.fetchImage('./Data/Images/Blue10x10.png').then(function(result) { - largeBlueImage = result; - }) - ); - }); + return when.join( + Resource.fetchImage('./Data/Images/Green2x2.png').then(function(result) { + greenImage = result; + }), + Resource.fetchImage('./Data/Images/Blue2x2.png').then(function(result) { + blueImage = result; + }), + Resource.fetchImage('./Data/Images/White2x2.png').then(function(result) { + whiteImage = result; + }), + Resource.fetchImage('./Data/Images/Blue10x10.png').then(function(result) { + largeBlueImage = result; + }) + ); }); afterAll(function() { diff --git a/Specs/Scene/BingMapsImageryProviderSpec.js b/Specs/Scene/BingMapsImageryProviderSpec.js index faf273881718..0a4b093b6872 100644 --- a/Specs/Scene/BingMapsImageryProviderSpec.js +++ b/Specs/Scene/BingMapsImageryProviderSpec.js @@ -38,10 +38,6 @@ defineSuite([ Uri) { 'use strict'; - beforeAll(function() { - return FeatureDetection.supportsImageBitmapOptions(); - }); - beforeEach(function() { RequestScheduler.clearForSpecs(); }); diff --git a/Specs/Scene/DiscardMissingTileImagePolicySpec.js b/Specs/Scene/DiscardMissingTileImagePolicySpec.js index 85b207a0eeaf..78240aa879e7 100644 --- a/Specs/Scene/DiscardMissingTileImagePolicySpec.js +++ b/Specs/Scene/DiscardMissingTileImagePolicySpec.js @@ -14,15 +14,17 @@ defineSuite([ when) { 'use strict'; + beforeAll(function() { + // This suite spies on requests. The test below needs to make a request to a data URI. + // We run it here to avoid interfering with the tests. + return Resource.supportsImageBitmapOptions(); + }); + afterEach(function() { Resource._Implementations.createImage = Resource._DefaultImplementations.createImage; Resource._Implementations.loadWithXhr = Resource._DefaultImplementations.loadWithXhr; }); - beforeAll(function() { - return FeatureDetection.supportsImageBitmapOptions(); - }); - describe('construction', function() { it('throws if missingImageUrl is not provided', function() { function constructWithoutMissingImageUrl() { @@ -45,9 +47,9 @@ defineSuite([ it('requests the missing image url', function() { var missingImageUrl = 'http://some.host.invalid/missingImage.png'; + spyOn(Resource._Implementations, 'createImageBitmapFromBlob').and.callThrough(); spyOn(Resource._Implementations, 'createImage').and.callFake(function(url, crossOrigin, deferred) { - if (/^blob:/.test(url) || FeatureDetection.supportsCreateImageBitmap()) { - // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. + if (/^blob:/.test(url)) { Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { expect(url).toEqual(missingImageUrl); @@ -68,7 +70,12 @@ defineSuite([ return pollToPromise(function() { return policy.isReady(); }).then(function() { - expect(Resource._Implementations.createImage).toHaveBeenCalled(); + if (FeatureDetection.supportsCreateImageBitmap()) { + expect(Resource._Implementations.createImageBitmapFromBlob).toHaveBeenCalled(); + } else { + expect(Resource._Implementations.createImage).toHaveBeenCalled(); + } + }); }); }); diff --git a/Specs/Scene/GoogleEarthEnterpriseImageryProviderSpec.js b/Specs/Scene/GoogleEarthEnterpriseImageryProviderSpec.js index 7fde0a564c6c..b584b6580ad6 100644 --- a/Specs/Scene/GoogleEarthEnterpriseImageryProviderSpec.js +++ b/Specs/Scene/GoogleEarthEnterpriseImageryProviderSpec.js @@ -4,7 +4,6 @@ defineSuite([ 'Core/DefaultProxy', 'Core/defaultValue', 'Core/defined', - 'Core/FeatureDetection', 'Core/GeographicTilingScheme', 'Core/GoogleEarthEnterpriseMetadata', 'Core/GoogleEarthEnterpriseTileInformation', @@ -26,7 +25,6 @@ defineSuite([ DefaultProxy, defaultValue, defined, - FeatureDetection, GeographicTilingScheme, GoogleEarthEnterpriseMetadata, GoogleEarthEnterpriseTileInformation, @@ -50,7 +48,6 @@ defineSuite([ beforeAll(function() { decodeGoogleEarthEnterpriseData.passThroughDataForTesting = true; - return FeatureDetection.supportsImageBitmapOptions(); }); afterAll(function() { diff --git a/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js b/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js index 3c5c7325ea24..4d7a67ca1005 100644 --- a/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js +++ b/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js @@ -30,10 +30,6 @@ defineSuite([ isImageOrImageBitmap) { 'use strict'; - beforeAll(function() { - return FeatureDetection.supportsImageBitmapOptions(); - }); - afterEach(function() { Resource._Implementations.createImage = Resource._DefaultImplementations.createImage; Resource._Implementations.loadWithXhr = Resource._DefaultImplementations.loadWithXhr; diff --git a/Specs/Scene/MapboxImageryProviderSpec.js b/Specs/Scene/MapboxImageryProviderSpec.js index eef7fbc1dd49..25e854bc4b62 100644 --- a/Specs/Scene/MapboxImageryProviderSpec.js +++ b/Specs/Scene/MapboxImageryProviderSpec.js @@ -1,7 +1,6 @@ defineSuite([ 'Scene/MapboxImageryProvider', 'Core/DefaultProxy', - 'Core/FeatureDetection', 'Core/Math', 'Core/Rectangle', 'Core/RequestScheduler', @@ -16,7 +15,6 @@ defineSuite([ ], function( MapboxImageryProvider, DefaultProxy, - FeatureDetection, CesiumMath, Rectangle, RequestScheduler, @@ -30,10 +28,6 @@ defineSuite([ isImageOrImageBitmap) { 'use strict'; - beforeAll(function() { - return FeatureDetection.supportsImageBitmapOptions(); - }); - beforeEach(function() { RequestScheduler.clearForSpecs(); }); diff --git a/Specs/Scene/SingleTileImageryProviderSpec.js b/Specs/Scene/SingleTileImageryProviderSpec.js index 1482014aed4d..62fba1eca3a1 100644 --- a/Specs/Scene/SingleTileImageryProviderSpec.js +++ b/Specs/Scene/SingleTileImageryProviderSpec.js @@ -2,7 +2,6 @@ defineSuite([ 'Scene/SingleTileImageryProvider', 'Core/DefaultProxy', 'Core/Ellipsoid', - 'Core/FeatureDetection', 'Core/GeographicTilingScheme', 'Core/Rectangle', 'Core/Resource', @@ -17,7 +16,6 @@ defineSuite([ SingleTileImageryProvider, DefaultProxy, Ellipsoid, - FeatureDetection, GeographicTilingScheme, Rectangle, Resource, @@ -30,10 +28,6 @@ defineSuite([ when) { 'use strict'; - beforeAll(function() { - return FeatureDetection.supportsImageBitmapOptions(); - }); - afterEach(function() { Resource._Implementations.createImage = Resource._DefaultImplementations.createImage; }); diff --git a/Specs/Scene/TextureAtlasSpec.js b/Specs/Scene/TextureAtlasSpec.js index ef47f4b813b5..54628701f747 100644 --- a/Specs/Scene/TextureAtlasSpec.js +++ b/Specs/Scene/TextureAtlasSpec.js @@ -3,7 +3,6 @@ defineSuite([ 'Core/BoundingRectangle', 'Core/Cartesian2', 'Core/createGuid', - 'Core/FeatureDetection', 'Core/Math', 'Core/PixelFormat', 'Core/Resource', @@ -14,7 +13,6 @@ defineSuite([ BoundingRectangle, Cartesian2, createGuid, - FeatureDetection, CesiumMath, PixelFormat, Resource, @@ -40,29 +38,26 @@ defineSuite([ guidArray.push(createGuid()); } - return FeatureDetection.supportsImageBitmapOptions() - .then(function() { - return when.join( - Resource.fetchImage('./Data/Images/Green.png').then(function(image) { - greenImage = image; - }), - Resource.fetchImage('./Data/Images/Green1x4.png').then(function(image) { - tallGreenImage = image; - }), - Resource.fetchImage('./Data/Images/Blue.png').then(function(image) { - blueImage = image; - }), - Resource.fetchImage('./Data/Images/Red16x16.png').then(function(image) { - bigRedImage = image; - }), - Resource.fetchImage('./Data/Images/Blue10x10.png').then(function(image) { - bigBlueImage = image; - }), - Resource.fetchImage('./Data/Images/Green4x4.png').then(function(image) { - bigGreenImage = image; - }) - ); - }); + return when.join( + Resource.fetchImage('./Data/Images/Green.png').then(function(image) { + greenImage = image; + }), + Resource.fetchImage('./Data/Images/Green1x4.png').then(function(image) { + tallGreenImage = image; + }), + Resource.fetchImage('./Data/Images/Blue.png').then(function(image) { + blueImage = image; + }), + Resource.fetchImage('./Data/Images/Red16x16.png').then(function(image) { + bigRedImage = image; + }), + Resource.fetchImage('./Data/Images/Blue10x10.png').then(function(image) { + bigBlueImage = image; + }), + Resource.fetchImage('./Data/Images/Green4x4.png').then(function(image) { + bigGreenImage = image; + }) + ); }); afterAll(function() { diff --git a/Specs/Scene/UrlTemplateImageryProviderSpec.js b/Specs/Scene/UrlTemplateImageryProviderSpec.js index 8158ccc3797a..0c33d8305d47 100644 --- a/Specs/Scene/UrlTemplateImageryProviderSpec.js +++ b/Specs/Scene/UrlTemplateImageryProviderSpec.js @@ -2,7 +2,6 @@ defineSuite([ 'Scene/UrlTemplateImageryProvider', 'Core/DefaultProxy', 'Core/Ellipsoid', - 'Core/FeatureDetection', 'Core/GeographicTilingScheme', 'Core/Math', 'Core/Rectangle', @@ -22,7 +21,6 @@ defineSuite([ UrlTemplateImageryProvider, DefaultProxy, Ellipsoid, - FeatureDetection, GeographicTilingScheme, CesiumMath, Rectangle, @@ -40,10 +38,6 @@ defineSuite([ when) { 'use strict'; - beforeAll(function() { - return FeatureDetection.supportsImageBitmapOptions(); - }); - beforeEach(function() { RequestScheduler.clearForSpecs(); }); diff --git a/Specs/Scene/WebMapServiceImageryProviderSpec.js b/Specs/Scene/WebMapServiceImageryProviderSpec.js index 94d59764d672..d2b6312d2d8f 100644 --- a/Specs/Scene/WebMapServiceImageryProviderSpec.js +++ b/Specs/Scene/WebMapServiceImageryProviderSpec.js @@ -5,7 +5,6 @@ defineSuite([ 'Core/ClockStep', 'Core/DefaultProxy', 'Core/Ellipsoid', - 'Core/FeatureDetection', 'Core/GeographicTilingScheme', 'Core/JulianDate', 'Core/Math', @@ -33,7 +32,6 @@ defineSuite([ ClockStep, DefaultProxy, Ellipsoid, - FeatureDetection, GeographicTilingScheme, JulianDate, CesiumMath, @@ -56,10 +54,6 @@ defineSuite([ Uri) { 'use strict'; - beforeAll(function() { - return FeatureDetection.supportsImageBitmapOptions(); - }); - beforeEach(function() { RequestScheduler.clearForSpecs(); }); diff --git a/Specs/Scene/WebMapTileServiceImageryProviderSpec.js b/Specs/Scene/WebMapTileServiceImageryProviderSpec.js index d06d28f6322d..cdff245bf81d 100644 --- a/Specs/Scene/WebMapTileServiceImageryProviderSpec.js +++ b/Specs/Scene/WebMapTileServiceImageryProviderSpec.js @@ -4,7 +4,6 @@ defineSuite([ 'Core/ClockStep', 'Core/Credit', 'Core/DefaultProxy', - 'Core/FeatureDetection', 'Core/GeographicTilingScheme', 'Core/JulianDate', 'Core/objectToQuery', @@ -28,7 +27,6 @@ defineSuite([ ClockStep, Credit, DefaultProxy, - FeatureDetection, GeographicTilingScheme, JulianDate, objectToQuery, @@ -48,10 +46,6 @@ defineSuite([ Uri) { 'use strict'; - beforeAll(function() { - return FeatureDetection.supportsImageBitmapOptions(); - }); - beforeEach(function() { RequestScheduler.clearForSpecs(); }); diff --git a/Specs/Scene/createOpenStreetMapImageryProviderSpec.js b/Specs/Scene/createOpenStreetMapImageryProviderSpec.js index 3d6c327a4f58..e097ceff92d2 100644 --- a/Specs/Scene/createOpenStreetMapImageryProviderSpec.js +++ b/Specs/Scene/createOpenStreetMapImageryProviderSpec.js @@ -1,7 +1,6 @@ defineSuite([ 'Scene/createOpenStreetMapImageryProvider', 'Core/DefaultProxy', - 'Core/FeatureDetection', 'Core/Math', 'Core/Rectangle', 'Core/RequestScheduler', @@ -16,7 +15,6 @@ defineSuite([ ], function( createOpenStreetMapImageryProvider, DefaultProxy, - FeatureDetection, CesiumMath, Rectangle, RequestScheduler, @@ -30,10 +28,6 @@ defineSuite([ isImageOrImageBitmap) { 'use strict'; - beforeAll(function() { - return FeatureDetection.supportsImageBitmapOptions(); - }); - beforeEach(function() { RequestScheduler.clearForSpecs(); }); diff --git a/Specs/Scene/createTileMapServiceImageryProviderSpec.js b/Specs/Scene/createTileMapServiceImageryProviderSpec.js index f32f7866adf0..e813f76e3f2b 100644 --- a/Specs/Scene/createTileMapServiceImageryProviderSpec.js +++ b/Specs/Scene/createTileMapServiceImageryProviderSpec.js @@ -3,7 +3,6 @@ defineSuite([ 'Core/Cartesian2', 'Core/Cartographic', 'Core/DefaultProxy', - 'Core/FeatureDetection', 'Core/GeographicProjection', 'Core/GeographicTilingScheme', 'Core/getAbsoluteUri', @@ -25,7 +24,6 @@ defineSuite([ Cartesian2, Cartographic, DefaultProxy, - FeatureDetection, GeographicProjection, GeographicTilingScheme, getAbsoluteUri, @@ -44,10 +42,6 @@ defineSuite([ when) { 'use strict'; - beforeAll(function() { - return FeatureDetection.supportsImageBitmapOptions(); - }); - beforeEach(function() { RequestScheduler.clearForSpecs(); }); From 9e7fea4882a8484b155d4bc69ace9177bd62d63a Mon Sep 17 00:00:00 2001 From: Omar Shehata Date: Thu, 21 Feb 2019 13:07:32 -0500 Subject: [PATCH 11/16] Cleanup ImageBitmap implementation --- CHANGES.md | 2 +- Source/Core/FeatureDetection.js | 7 +- Source/Core/IonResource.js | 9 +- Source/Core/Resource.js | 58 ++++---- Source/Core/loadImageFromTypedArray.js | 6 +- Source/Scene/DiscardMissingTileImagePolicy.js | 4 +- .../DiscardMissingTileImagePolicySpec.js | 1 - Specs/Scene/TextureAtlasSpec.js | 132 +++++++++--------- 8 files changed, 109 insertions(+), 110 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 6fade07e29d5..5ed21d4e7602 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,10 +5,10 @@ Change Log ##### Breaking Changes :mega: * `czm_materialInput.slope` is now an angle in radians between 0 and pi/2 (flat to vertical), rather than a projected length 1 to 0 (flat to vertical). +* `Resource.fetchImage` now returns an `ImageBitmap` instead of `Image` when supported. This allows for decoding images while fetching using `createImageBitmap` to greatly speed up texture upload and decrease frame drops when loading models with large textures. `Resource.fetchImage` now has a `flipY` option to vertically flip an image during fetch & decode when `ImageBitmapOptions` are supported since an `ImageBitmap` cannot be vertically flipped during texture upload. [#7579](https://github.com/AnalyticalGraphicsInc/cesium/pull/7579) ##### Additions :tada: * `czm_materialInput.aspect` was added as an angle in radians between 0 and 2pi (east, north, west to south). -* Add support for decoding images while fetching using `createImageBitmap` to greatly speed up texture upload and decrease frame drops when loading models with large textures. `Resource.fetchImage` now has a `flipY` option to vertically flip an image during fetch & decode when `ImageBitmapOptions` are supported since an `ImageBitmap` cannot be vertically flipped during texture upload. [#7579](https://github.com/AnalyticalGraphicsInc/cesium/pull/7579) ##### Deprecated :hourglass_flowing_sand: * `Resource.fetchImage` now takes an options object. Use `resource.fetchImage({ preferBlob: true })` instead of `resource.fetchImage(true)`. The previous function definition will no longer work in 1.56. [#7579](https://github.com/AnalyticalGraphicsInc/cesium/pull/7579) diff --git a/Source/Core/FeatureDetection.js b/Source/Core/FeatureDetection.js index cd0d56800b13..9d982e34bce9 100644 --- a/Source/Core/FeatureDetection.js +++ b/Source/Core/FeatureDetection.js @@ -253,13 +253,8 @@ define([ } } - var supportsCreateImageBitmapResult; function supportsCreateImageBitmap() { - if (!defined(supportsCreateImageBitmapResult)) { - supportsCreateImageBitmapResult = defined(window.createImageBitmap); - } - - return supportsCreateImageBitmapResult; + return typeof createImageBitmap === 'function'; } /** diff --git a/Source/Core/IonResource.js b/Source/Core/IonResource.js index b474f26ca857..4a629d563b3b 100644 --- a/Source/Core/IonResource.js +++ b/Source/Core/IonResource.js @@ -166,8 +166,13 @@ define([ IonResource.prototype.fetchImage = function (options) { if (!this._isExternal) { - options = defaultValue(options, {}); - options.preferBlob = true; + var userOptions = options; + options = { + preferBlob : true + }; + if (defined(userOptions)) { + options.flipY = userOptions.flipY; + } } return Resource.prototype.fetchImage.call(this, options); diff --git a/Source/Core/Resource.js b/Source/Core/Resource.js index 6b315d048367..304c574b9a6d 100644 --- a/Source/Core/Resource.js +++ b/Source/Core/Resource.js @@ -389,34 +389,32 @@ define([ // know if this browser supports passing options to the createImageBitmap function. // https://github.com/whatwg/html/pull/4248 if (defined(supportsImageBitmapOptionsPromise)) { - return supportsImageBitmapOptionsPromise.promise; + return supportsImageBitmapOptionsPromise; } - supportsImageBitmapOptionsPromise = when.defer(); - if (!FeatureDetection.supportsCreateImageBitmap()) { - supportsImageBitmapOptionsPromise.resolve(false); - return supportsImageBitmapOptionsPromise.promise; + supportsImageBitmapOptionsPromise = when.resolve(false); + return supportsImageBitmapOptionsPromise; } var imageDataUri = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWP4////fwAJ+wP9CNHoHgAAAABJRU5ErkJggg=='; - Resource.fetchBlob({ + supportsImageBitmapOptionsPromise = Resource.fetchBlob({ url : imageDataUri }) .then(function(blob) { - return when(createImageBitmap(blob, { + return createImageBitmap(blob, { imageOrientation: 'flipY' - })); + }); }) .then(function(imageBitmap) { - supportsImageBitmapOptionsPromise.resolve(true); + return true; }) .otherwise(function() { - supportsImageBitmapOptionsPromise.resolve(false); + return false; }); - return supportsImageBitmapOptionsPromise.promise; + return supportsImageBitmapOptionsPromise; }; defineProperties(Resource, { @@ -875,10 +873,10 @@ define([ * an {@link https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap|ImageBitmap} if the browser supports `createImageBitmap` or otherwise an * {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement|Image} once loaded, or reject if the image failed to load. * - * @param {String|Object} options An object with the following properties. + * @param {Object} [options] An object with the following properties. * @param {Boolean} [options.preferBlob=false] If true, we will load the image via a blob. * @param {Boolean} [options.flipY=true] If true, image will be vertially flipped during decode. Only applies if the browser supports `createImageBitmap`. - * @returns {Promise.|Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. + * @returns {Promise.|Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * * @example @@ -898,13 +896,13 @@ define([ * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ Resource.prototype.fetchImage = function (options) { - if (defined(options) && typeof options === 'boolean') { + if (typeof options === 'boolean') { deprecationWarning('fetchImage-parameter-change', 'fetchImage now takes an options object in CesiumJS 1.55. Use resource.fetchImage({ preferBlob: true }) instead of resource.fetchImage(true).'); options = { preferBlob : options }; } - options = defaultValue(options, {}); + options = defaultValue(options, defaultValue.EMPTY_OBJECT); var preferBlob = defaultValue(options.preferBlob, false); var flipY = defaultValue(options.flipY, true); @@ -927,9 +925,7 @@ define([ if (FeatureDetection.supportsCreateImageBitmap()) { return blobPromise .then(function(blob) { - var deferred = when.defer(); - Resource._Implementations.createImageBitmapFromBlob(blob, deferred, flipY); - return deferred; + return Resource._Implementations.createImageBitmapFromBlob(blob, flipY); }); } @@ -1029,7 +1025,7 @@ define([ * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. * @param {Boolean} [options.preferBlob = false] If true, we will load the image via a blob. - * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. + * @returns {Promise.|Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. */ Resource.fetchImage = function (options) { var resource = new Resource(options); @@ -1847,28 +1843,30 @@ define([ image.src = url; } else { - // createImageBitmap will block the main thread during decode if a blob is not supplied. + // Passing an Image to createImageBitmap will force it to run on the main thread + // since DOM elements don't exist on workers. We convert it to a blob so it's non-blocking. + // See: + // https://bugzilla.mozilla.org/show_bug.cgi?id=1044102#c38 + // https://bugs.chromium.org/p/chromium/issues/detail?id=580202#c10 Resource.fetchBlob({ url: url }).then(function(blob) { - Resource._Implementations.createImageBitmapFromBlob(blob, deferred, flipY); - }).otherwise(deferred.reject); + return Resource._Implementations.createImageBitmapFromBlob(blob, flipY); + }).then(deferred.resolve).otherwise(deferred.reject); } }; - Resource._Implementations.createImageBitmapFromBlob = function(blob, deferred, flipY) { - Resource.supportsImageBitmapOptions() + Resource._Implementations.createImageBitmapFromBlob = function(blob, flipY) { + return Resource.supportsImageBitmapOptions() .then(function(supportsBitmapOptions) { if (!supportsBitmapOptions) { - return when(createImageBitmap(blob)); + return createImageBitmap(blob); } - return when(createImageBitmap(blob, { + return createImageBitmap(blob, { imageOrientation: flipY ? 'flipY' : 'none' - })); - }) - .then(deferred.resolve) - .otherwise(deferred.reject); + }); + }); }; function decodeResponse(loadWithHttpResponse, responseType) { diff --git a/Source/Core/loadImageFromTypedArray.js b/Source/Core/loadImageFromTypedArray.js index 987fc07ae461..7ae09f8a30c8 100644 --- a/Source/Core/loadImageFromTypedArray.js +++ b/Source/Core/loadImageFromTypedArray.js @@ -37,12 +37,12 @@ define([ return Resource.supportsImageBitmapOptions() .then(function(supportsBitmapOptions) { if (supportsBitmapOptions) { - return when(createImageBitmap(blob, { + return createImageBitmap(blob, { imageOrientation: flipY ? 'flipY' : 'none' - })); + }); } - return when(createImageBitmap(blob)); + return createImageBitmap(blob); }); } diff --git a/Source/Scene/DiscardMissingTileImagePolicy.js b/Source/Scene/DiscardMissingTileImagePolicy.js index a8da9e33f071..83c268243026 100644 --- a/Source/Scene/DiscardMissingTileImagePolicy.js +++ b/Source/Scene/DiscardMissingTileImagePolicy.js @@ -89,9 +89,9 @@ define([ that._isReady = true; } - when(resource.fetchImage({ + resource.fetchImage({ preferBlob : true - }), success, failure); + }).then(success).otherwise(failure); } /** diff --git a/Specs/Scene/DiscardMissingTileImagePolicySpec.js b/Specs/Scene/DiscardMissingTileImagePolicySpec.js index 78240aa879e7..d84d40a3a03d 100644 --- a/Specs/Scene/DiscardMissingTileImagePolicySpec.js +++ b/Specs/Scene/DiscardMissingTileImagePolicySpec.js @@ -75,7 +75,6 @@ defineSuite([ } else { expect(Resource._Implementations.createImage).toHaveBeenCalled(); } - }); }); }); diff --git a/Specs/Scene/TextureAtlasSpec.js b/Specs/Scene/TextureAtlasSpec.js index 54628701f747..b22948eb0096 100644 --- a/Specs/Scene/TextureAtlasSpec.js +++ b/Specs/Scene/TextureAtlasSpec.js @@ -28,36 +28,42 @@ defineSuite([ var bigRedImage; var bigBlueImage; var bigGreenImage; - var guidArray; + + var greenGuid; + var tallGreenGuid; + var blueGuid; + var bigRedGuid; + var bigBlueGuid; + var bigGreenGuid; beforeAll(function() { scene = createScene(); - // Create enough guid's to use throughout these tests. - guidArray = []; - for (var i = 0; i < 4; ++i) { - guidArray.push(createGuid()); - } return when.join( Resource.fetchImage('./Data/Images/Green.png').then(function(image) { greenImage = image; + greenGuid = createGuid(); }), Resource.fetchImage('./Data/Images/Green1x4.png').then(function(image) { tallGreenImage = image; + tallGreenGuid = createGuid(); }), Resource.fetchImage('./Data/Images/Blue.png').then(function(image) { blueImage = image; + blueGuid = createGuid(); }), Resource.fetchImage('./Data/Images/Red16x16.png').then(function(image) { bigRedImage = image; + bigRedGuid = createGuid(); }), Resource.fetchImage('./Data/Images/Blue10x10.png').then(function(image) { bigBlueImage = image; + bigBlueGuid = createGuid(); }), Resource.fetchImage('./Data/Images/Green4x4.png').then(function(image) { bigGreenImage = image; - }) - ); + bigGreenGuid = createGuid(); + })); }); afterAll(function() { @@ -96,7 +102,7 @@ defineSuite([ initialSize : new Cartesian2(1, 1) }); - return atlas.addImage(guidArray[0], greenImage).then(function(index) { + return atlas.addImage(greenGuid, greenImage).then(function(index) { expect(index).toEqual(0); expect(atlas.numberOfImages).toEqual(1); @@ -124,7 +130,7 @@ defineSuite([ initialSize : new Cartesian2(1, 1) }); - return atlas.addImage(guidArray[0], greenImage).then(function(index) { + return atlas.addImage(greenGuid, greenImage).then(function(index) { var texture = atlas.texture; var coords = atlas.textureCoordinates[index]; @@ -137,7 +143,7 @@ defineSuite([ context : scene.context }); - return atlas.addImage(guidArray[0], greenImage).then(function(index) { + return atlas.addImage(greenGuid, greenImage).then(function(index) { expect(index).toEqual(0); expect(atlas.numberOfImages).toEqual(1); @@ -164,7 +170,7 @@ defineSuite([ context : scene.context }); - return atlas.addImage(guidArray[0], greenImage).then(function(index) { + return atlas.addImage(greenGuid, greenImage).then(function(index) { var texture = atlas.texture; var coords = atlas.textureCoordinates[index]; @@ -179,7 +185,7 @@ defineSuite([ initialSize : new Cartesian2(1.0, 5.0) }); - return atlas.addImage(guidArray[0], tallGreenImage).then(function(index) { + return atlas.addImage(tallGreenGuid, tallGreenImage).then(function(index) { expect(index).toEqual(0); expect(atlas.numberOfImages).toEqual(1); @@ -206,7 +212,7 @@ defineSuite([ initialSize : new Cartesian2(1.0, 5.0) }); - return atlas.addImage(guidArray[0], tallGreenImage).then(function(index) { + return atlas.addImage(tallGreenGuid, tallGreenImage).then(function(index) { var texture = atlas.texture; var coords = atlas.textureCoordinates[index]; @@ -222,8 +228,8 @@ defineSuite([ }); var promises = []; - promises.push(atlas.addImage(guidArray[0], greenImage)); - promises.push(atlas.addImage(guidArray[1], blueImage)); + promises.push(atlas.addImage(greenGuid, greenImage)); + promises.push(atlas.addImage(blueGuid, blueImage)); return when.all(promises, function(indices) { var greenIndex = indices[0]; @@ -259,8 +265,8 @@ defineSuite([ }); var promises = []; - promises.push(atlas.addImage(guidArray[0], greenImage)); - promises.push(atlas.addImage(guidArray[1], blueImage)); + promises.push(atlas.addImage(greenGuid, greenImage)); + promises.push(atlas.addImage(blueGuid, blueImage)); return when.all(promises, function(indices) { var greenIndex = indices[0]; @@ -283,10 +289,10 @@ defineSuite([ }); var promises = []; - promises.push(atlas.addImage(guidArray[0], greenImage)); - promises.push(atlas.addImage(guidArray[1], blueImage)); - promises.push(atlas.addImage(guidArray[2], bigRedImage)); - promises.push(atlas.addImage(guidArray[3], bigBlueImage)); + promises.push(atlas.addImage(greenGuid, greenImage)); + promises.push(atlas.addImage(blueGuid, blueImage)); + promises.push(atlas.addImage(bigRedGuid, bigRedImage)); + promises.push(atlas.addImage(bigBlueGuid, bigBlueImage)); return when.all(promises, function(indices) { var greenIndex = indices.shift(); @@ -316,10 +322,10 @@ defineSuite([ }); var promises = []; - promises.push(atlas.addImage(guidArray[0], greenImage)); - promises.push(atlas.addImage(guidArray[1], blueImage)); - promises.push(atlas.addImage(guidArray[2], bigRedImage)); - promises.push(atlas.addImage(guidArray[3], bigBlueImage)); + promises.push(atlas.addImage(greenGuid, greenImage)); + promises.push(atlas.addImage(blueGuid, blueImage)); + promises.push(atlas.addImage(bigRedGuid, bigRedImage)); + promises.push(atlas.addImage(bigBlueGuid, bigBlueImage)); return when.all(promises, function(indices) { var greenIndex = indices.shift(); @@ -370,10 +376,10 @@ defineSuite([ }); var promises = []; - promises.push(atlas.addImage(guidArray[0], greenImage)); - promises.push(atlas.addImage(guidArray[1], blueImage)); - promises.push(atlas.addImage(guidArray[2], bigRedImage)); - promises.push(atlas.addImage(guidArray[3], bigBlueImage)); + promises.push(atlas.addImage(greenGuid, greenImage)); + promises.push(atlas.addImage(blueGuid, blueImage)); + promises.push(atlas.addImage(bigRedGuid, bigRedImage)); + promises.push(atlas.addImage(bigBlueGuid, bigBlueImage)); return when.all(promises, function(indices) { var greenIndex = indices.shift(); @@ -403,7 +409,7 @@ defineSuite([ initialSize : new Cartesian2(1, 1) }); - return atlas.addImage(guidArray[0], blueImage).then(function(blueIndex) { + return atlas.addImage(blueGuid, blueImage).then(function(blueIndex) { expect(atlas.numberOfImages).toEqual(1); var texture = atlas.texture; @@ -421,7 +427,7 @@ defineSuite([ expect(coordinates[blueIndex].height).toEqual(1.0 / atlasHeight); //Add the big green image - return atlas.addImage(guidArray[1], bigGreenImage).then(function(greenIndex) { + return atlas.addImage(bigGreenGuid, bigGreenImage).then(function(greenIndex) { expect(atlas.numberOfImages).toEqual(2); var texture = atlas.texture; @@ -454,7 +460,7 @@ defineSuite([ initialSize : new Cartesian2(1, 1) }); - return atlas.addImage(guidArray[0], blueImage).then(function(blueIndex) { + return atlas.addImage(blueGuid, blueImage).then(function(blueIndex) { expect(atlas.numberOfImages).toEqual(1); var texture = atlas.texture; @@ -463,7 +469,7 @@ defineSuite([ var blueCoords = coordinates[blueIndex]; expectToRender(texture, blueCoords, [0, 0, 255, 255]); - return atlas.addImage(guidArray[1], bigGreenImage).then(function(greenIndex) { + return atlas.addImage(bigGreenGuid, bigGreenImage).then(function(greenIndex) { expect(atlas.numberOfImages).toEqual(2); var texture = atlas.texture; @@ -485,7 +491,7 @@ defineSuite([ initialSize : new Cartesian2(1, 1) }); - return atlas.addImage(guidArray[0], bigRedImage).then(function(index) { + return atlas.addImage(bigRedGuid, bigRedImage).then(function(index) { expect(atlas.numberOfImages).toEqual(1); var texture = atlas.texture; @@ -510,7 +516,7 @@ defineSuite([ initialSize : new Cartesian2(1, 1) }); - return atlas.addImage(guidArray[0], bigRedImage).then(function(index) { + return atlas.addImage(bigRedGuid, bigRedImage).then(function(index) { var texture = atlas.texture; var coords = atlas.textureCoordinates[index]; @@ -525,8 +531,8 @@ defineSuite([ initialSize : new Cartesian2(2, 2) }); - var greenPromise = atlas.addImage(guidArray[0], greenImage); - var bluePromise = atlas.addImage(guidArray[1], blueImage); + var greenPromise = atlas.addImage(greenGuid, greenImage); + var bluePromise = atlas.addImage(blueGuid, blueImage); return when.all([greenPromise, bluePromise], function(indices) { var greenIndex = indices.shift(); @@ -561,8 +567,8 @@ defineSuite([ initialSize : new Cartesian2(2, 2) }); - var greenPromise = atlas.addImage(guidArray[0], greenImage); - var bluePromise = atlas.addImage(guidArray[1], blueImage); + var greenPromise = atlas.addImage(greenGuid, greenImage); + var bluePromise = atlas.addImage(blueGuid, blueImage); return when.all([greenPromise, bluePromise], function(indices) { var greenIndex = indices.shift(); @@ -586,7 +592,7 @@ defineSuite([ initialSize : new Cartesian2(1.0, 1.0) }); - return atlas.addImage(guidArray[0], tallGreenImage).then(function(index) { + return atlas.addImage(tallGreenGuid, tallGreenImage).then(function(index) { expect(atlas.numberOfImages).toEqual(1); var texture = atlas.texture; @@ -611,7 +617,7 @@ defineSuite([ initialSize : new Cartesian2(1.0, 1.0) }); - return atlas.addImage(guidArray[0], tallGreenImage).then(function(index) { + return atlas.addImage(tallGreenGuid, tallGreenImage).then(function(index) { var texture = atlas.texture; var coords = atlas.textureCoordinates[index]; @@ -626,9 +632,9 @@ defineSuite([ initialSize : new Cartesian2(1, 1) }); - var bluePromise = atlas.addImage(guidArray[0], blueImage); - var bigGreenPromise = atlas.addImage(guidArray[1], bigGreenImage); - var bigRedPromise = atlas.addImage(guidArray[2], bigRedImage); + var bluePromise = atlas.addImage(blueGuid, blueImage); + var bigGreenPromise = atlas.addImage(bigGreenGuid, bigGreenImage); + var bigRedPromise = atlas.addImage(bigRedGuid, bigRedImage); return when.all([bluePromise, bigGreenPromise, bigRedPromise], function(indices) { var blueIndex = indices.shift(); @@ -653,13 +659,13 @@ defineSuite([ initialSize : new Cartesian2(4, 4) }); - return atlas.addImage(guidArray[0], blueImage).then(function(blueIndex) { + return atlas.addImage(blueGuid, blueImage).then(function(blueIndex) { expect(blueIndex).toEqual(0); - return atlas.addImage(guidArray[1], greenImage).then(function(greenIndex) { + return atlas.addImage(greenGuid, greenImage).then(function(greenIndex) { expect(greenIndex).toEqual(1); - return atlas.addImage(guidArray[0], blueImage).then(function(index) { + return atlas.addImage(blueGuid, blueImage).then(function(index) { expect(index).toEqual(blueIndex); expect(atlas.numberOfImages).toEqual(2); @@ -684,14 +690,12 @@ defineSuite([ initialSize : new Cartesian2(1, 1) }); - var id = guidArray[0]; - - atlas.addImage(id, greenImage); + atlas.addImage(greenGuid, greenImage); - var promise1 = atlas.addSubRegion(id, new BoundingRectangle(0.0, 0.0, 0.5, 0.5)); - var promise2 = atlas.addSubRegion(id, new BoundingRectangle(0.0, 0.5, 0.5, 0.5)); - var promise3 = atlas.addSubRegion(id, new BoundingRectangle(0.5, 0.0, 0.5, 0.5)); - var promise4 = atlas.addSubRegion(id, new BoundingRectangle(0.5, 0.5, 0.5, 0.5)); + var promise1 = atlas.addSubRegion(greenGuid, new BoundingRectangle(0.0, 0.0, 0.5, 0.5)); + var promise2 = atlas.addSubRegion(greenGuid, new BoundingRectangle(0.0, 0.5, 0.5, 0.5)); + var promise3 = atlas.addSubRegion(greenGuid, new BoundingRectangle(0.5, 0.0, 0.5, 0.5)); + var promise4 = atlas.addSubRegion(greenGuid, new BoundingRectangle(0.5, 0.5, 0.5, 0.5)); return when.all([promise1, promise2, promise3, promise4], function(indices) { var index1 = indices.shift(); @@ -734,14 +738,12 @@ defineSuite([ initialSize : new Cartesian2(1, 1) }); - var id = guidArray[0]; - - atlas.addImage(id, greenImage); + atlas.addImage(greenGuid, greenImage); - var promise1 = atlas.addSubRegion(id, new BoundingRectangle(0.0, 0.0, 0.5, 0.5)); - var promise2 = atlas.addSubRegion(id, new BoundingRectangle(0.0, 0.5, 0.5, 0.5)); - var promise3 = atlas.addSubRegion(id, new BoundingRectangle(0.5, 0.0, 0.5, 0.5)); - var promise4 = atlas.addSubRegion(id, new BoundingRectangle(0.5, 0.5, 0.5, 0.5)); + var promise1 = atlas.addSubRegion(greenGuid, new BoundingRectangle(0.0, 0.0, 0.5, 0.5)); + var promise2 = atlas.addSubRegion(greenGuid, new BoundingRectangle(0.0, 0.5, 0.5, 0.5)); + var promise3 = atlas.addSubRegion(greenGuid, new BoundingRectangle(0.5, 0.0, 0.5, 0.5)); + var promise4 = atlas.addSubRegion(greenGuid, new BoundingRectangle(0.5, 0.5, 0.5, 0.5)); return when.all([promise1, promise2, promise3, promise4], function(indices) { var index1 = indices.shift(); @@ -751,7 +753,7 @@ defineSuite([ expect(atlas.numberOfImages).toEqual(5); - return atlas.addImage(guidArray[1], blueImage).then(function(blueIndex) { + return atlas.addImage(blueGuid, blueImage).then(function(blueIndex) { expect(atlas.numberOfImages).toEqual(6); var coordinates = atlas.textureCoordinates; @@ -832,11 +834,11 @@ defineSuite([ var guid1 = atlas.guid; - return atlas.addImage(guidArray[0], greenImage).then(function(index) { + return atlas.addImage(greenGuid, greenImage).then(function(index) { var guid2 = atlas.guid; expect(guid1).not.toEqual(guid2); - return atlas.addSubRegion(guidArray[0], new BoundingRectangle(0.0, 0.0, 0.5, 0.5)).then(function(index) { + return atlas.addSubRegion(greenGuid, new BoundingRectangle(0.0, 0.0, 0.5, 0.5)).then(function(index) { var guid3 = atlas.guid; expect(guid2).not.toEqual(guid3); }); From bf46f89eefb9055f9ae60575ce324345f63ffa8b Mon Sep 17 00:00:00 2001 From: Matthew Amato Date: Thu, 21 Feb 2019 13:31:46 -0500 Subject: [PATCH 12/16] Update CHANGES.md --- CHANGES.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 5ed21d4e7602..071871962bad 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,14 +5,15 @@ Change Log ##### Breaking Changes :mega: * `czm_materialInput.slope` is now an angle in radians between 0 and pi/2 (flat to vertical), rather than a projected length 1 to 0 (flat to vertical). -* `Resource.fetchImage` now returns an `ImageBitmap` instead of `Image` when supported. This allows for decoding images while fetching using `createImageBitmap` to greatly speed up texture upload and decrease frame drops when loading models with large textures. `Resource.fetchImage` now has a `flipY` option to vertically flip an image during fetch & decode when `ImageBitmapOptions` are supported since an `ImageBitmap` cannot be vertically flipped during texture upload. [#7579](https://github.com/AnalyticalGraphicsInc/cesium/pull/7579) - -##### Additions :tada: -* `czm_materialInput.aspect` was added as an angle in radians between 0 and 2pi (east, north, west to south). +* `Resource.fetchImage` now returns an `ImageBitmap` instead of `Image` when supported. This allows for decoding images while fetching using `createImageBitmap` to greatly speed up texture upload and decrease frame drops when loading models with large textures. [#7579](https://github.com/AnalyticalGraphicsInc/cesium/pull/7579) ##### Deprecated :hourglass_flowing_sand: * `Resource.fetchImage` now takes an options object. Use `resource.fetchImage({ preferBlob: true })` instead of `resource.fetchImage(true)`. The previous function definition will no longer work in 1.56. [#7579](https://github.com/AnalyticalGraphicsInc/cesium/pull/7579) +##### Additions :tada: +* `czm_materialInput.aspect` was added as an angle in radians between 0 and 2pi (east, north, west to south). +* `Resource.fetchImage` now has a `flipY` option to vertically flip an image during fetch & decode. It is only valid when `ImageBitmapOptions` is supported by the browser. [#7579](https://github.com/AnalyticalGraphicsInc/cesium/pull/7579) + ##### Fixes :wrench: * Fixed an issue where models would cause a crash on load if some primitives were Draco encoded and others were not. [#7383](https://github.com/AnalyticalGraphicsInc/cesium/issues/7383) * Fixed Node.js support for the `Resource` class and any functionality using it internally. From 0964441df2b822b4812bd4a35d806a244de90f2c Mon Sep 17 00:00:00 2001 From: Shehata Date: Mon, 4 Mar 2019 17:48:13 -0500 Subject: [PATCH 13/16] Move ImageBitmap changes to 1.56 --- CHANGES.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index dedff621e21a..73dc545ddd72 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,19 +1,25 @@ Change Log ========== -### 1.55 - 2019-03-01 +### 1.56 - 2019-04-01 ##### Breaking Changes :mega: -* `czm_materialInput.slope` is now an angle in radians between 0 and pi/2 (flat to vertical), rather than a projected length 1 to 0 (flat to vertical). * `Resource.fetchImage` now returns an `ImageBitmap` instead of `Image` when supported. This allows for decoding images while fetching using `createImageBitmap` to greatly speed up texture upload and decrease frame drops when loading models with large textures. [#7579](https://github.com/AnalyticalGraphicsInc/cesium/pull/7579) ##### Deprecated :hourglass_flowing_sand: * `Resource.fetchImage` now takes an options object. Use `resource.fetchImage({ preferBlob: true })` instead of `resource.fetchImage(true)`. The previous function definition will no longer work in 1.56. [#7579](https://github.com/AnalyticalGraphicsInc/cesium/pull/7579) ##### Additions :tada: -* `czm_materialInput.aspect` was added as an angle in radians between 0 and 2pi (east, north, west to south). * `Resource.fetchImage` now has a `flipY` option to vertically flip an image during fetch & decode. It is only valid when `ImageBitmapOptions` is supported by the browser. [#7579](https://github.com/AnalyticalGraphicsInc/cesium/pull/7579) +### 1.55 - 2019-03-01 + +##### Breaking Changes :mega: +* `czm_materialInput.slope` is now an angle in radians between 0 and pi/2 (flat to vertical), rather than a projected length 1 to 0 (flat to vertical). + +##### Additions :tada: +* `czm_materialInput.aspect` was added as an angle in radians between 0 and 2pi (east, north, west to south). + ##### Fixes :wrench: * Fixed an issue where models would cause a crash on load if some primitives were Draco encoded and others were not. [#7383](https://github.com/AnalyticalGraphicsInc/cesium/issues/7383) * Fixed an issue where RTL labels not reversing correctly non alphabetic characters [#7501](https://github.com/AnalyticalGraphicsInc/cesium/pull/7501) From 0b878e73b183d5129c225bd416b2cb1189d1d68c Mon Sep 17 00:00:00 2001 From: Shehata Date: Mon, 4 Mar 2019 18:48:31 -0500 Subject: [PATCH 14/16] Only use createImageBitmap when options are supported --- Source/Core/Resource.js | 94 +++++++++++++------ Source/Core/loadImageFromTypedArray.js | 15 +-- Specs/Core/ResourceSpec.js | 11 ++- .../ArcGisMapServerImageryProviderSpec.js | 4 +- Specs/Scene/BingMapsImageryProviderSpec.js | 2 +- .../DiscardMissingTileImagePolicySpec.js | 2 +- ...oogleEarthEnterpriseImageryProviderSpec.js | 5 +- .../GoogleEarthEnterpriseMapsProviderSpec.js | 10 +- Specs/isImageOrImageBitmap.js | 8 +- 9 files changed, 98 insertions(+), 53 deletions(-) diff --git a/Source/Core/Resource.js b/Source/Core/Resource.js index 304c574b9a6d..25b99ca6114e 100644 --- a/Source/Core/Resource.js +++ b/Source/Core/Resource.js @@ -376,6 +376,7 @@ define([ }); }; + var supportsImageBitmapOptionsResult; var supportsImageBitmapOptionsPromise; /** * A helper function to check whether createImageBitmap supports passing ImageBitmapOptions. @@ -393,7 +394,8 @@ define([ } if (!FeatureDetection.supportsCreateImageBitmap()) { - supportsImageBitmapOptionsPromise = when.resolve(false); + supportsImageBitmapOptionsResult = false; + supportsImageBitmapOptionsPromise = when.resolve(supportsImageBitmapOptionsResult); return supportsImageBitmapOptionsPromise; } @@ -408,15 +410,32 @@ define([ }); }) .then(function(imageBitmap) { - return true; + supportsImageBitmapOptionsResult = true; + return supportsImageBitmapOptionsResult; }) .otherwise(function() { - return false; + supportsImageBitmapOptionsResult = false; + return supportsImageBitmapOptionsResult; }); return supportsImageBitmapOptionsPromise; }; + /** + * Same as Resource.supportsImageBitmapOptions but synchronous. If the result is not ready, returns undefined. + * + * @returns {Boolean} True if this browser supports creating an ImageBitmap with options. + * + * @private + */ + Resource.supportsImageBitmapOptionsSync = function() { + if (!defined(supportsImageBitmapOptionsPromise)) { + Resource.supportsImageBitmapOptions(); + } + + return supportsImageBitmapOptionsResult; + }; + defineProperties(Resource, { /** * Returns true if blobs are supported. @@ -1821,39 +1840,54 @@ define([ */ Resource._Implementations = {}; - Resource._Implementations.createImage = function(url, crossOrigin, deferred, flipY) { - if (!FeatureDetection.supportsCreateImageBitmap()) { - var image = new Image(); + function loadImageElement(url, crossOrigin, deferred) { + var image = new Image(); - image.onload = function() { - deferred.resolve(image); - }; + image.onload = function() { + deferred.resolve(image); + }; - image.onerror = function(e) { - deferred.reject(e); - }; + image.onerror = function(e) { + deferred.reject(e); + }; - if (crossOrigin) { - if (TrustedServers.contains(url)) { - image.crossOrigin = 'use-credentials'; - } else { - image.crossOrigin = ''; - } + if (crossOrigin) { + if (TrustedServers.contains(url)) { + image.crossOrigin = 'use-credentials'; + } else { + image.crossOrigin = ''; } + } - image.src = url; - } else { - // Passing an Image to createImageBitmap will force it to run on the main thread - // since DOM elements don't exist on workers. We convert it to a blob so it's non-blocking. - // See: - // https://bugzilla.mozilla.org/show_bug.cgi?id=1044102#c38 - // https://bugs.chromium.org/p/chromium/issues/detail?id=580202#c10 - Resource.fetchBlob({ - url: url - }).then(function(blob) { - return Resource._Implementations.createImageBitmapFromBlob(blob, flipY); - }).then(deferred.resolve).otherwise(deferred.reject); + image.src = url; + } + + Resource._Implementations.createImage = function(url, crossOrigin, deferred, flipY) { + if (!FeatureDetection.supportsCreateImageBitmap()) { + loadImageElement(url, crossOrigin, deferred); + return; } + + // Passing an Image to createImageBitmap will force it to run on the main thread + // since DOM elements don't exist on workers. We convert it to a blob so it's non-blocking. + // See: + // https://bugzilla.mozilla.org/show_bug.cgi?id=1044102#c38 + // https://bugs.chromium.org/p/chromium/issues/detail?id=580202#c10 + Resource.supportsImageBitmapOptions() + .then(function(result) { + // We can only use ImageBitmap if we can flip on decode. + // See: https://github.com/AnalyticalGraphicsInc/cesium/pull/7579#issuecomment-466146898 + if (!result) { + loadImageElement(url, crossOrigin, deferred); + return; + } + + Resource.fetchBlob({ + url: url + }).then(function(blob) { + return Resource._Implementations.createImageBitmapFromBlob(blob, flipY); + }).then(deferred.resolve).otherwise(deferred.reject); + }); }; Resource._Implementations.createImageBitmapFromBlob = function(blob, flipY) { diff --git a/Source/Core/loadImageFromTypedArray.js b/Source/Core/loadImageFromTypedArray.js index 7ae09f8a30c8..857fdae18844 100644 --- a/Source/Core/loadImageFromTypedArray.js +++ b/Source/Core/loadImageFromTypedArray.js @@ -33,17 +33,10 @@ define([ // Avoid an extra fetch by just calling createImageBitmap here directly on the blob // instead of sending it to Resource as a blob URL. - if (FeatureDetection.supportsCreateImageBitmap()) { - return Resource.supportsImageBitmapOptions() - .then(function(supportsBitmapOptions) { - if (supportsBitmapOptions) { - return createImageBitmap(blob, { - imageOrientation: flipY ? 'flipY' : 'none' - }); - } - - return createImageBitmap(blob); - }); + if (FeatureDetection.supportsCreateImageBitmap() && Resource.supportsImageBitmapOptionsSync()) { + return when(createImageBitmap(blob, { + imageOrientation: flipY ? 'flipY' : 'none' + })); } var blobUrl = window.URL.createObjectURL(blob); diff --git a/Specs/Core/ResourceSpec.js b/Specs/Core/ResourceSpec.js index cac4e31efe6f..8cf4b4cbf5e5 100644 --- a/Specs/Core/ResourceSpec.js +++ b/Specs/Core/ResourceSpec.js @@ -1165,6 +1165,13 @@ defineSuite([ return [imageData.data[0], imageData.data[1], imageData.data[2], imageData.data[3]]; } + it('can call supportsImageBitmapOptions and supportsImageBitmapOptionsSync', function() { + return Resource.supportsImageBitmapOptions() + .then(function(result) { + expect(Resource.supportsImageBitmapOptionsSync()).toEqual(result); + }); + }); + it('can load and decode an image', function() { return Resource.fetchImage('./Data/Images/Green.png').then(function(loadedImage) { expect(loadedImage.width).toEqual(1); @@ -1209,12 +1216,12 @@ defineSuite([ }); }); - it('does not pass options when ImageBitmapOptions are not supported', function() { + it('does not use ImageBitmap when ImageBitmapOptions are not supported', function() { spyOn(Resource, 'supportsImageBitmapOptions').and.returnValue(when.resolve(false)); spyOn(window, 'createImageBitmap').and.callThrough(); return Resource.fetchImage('./Data/Images/Green.png').then(function(loadedImage) { - expect(window.createImageBitmap).toHaveBeenCalledWith(new Blob()); + expect(window.createImageBitmap).not.toHaveBeenCalledWith(); }); }); diff --git a/Specs/Scene/ArcGisMapServerImageryProviderSpec.js b/Specs/Scene/ArcGisMapServerImageryProviderSpec.js index 9cc36109f138..f5ef14c6bbec 100644 --- a/Specs/Scene/ArcGisMapServerImageryProviderSpec.js +++ b/Specs/Scene/ArcGisMapServerImageryProviderSpec.js @@ -292,7 +292,7 @@ defineSuite([ expect(provider.usingPrecachedTiles).toEqual(true); Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url) || FeatureDetection.supportsCreateImageBitmap()) { + if (/^blob:/.test(url) || (FeatureDetection.supportsCreateImageBitmap() && Resource.supportsImageBitmapOptionsSync())) { // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { @@ -465,7 +465,7 @@ defineSuite([ expect(provider.hasAlphaChannel).toBeDefined(); Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url) || FeatureDetection.supportsCreateImageBitmap()) { + if (/^blob:/.test(url) || (FeatureDetection.supportsCreateImageBitmap() && Resource.supportsImageBitmapOptionsSync())) { // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { diff --git a/Specs/Scene/BingMapsImageryProviderSpec.js b/Specs/Scene/BingMapsImageryProviderSpec.js index 0a4b093b6872..591078729dc9 100644 --- a/Specs/Scene/BingMapsImageryProviderSpec.js +++ b/Specs/Scene/BingMapsImageryProviderSpec.js @@ -177,7 +177,7 @@ defineSuite([ function installFakeImageRequest(expectedUrl, expectedParams, proxy) { Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url) || FeatureDetection.supportsCreateImageBitmap()) { + if (/^blob:/.test(url) || (FeatureDetection.supportsCreateImageBitmap() && Resource.supportsImageBitmapOptionsSync())) { // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { diff --git a/Specs/Scene/DiscardMissingTileImagePolicySpec.js b/Specs/Scene/DiscardMissingTileImagePolicySpec.js index d84d40a3a03d..2f303bf9d21b 100644 --- a/Specs/Scene/DiscardMissingTileImagePolicySpec.js +++ b/Specs/Scene/DiscardMissingTileImagePolicySpec.js @@ -70,7 +70,7 @@ defineSuite([ return pollToPromise(function() { return policy.isReady(); }).then(function() { - if (FeatureDetection.supportsCreateImageBitmap()) { + if ((FeatureDetection.supportsCreateImageBitmap() && Resource.supportsImageBitmapOptionsSync())) { expect(Resource._Implementations.createImageBitmapFromBlob).toHaveBeenCalled(); } else { expect(Resource._Implementations.createImage).toHaveBeenCalled(); diff --git a/Specs/Scene/GoogleEarthEnterpriseImageryProviderSpec.js b/Specs/Scene/GoogleEarthEnterpriseImageryProviderSpec.js index b584b6580ad6..68d98060d4f8 100644 --- a/Specs/Scene/GoogleEarthEnterpriseImageryProviderSpec.js +++ b/Specs/Scene/GoogleEarthEnterpriseImageryProviderSpec.js @@ -4,6 +4,7 @@ defineSuite([ 'Core/DefaultProxy', 'Core/defaultValue', 'Core/defined', + 'Core/FeatureDetection', 'Core/GeographicTilingScheme', 'Core/GoogleEarthEnterpriseMetadata', 'Core/GoogleEarthEnterpriseTileInformation', @@ -25,6 +26,7 @@ defineSuite([ DefaultProxy, defaultValue, defined, + FeatureDetection, GeographicTilingScheme, GoogleEarthEnterpriseMetadata, GoogleEarthEnterpriseTileInformation, @@ -44,6 +46,7 @@ defineSuite([ beforeEach(function() { RequestScheduler.clearForSpecs(); + return Resource.supportsImageBitmapOptions(); }); beforeAll(function() { @@ -86,7 +89,7 @@ defineSuite([ function installFakeImageRequest(expectedUrl, proxy) { Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url)) { + if (/^blob:/.test(url) || (FeatureDetection.supportsCreateImageBitmap() && Resource.supportsImageBitmapOptionsSync())) { // load blob url normally Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { diff --git a/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js b/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js index 4d7a67ca1005..2b3be052cd94 100644 --- a/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js +++ b/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js @@ -30,6 +30,12 @@ defineSuite([ isImageOrImageBitmap) { 'use strict'; + beforeAll(function() { + // This suite spies on requests. The test below needs to make a request to a data URI. + // We run it here to avoid interfering with the tests. + return Resource.supportsImageBitmapOptions(); + }); + afterEach(function() { Resource._Implementations.createImage = Resource._DefaultImplementations.createImage; Resource._Implementations.loadWithXhr = Resource._DefaultImplementations.loadWithXhr; @@ -176,7 +182,7 @@ defineSuite([ expect(provider.credit).toBeInstanceOf(Object); Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url) || FeatureDetection.supportsCreateImageBitmap()) { + if (/^blob:/.test(url) || (FeatureDetection.supportsCreateImageBitmap() && Resource.supportsImageBitmapOptionsSync())) { // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { @@ -294,7 +300,7 @@ defineSuite([ }); Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url) || FeatureDetection.supportsCreateImageBitmap()) { + if (/^blob:/.test(url) || (FeatureDetection.supportsCreateImageBitmap() && Resource.supportsImageBitmapOptionsSync())) { // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else if (tries === 2) { diff --git a/Specs/isImageOrImageBitmap.js b/Specs/isImageOrImageBitmap.js index 810cf452548c..5604e91a8e08 100644 --- a/Specs/isImageOrImageBitmap.js +++ b/Specs/isImageOrImageBitmap.js @@ -1,14 +1,16 @@ define([ - 'Core/FeatureDetection' + 'Core/FeatureDetection', + 'Core/Resource' ], function( - FeatureDetection) { + FeatureDetection, + Resource) { 'use strict'; function isImageOrImageBitmap(image) { // Many ImageryProvider specs will test if the requested image // succeeded by checking its instance. Since this may be an Image // or ImageBitmap, we abstract this check here. - if (FeatureDetection.supportsCreateImageBitmap()) { + if (FeatureDetection.supportsCreateImageBitmap() && Resource.supportsImageBitmapOptionsSync()) { return image instanceof ImageBitmap; } From d2cc1733275bf5d4d5218dab5f82c9ae713d86ab Mon Sep 17 00:00:00 2001 From: Shehata Date: Mon, 4 Mar 2019 19:06:04 -0500 Subject: [PATCH 15/16] Fix Firefox specs --- Source/Core/Resource.js | 2 +- Specs/Core/ResourceSpec.js | 25 ++++++++++++++ Specs/Core/loadImageFromTypedArraySpec.js | 40 ++++++++++------------- 3 files changed, 44 insertions(+), 23 deletions(-) diff --git a/Source/Core/Resource.js b/Source/Core/Resource.js index 25b99ca6114e..a38f28e3f538 100644 --- a/Source/Core/Resource.js +++ b/Source/Core/Resource.js @@ -941,7 +941,7 @@ define([ return; } - if (FeatureDetection.supportsCreateImageBitmap()) { + if (FeatureDetection.supportsCreateImageBitmap() && Resource.supportsImageBitmapOptionsSync()) { return blobPromise .then(function(blob) { return Resource._Implementations.createImageBitmapFromBlob(blob, flipY); diff --git a/Specs/Core/ResourceSpec.js b/Specs/Core/ResourceSpec.js index 8cf4b4cbf5e5..8f70d443a149 100644 --- a/Specs/Core/ResourceSpec.js +++ b/Specs/Core/ResourceSpec.js @@ -1152,6 +1152,7 @@ defineSuite([ var canvas; beforeAll(function() { canvas = createCanvas(1, 2); + return Resource.supportsImageBitmapOptions(); }); afterAll(function() { @@ -1166,6 +1167,10 @@ defineSuite([ } it('can call supportsImageBitmapOptions and supportsImageBitmapOptionsSync', function() { + if (!Resource.supportsImageBitmapOptionsSync()) { + return; + } + return Resource.supportsImageBitmapOptions() .then(function(result) { expect(Resource.supportsImageBitmapOptionsSync()).toEqual(result); @@ -1173,6 +1178,10 @@ defineSuite([ }); it('can load and decode an image', function() { + if (!Resource.supportsImageBitmapOptionsSync()) { + return; + } + return Resource.fetchImage('./Data/Images/Green.png').then(function(loadedImage) { expect(loadedImage.width).toEqual(1); expect(loadedImage.height).toEqual(1); @@ -1181,6 +1190,10 @@ defineSuite([ }); it('correctly flips image when ImageBitmapOptions are supported', function() { + if (!Resource.supportsImageBitmapOptionsSync()) { + return; + } + var loadedImage; return Resource.fetchImage({ @@ -1199,6 +1212,10 @@ defineSuite([ }); it('correctly loads image without flip when ImageBitmapOptions are supported', function() { + if (!Resource.supportsImageBitmapOptionsSync()) { + return; + } + var loadedImage; return Resource.fetchImage({ @@ -1217,6 +1234,10 @@ defineSuite([ }); it('does not use ImageBitmap when ImageBitmapOptions are not supported', function() { + if (!Resource.supportsImageBitmapOptionsSync()) { + return; + } + spyOn(Resource, 'supportsImageBitmapOptions').and.returnValue(when.resolve(false)); spyOn(window, 'createImageBitmap').and.callThrough(); @@ -1226,6 +1247,10 @@ defineSuite([ }); it('rejects the promise when the image errors', function() { + if (!Resource.supportsImageBitmapOptionsSync()) { + return; + } + return Resource.fetchImage('http://example.invalid/testuri.png') .then(function() { fail('expected promise to reject'); diff --git a/Specs/Core/loadImageFromTypedArraySpec.js b/Specs/Core/loadImageFromTypedArraySpec.js index 052e3f2d5d06..16013ee3bd06 100644 --- a/Specs/Core/loadImageFromTypedArraySpec.js +++ b/Specs/Core/loadImageFromTypedArraySpec.js @@ -22,7 +22,7 @@ defineSuite([ }); }); - it('flips image when ImageBitmapOptions are supported', function() { + it('flips image only when flipY is true', function() { var options = { uint8Array: new Uint8Array([67, 101, 115, 105, 117, 109]), // This is an invalid PNG. format: 'image/png', @@ -32,32 +32,28 @@ defineSuite([ var blob = new Blob([options.uint8Array], { type : options.format }); - var supportsImageBitmapOptions; - return loadImageFromTypedArray(options) - .then(Resource.supportsImageBitmapOptions) + return Resource.supportsImageBitmapOptions() .then(function(result) { - supportsImageBitmapOptions = result; - if (supportsImageBitmapOptions) { - expect(window.createImageBitmap).toHaveBeenCalledWith(blob, { - imageOrientation: 'flipY' - }); - } else { - expect(window.createImageBitmap).toHaveBeenCalledWith(blob); + if (!result) { + return; } - options.flipY = false; - window.createImageBitmap.calls.reset(); - return loadImageFromTypedArray(options); - }) - .then(function() { - if (supportsImageBitmapOptions) { - expect(window.createImageBitmap).toHaveBeenCalledWith(blob, { - imageOrientation: 'none' + return loadImageFromTypedArray(options) + .then(function() { + expect(window.createImageBitmap).toHaveBeenCalledWith(blob, { + imageOrientation: 'flipY' + }); + + window.createImageBitmap.calls.reset(); + options.flipY = false; + return loadImageFromTypedArray(options); + }) + .then(function() { + expect(window.createImageBitmap).toHaveBeenCalledWith(blob, { + imageOrientation: 'none' + }); }); - } else { - expect(window.createImageBitmap).toHaveBeenCalledWith(blob); - } }); }); From a6324e2bcf8b3547b4adb589e7ee789321997678 Mon Sep 17 00:00:00 2001 From: Shehata Date: Fri, 8 Mar 2019 16:22:38 -0500 Subject: [PATCH 16/16] Remove FeatureDetection.supportsCreateImageBitmap --- Source/Core/FeatureDetection.js | 5 -- Source/Core/Resource.js | 82 +++++++++---------- Source/Core/loadImageFromTypedArray.js | 15 ++-- Specs/Core/FeatureDetectionSpec.js | 5 -- Specs/Core/ResourceSpec.js | 43 +++------- Specs/Core/loadImageFromTypedArraySpec.js | 12 +-- .../ArcGisMapServerImageryProviderSpec.js | 14 ++-- Specs/Scene/BingMapsImageryProviderSpec.js | 14 +++- .../DiscardMissingTileImagePolicySpec.js | 12 +-- ...oogleEarthEnterpriseImageryProviderSpec.js | 14 ++-- .../GoogleEarthEnterpriseMapsProviderSpec.js | 14 ++-- Specs/isImageOrImageBitmap.js | 7 +- 12 files changed, 107 insertions(+), 130 deletions(-) diff --git a/Source/Core/FeatureDetection.js b/Source/Core/FeatureDetection.js index 4392946224b3..620fafa3f8d5 100644 --- a/Source/Core/FeatureDetection.js +++ b/Source/Core/FeatureDetection.js @@ -267,10 +267,6 @@ define([ } } - function supportsCreateImageBitmap() { - return typeof createImageBitmap === 'function'; - } - /** * A set of functions to detect whether the current browser supports * various features. @@ -295,7 +291,6 @@ define([ supportsPointerEvents : supportsPointerEvents, supportsImageRenderingPixelated: supportsImageRenderingPixelated, supportsWebP: supportsWebP, - supportsCreateImageBitmap: supportsCreateImageBitmap, imageRenderingValue: imageRenderingValue, typedArrayTypes: typedArrayTypes }; diff --git a/Source/Core/Resource.js b/Source/Core/Resource.js index a38f28e3f538..9034d45d0a7b 100644 --- a/Source/Core/Resource.js +++ b/Source/Core/Resource.js @@ -376,7 +376,6 @@ define([ }); }; - var supportsImageBitmapOptionsResult; var supportsImageBitmapOptionsPromise; /** * A helper function to check whether createImageBitmap supports passing ImageBitmapOptions. @@ -393,9 +392,8 @@ define([ return supportsImageBitmapOptionsPromise; } - if (!FeatureDetection.supportsCreateImageBitmap()) { - supportsImageBitmapOptionsResult = false; - supportsImageBitmapOptionsPromise = when.resolve(supportsImageBitmapOptionsResult); + if (typeof createImageBitmap !== 'function') { + supportsImageBitmapOptionsPromise = when.resolve(false); return supportsImageBitmapOptionsPromise; } @@ -410,32 +408,15 @@ define([ }); }) .then(function(imageBitmap) { - supportsImageBitmapOptionsResult = true; - return supportsImageBitmapOptionsResult; + return true; }) .otherwise(function() { - supportsImageBitmapOptionsResult = false; - return supportsImageBitmapOptionsResult; + return false; }); return supportsImageBitmapOptionsPromise; }; - /** - * Same as Resource.supportsImageBitmapOptions but synchronous. If the result is not ready, returns undefined. - * - * @returns {Boolean} True if this browser supports creating an ImageBitmap with options. - * - * @private - */ - Resource.supportsImageBitmapOptionsSync = function() { - if (!defined(supportsImageBitmapOptionsPromise)) { - Resource.supportsImageBitmapOptions(); - } - - return supportsImageBitmapOptionsResult; - }; - defineProperties(Resource, { /** * Returns true if blobs are supported. @@ -916,7 +897,7 @@ define([ */ Resource.prototype.fetchImage = function (options) { if (typeof options === 'boolean') { - deprecationWarning('fetchImage-parameter-change', 'fetchImage now takes an options object in CesiumJS 1.55. Use resource.fetchImage({ preferBlob: true }) instead of resource.fetchImage(true).'); + deprecationWarning('fetchImage-parameter-change', 'fetchImage now takes an options object in CesiumJS 1.56. Use resource.fetchImage({ preferBlob: true }) instead of resource.fetchImage(true).'); options = { preferBlob : options }; @@ -941,21 +922,21 @@ define([ return; } - if (FeatureDetection.supportsCreateImageBitmap() && Resource.supportsImageBitmapOptionsSync()) { - return blobPromise - .then(function(blob) { - return Resource._Implementations.createImageBitmapFromBlob(blob, flipY); - }); - } - + var supportsImageBitmap; var generatedBlobResource; var generatedBlob; - return blobPromise + return Resource.supportsImageBitmapOptions() + .then(function(result) { + supportsImageBitmap = result; + return blobPromise; + }) .then(function(blob) { if (!defined(blob)) { return; } - + if (supportsImageBitmap) { + return Resource._Implementations.createImageBitmapFromBlob(blob, flipY); + } generatedBlob = blob; var blobUrl = window.URL.createObjectURL(blob); generatedBlobResource = new Resource({ @@ -968,6 +949,9 @@ define([ if (!defined(image)) { return; } + if (supportsImageBitmap) { + return image; + } window.URL.revokeObjectURL(generatedBlobResource.url); // This is because the blob object is needed for DiscardMissingTileImagePolicy @@ -1863,11 +1847,6 @@ define([ } Resource._Implementations.createImage = function(url, crossOrigin, deferred, flipY) { - if (!FeatureDetection.supportsCreateImageBitmap()) { - loadImageElement(url, crossOrigin, deferred); - return; - } - // Passing an Image to createImageBitmap will force it to run on the main thread // since DOM elements don't exist on workers. We convert it to a blob so it's non-blocking. // See: @@ -1882,18 +1861,31 @@ define([ return; } - Resource.fetchBlob({ + return Resource.fetchBlob({ url: url - }).then(function(blob) { - return Resource._Implementations.createImageBitmapFromBlob(blob, flipY); - }).then(deferred.resolve).otherwise(deferred.reject); - }); + }); + }) + .then(function(blob) { + if (!defined(blob)) { + return; + } + + return Resource._Implementations.createImageBitmapFromBlob(blob, flipY); + }) + .then(function(imageBitmap) { + if (!defined(imageBitmap)) { + return; + } + + deferred.resolve(imageBitmap); + }) + .otherwise(deferred.reject); }; Resource._Implementations.createImageBitmapFromBlob = function(blob, flipY) { return Resource.supportsImageBitmapOptions() - .then(function(supportsBitmapOptions) { - if (!supportsBitmapOptions) { + .then(function(result) { + if (!result) { return createImageBitmap(blob); } diff --git a/Source/Core/loadImageFromTypedArray.js b/Source/Core/loadImageFromTypedArray.js index 857fdae18844..0ee8cc375c01 100644 --- a/Source/Core/loadImageFromTypedArray.js +++ b/Source/Core/loadImageFromTypedArray.js @@ -31,24 +31,19 @@ define([ type : format }); - // Avoid an extra fetch by just calling createImageBitmap here directly on the blob - // instead of sending it to Resource as a blob URL. - if (FeatureDetection.supportsCreateImageBitmap() && Resource.supportsImageBitmapOptionsSync()) { - return when(createImageBitmap(blob, { - imageOrientation: flipY ? 'flipY' : 'none' - })); - } - var blobUrl = window.URL.createObjectURL(blob); var resource = new Resource({ url: blobUrl, request: request }); - return resource.fetchImage() + return resource.fetchImage({ + flipY : flipY + }) .then(function(image) { window.URL.revokeObjectURL(blobUrl); return image; - }, function(error) { + }) + .otherwise(function(error) { window.URL.revokeObjectURL(blobUrl); return when.reject(error); }); diff --git a/Specs/Core/FeatureDetectionSpec.js b/Specs/Core/FeatureDetectionSpec.js index 85490be8bd6a..eb8d4d909d8d 100644 --- a/Specs/Core/FeatureDetectionSpec.js +++ b/Specs/Core/FeatureDetectionSpec.js @@ -134,9 +134,4 @@ defineSuite([ expect(FeatureDetection.supportsWebP()).toEqual(supportsWebP); }); }); - - it('detects createImageBitmap support', function() { - var supportsCreateImageBitmap = FeatureDetection.supportsCreateImageBitmap(); - expect(typeof supportsCreateImageBitmap).toEqual('boolean'); - }); }); diff --git a/Specs/Core/ResourceSpec.js b/Specs/Core/ResourceSpec.js index 8f70d443a149..9203cd4e9c93 100644 --- a/Specs/Core/ResourceSpec.js +++ b/Specs/Core/ResourceSpec.js @@ -2,7 +2,6 @@ defineSuite([ 'Core/Resource', 'Core/defaultValue', 'Core/DefaultProxy', - 'Core/FeatureDetection', 'Core/queryToObject', 'Core/Request', 'Core/RequestErrorEvent', @@ -15,7 +14,6 @@ defineSuite([ Resource, defaultValue, DefaultProxy, - FeatureDetection, queryToObject, Request, RequestErrorEvent, @@ -27,6 +25,14 @@ defineSuite([ 'use strict'; var dataUri = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIW2Nk+M/wHwAEBgIA5agATwAAAABJRU5ErkJggg=='; + var supportsImageBitmapOptions; + + beforeAll(function() { + return Resource.supportsImageBitmapOptions() + .then(function(result) { + supportsImageBitmapOptions = result; + }); + }); it('Constructor sets correct properties', function() { var proxy = new DefaultProxy('/proxy/'); @@ -1145,14 +1151,13 @@ defineSuite([ }); describe('fetchImage with ImageBitmap', function() { - if (!FeatureDetection.supportsCreateImageBitmap()) { + if (!supportsImageBitmapOptions) { return; } var canvas; beforeAll(function() { canvas = createCanvas(1, 2); - return Resource.supportsImageBitmapOptions(); }); afterAll(function() { @@ -1166,22 +1171,14 @@ defineSuite([ return [imageData.data[0], imageData.data[1], imageData.data[2], imageData.data[3]]; } - it('can call supportsImageBitmapOptions and supportsImageBitmapOptionsSync', function() { - if (!Resource.supportsImageBitmapOptionsSync()) { - return; - } - + it('can call supportsImageBitmapOptions', function() { return Resource.supportsImageBitmapOptions() .then(function(result) { - expect(Resource.supportsImageBitmapOptionsSync()).toEqual(result); + expect(typeof result).toEqual('boolean'); }); }); it('can load and decode an image', function() { - if (!Resource.supportsImageBitmapOptionsSync()) { - return; - } - return Resource.fetchImage('./Data/Images/Green.png').then(function(loadedImage) { expect(loadedImage.width).toEqual(1); expect(loadedImage.height).toEqual(1); @@ -1190,10 +1187,6 @@ defineSuite([ }); it('correctly flips image when ImageBitmapOptions are supported', function() { - if (!Resource.supportsImageBitmapOptionsSync()) { - return; - } - var loadedImage; return Resource.fetchImage({ @@ -1212,10 +1205,6 @@ defineSuite([ }); it('correctly loads image without flip when ImageBitmapOptions are supported', function() { - if (!Resource.supportsImageBitmapOptionsSync()) { - return; - } - var loadedImage; return Resource.fetchImage({ @@ -1234,10 +1223,6 @@ defineSuite([ }); it('does not use ImageBitmap when ImageBitmapOptions are not supported', function() { - if (!Resource.supportsImageBitmapOptionsSync()) { - return; - } - spyOn(Resource, 'supportsImageBitmapOptions').and.returnValue(when.resolve(false)); spyOn(window, 'createImageBitmap').and.callThrough(); @@ -1247,10 +1232,6 @@ defineSuite([ }); it('rejects the promise when the image errors', function() { - if (!Resource.supportsImageBitmapOptionsSync()) { - return; - } - return Resource.fetchImage('http://example.invalid/testuri.png') .then(function() { fail('expected promise to reject'); @@ -1266,7 +1247,7 @@ defineSuite([ // Force it to use the Image constructor since these specs all test // specific functionality of this code path. For example, the crossOrigin // restriction does not apply to images loaded with ImageBitmap. - spyOn(FeatureDetection, 'supportsCreateImageBitmap').and.returnValue(false); + spyOn(Resource, 'supportsImageBitmapOptions').and.returnValue(when.resolve(false)); }); it('can load an image', function() { diff --git a/Specs/Core/loadImageFromTypedArraySpec.js b/Specs/Core/loadImageFromTypedArraySpec.js index 16013ee3bd06..05c0f59826d8 100644 --- a/Specs/Core/loadImageFromTypedArraySpec.js +++ b/Specs/Core/loadImageFromTypedArraySpec.js @@ -1,11 +1,11 @@ defineSuite([ 'Core/loadImageFromTypedArray', - 'Core/FeatureDetection', - 'Core/Resource' + 'Core/Resource', + 'ThirdParty/when' ], function( loadImageFromTypedArray, - FeatureDetection, - Resource) { + Resource, + when) { 'use strict'; it('can load an image', function() { @@ -28,7 +28,7 @@ defineSuite([ format: 'image/png', flipY: true }; - spyOn(window, 'createImageBitmap'); + spyOn(window, 'createImageBitmap').and.returnValue(when.resolve({})); var blob = new Blob([options.uint8Array], { type : options.format }); @@ -58,7 +58,7 @@ defineSuite([ }); it('can load an image when ImageBitmap is not supported', function() { - spyOn(FeatureDetection, 'supportsCreateImageBitmap').and.returnValue(false); + spyOn(Resource, 'supportsImageBitmapOptions').and.returnValue(when.resolve(false)); spyOn(window, 'createImageBitmap').and.callThrough(); return Resource.fetchArrayBuffer('./Data/Images/Blue10x10.png').then(function(arrayBuffer) { var options = { diff --git a/Specs/Scene/ArcGisMapServerImageryProviderSpec.js b/Specs/Scene/ArcGisMapServerImageryProviderSpec.js index f5ef14c6bbec..cc6b5fb372f7 100644 --- a/Specs/Scene/ArcGisMapServerImageryProviderSpec.js +++ b/Specs/Scene/ArcGisMapServerImageryProviderSpec.js @@ -6,7 +6,6 @@ defineSuite([ 'Core/Cartographic', 'Core/DefaultProxy', 'Core/defined', - 'Core/FeatureDetection', 'Core/GeographicTilingScheme', 'Core/getAbsoluteUri', 'Core/objectToQuery', @@ -33,7 +32,6 @@ defineSuite([ Cartographic, DefaultProxy, defined, - FeatureDetection, GeographicTilingScheme, getAbsoluteUri, objectToQuery, @@ -54,10 +52,14 @@ defineSuite([ Uri) { 'use strict'; + var supportsImageBitmapOptions; beforeAll(function() { - // This suite spies on requests. The test below needs to make a request to a data URI. + // This suite spies on requests. Resource.supportsImageBitmapOptions needs to make a request to a data URI. // We run it here to avoid interfering with the tests. - return Resource.supportsImageBitmapOptions(); + return Resource.supportsImageBitmapOptions() + .then(function(result) { + supportsImageBitmapOptions = result; + }); }); beforeEach(function() { @@ -292,7 +294,7 @@ defineSuite([ expect(provider.usingPrecachedTiles).toEqual(true); Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url) || (FeatureDetection.supportsCreateImageBitmap() && Resource.supportsImageBitmapOptionsSync())) { + if (/^blob:/.test(url) || supportsImageBitmapOptions) { // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { @@ -465,7 +467,7 @@ defineSuite([ expect(provider.hasAlphaChannel).toBeDefined(); Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url) || (FeatureDetection.supportsCreateImageBitmap() && Resource.supportsImageBitmapOptionsSync())) { + if (/^blob:/.test(url) || supportsImageBitmapOptions) { // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { diff --git a/Specs/Scene/BingMapsImageryProviderSpec.js b/Specs/Scene/BingMapsImageryProviderSpec.js index 591078729dc9..2d9a06f31184 100644 --- a/Specs/Scene/BingMapsImageryProviderSpec.js +++ b/Specs/Scene/BingMapsImageryProviderSpec.js @@ -3,7 +3,6 @@ defineSuite([ 'Core/appendForwardSlash', 'Core/DefaultProxy', 'Core/defined', - 'Core/FeatureDetection', 'Core/queryToObject', 'Core/RequestScheduler', 'Core/Resource', @@ -22,7 +21,6 @@ defineSuite([ appendForwardSlash, DefaultProxy, defined, - FeatureDetection, queryToObject, RequestScheduler, Resource, @@ -38,6 +36,16 @@ defineSuite([ Uri) { 'use strict'; + var supportsImageBitmapOptions; + beforeAll(function() { + // This suite spies on requests. Resource.supportsImageBitmapOptions needs to make a request to a data URI. + // We run it here to avoid interfering with the tests. + return Resource.supportsImageBitmapOptions() + .then(function(result) { + supportsImageBitmapOptions = result; + }); + }); + beforeEach(function() { RequestScheduler.clearForSpecs(); }); @@ -177,7 +185,7 @@ defineSuite([ function installFakeImageRequest(expectedUrl, expectedParams, proxy) { Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url) || (FeatureDetection.supportsCreateImageBitmap() && Resource.supportsImageBitmapOptionsSync())) { + if (/^blob:/.test(url) || supportsImageBitmapOptions) { // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { diff --git a/Specs/Scene/DiscardMissingTileImagePolicySpec.js b/Specs/Scene/DiscardMissingTileImagePolicySpec.js index 2f303bf9d21b..0cb9cd5bf040 100644 --- a/Specs/Scene/DiscardMissingTileImagePolicySpec.js +++ b/Specs/Scene/DiscardMissingTileImagePolicySpec.js @@ -1,23 +1,25 @@ defineSuite([ 'Scene/DiscardMissingTileImagePolicy', 'Core/Cartesian2', - 'Core/FeatureDetection', 'Core/Resource', 'Specs/pollToPromise', 'ThirdParty/when' ], function( DiscardMissingTileImagePolicy, Cartesian2, - FeatureDetection, Resource, pollToPromise, when) { 'use strict'; + var supportsImageBitmapOptions; beforeAll(function() { - // This suite spies on requests. The test below needs to make a request to a data URI. + // This suite spies on requests. Resource.supportsImageBitmapOptions needs to make a request to a data URI. // We run it here to avoid interfering with the tests. - return Resource.supportsImageBitmapOptions(); + return Resource.supportsImageBitmapOptions() + .then(function(result) { + supportsImageBitmapOptions = result; + }); }); afterEach(function() { @@ -70,7 +72,7 @@ defineSuite([ return pollToPromise(function() { return policy.isReady(); }).then(function() { - if ((FeatureDetection.supportsCreateImageBitmap() && Resource.supportsImageBitmapOptionsSync())) { + if (supportsImageBitmapOptions) { expect(Resource._Implementations.createImageBitmapFromBlob).toHaveBeenCalled(); } else { expect(Resource._Implementations.createImage).toHaveBeenCalled(); diff --git a/Specs/Scene/GoogleEarthEnterpriseImageryProviderSpec.js b/Specs/Scene/GoogleEarthEnterpriseImageryProviderSpec.js index 68d98060d4f8..55a0a7a6c070 100644 --- a/Specs/Scene/GoogleEarthEnterpriseImageryProviderSpec.js +++ b/Specs/Scene/GoogleEarthEnterpriseImageryProviderSpec.js @@ -4,7 +4,6 @@ defineSuite([ 'Core/DefaultProxy', 'Core/defaultValue', 'Core/defined', - 'Core/FeatureDetection', 'Core/GeographicTilingScheme', 'Core/GoogleEarthEnterpriseMetadata', 'Core/GoogleEarthEnterpriseTileInformation', @@ -26,7 +25,6 @@ defineSuite([ DefaultProxy, defaultValue, defined, - FeatureDetection, GeographicTilingScheme, GoogleEarthEnterpriseMetadata, GoogleEarthEnterpriseTileInformation, @@ -46,11 +44,17 @@ defineSuite([ beforeEach(function() { RequestScheduler.clearForSpecs(); - return Resource.supportsImageBitmapOptions(); }); + var supportsImageBitmapOptions; beforeAll(function() { decodeGoogleEarthEnterpriseData.passThroughDataForTesting = true; + // This suite spies on requests. Resource.supportsImageBitmapOptions needs to make a request to a data URI. + // We run it here to avoid interfering with the tests. + return Resource.supportsImageBitmapOptions() + .then(function(result) { + supportsImageBitmapOptions = result; + }); }); afterAll(function() { @@ -89,7 +93,7 @@ defineSuite([ function installFakeImageRequest(expectedUrl, proxy) { Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url) || (FeatureDetection.supportsCreateImageBitmap() && Resource.supportsImageBitmapOptionsSync())) { + if (/^blob:/.test(url) || supportsImageBitmapOptions) { // load blob url normally Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { @@ -106,7 +110,7 @@ defineSuite([ }; Resource._Implementations.loadWithXhr = function(url, responseType, method, data, headers, deferred, overrideMimeType) { - if (defined(expectedUrl)) { + if (defined(expectedUrl) && !/^blob:/.test(url)) { if (proxy) { var uri = new Uri(url); url = decodeURIComponent(uri.query); diff --git a/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js b/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js index 2b3be052cd94..32924b25b4ee 100644 --- a/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js +++ b/Specs/Scene/GoogleEarthEnterpriseMapsProviderSpec.js @@ -1,7 +1,6 @@ defineSuite([ 'Scene/GoogleEarthEnterpriseMapsProvider', 'Core/DefaultProxy', - 'Core/FeatureDetection', 'Core/GeographicTilingScheme', 'Core/Rectangle', 'Core/RequestScheduler', @@ -16,7 +15,6 @@ defineSuite([ ], function( GoogleEarthEnterpriseMapsProvider, DefaultProxy, - FeatureDetection, GeographicTilingScheme, Rectangle, RequestScheduler, @@ -30,10 +28,14 @@ defineSuite([ isImageOrImageBitmap) { 'use strict'; + var supportsImageBitmapOptions; beforeAll(function() { - // This suite spies on requests. The test below needs to make a request to a data URI. + // This suite spies on requests. Resource.supportsImageBitmapOptions needs to make a request to a data URI. // We run it here to avoid interfering with the tests. - return Resource.supportsImageBitmapOptions(); + return Resource.supportsImageBitmapOptions() + .then(function(result) { + supportsImageBitmapOptions = result; + }); }); afterEach(function() { @@ -182,7 +184,7 @@ defineSuite([ expect(provider.credit).toBeInstanceOf(Object); Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url) || (FeatureDetection.supportsCreateImageBitmap() && Resource.supportsImageBitmapOptionsSync())) { + if (/^blob:/.test(url) || supportsImageBitmapOptions) { // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else { @@ -300,7 +302,7 @@ defineSuite([ }); Resource._Implementations.createImage = function(url, crossOrigin, deferred) { - if (/^blob:/.test(url) || (FeatureDetection.supportsCreateImageBitmap() && Resource.supportsImageBitmapOptionsSync())) { + if (/^blob:/.test(url) || supportsImageBitmapOptions) { // If ImageBitmap is supported, we expect a loadWithXhr request to fetch it as a blob. Resource._DefaultImplementations.createImage(url, crossOrigin, deferred); } else if (tries === 2) { diff --git a/Specs/isImageOrImageBitmap.js b/Specs/isImageOrImageBitmap.js index 5604e91a8e08..bc1cdd86f024 100644 --- a/Specs/isImageOrImageBitmap.js +++ b/Specs/isImageOrImageBitmap.js @@ -10,11 +10,12 @@ define([ // Many ImageryProvider specs will test if the requested image // succeeded by checking its instance. Since this may be an Image // or ImageBitmap, we abstract this check here. - if (FeatureDetection.supportsCreateImageBitmap() && Resource.supportsImageBitmapOptionsSync()) { - return image instanceof ImageBitmap; + var isImageBitmap = false; + if (typeof createImageBitmap === 'function') { + isImageBitmap = image instanceof ImageBitmap; } - return image instanceof Image; + return isImageBitmap || image instanceof Image; } return isImageOrImageBitmap;