Skip to content

Commit

Permalink
Use image bitmap and flip during fetch
Browse files Browse the repository at this point in the history
  • Loading branch information
Shehata committed Feb 18, 2019
1 parent 96cbc18 commit b772d52
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 22 deletions.
84 changes: 66 additions & 18 deletions Source/Core/Resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ define([
'./deprecationWarning',
'./DeveloperError',
'./freezeObject',
'./FeatureDetection',
'./getAbsoluteUri',
'./getBaseUri',
'./getExtensionFromUri',
Expand Down Expand Up @@ -39,6 +40,7 @@ define([
deprecationWarning,
DeveloperError,
freezeObject,
FeatureDetection,
getAbsoluteUri,
getBaseUri,
getExtensionFromUri,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -664,6 +676,9 @@ define([
if (defined(options.retryAttempts)) {
resource.retryAttempts = options.retryAttempts;
}
if (defined(options.flipImage)) {
resource.flipImage = options.flipImage;
}

return resource;
};
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -851,6 +869,7 @@ define([
*/
Resource.prototype.fetchImage = function (preferBlob) {
preferBlob = defaultValue(preferBlob, false);
var flipImage = this.flipImage;

checkAndResetRequest(this.request);

Expand All @@ -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();
Expand All @@ -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);
Expand Down Expand Up @@ -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.
Expand All @@ -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;
};
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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) {
Expand Down
25 changes: 24 additions & 1 deletion Source/Core/loadImageFromTypedArray.js
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -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,
Expand Down
5 changes: 4 additions & 1 deletion Source/Scene/GoogleEarthEnterpriseImageryProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,10 @@ define([
return invalidImage;
}

return loadImageFromTypedArray(a, type);
return loadImageFromTypedArray({
uint8Array: a,
format: type
});
});
};

Expand Down
9 changes: 7 additions & 2 deletions Source/Scene/Model.js
Original file line number Diff line number Diff line change
Expand Up @@ -1714,7 +1714,8 @@ define([
++model._loadResources.pendingTextureLoads;

var imageResource = model._resource.getDerivedResource({
url : uri
url : uri,
flipImage : false
});

var promise;
Expand Down Expand Up @@ -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;
}
Expand Down
6 changes: 6 additions & 0 deletions Source/Widgets/Viewer/Viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ define([
'../../Core/DeveloperError',
'../../Core/Event',
'../../Core/EventHelper',
'../../Core/FeatureDetection',
'../../Core/HeadingPitchRange',
'../../Core/isArray',
'../../Core/Matrix4',
Expand Down Expand Up @@ -60,6 +61,7 @@ define([
DeveloperError,
Event,
EventHelper,
FeatureDetection,
HeadingPitchRange,
isArray,
Matrix4,
Expand Down Expand Up @@ -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);

Expand Down

0 comments on commit b772d52

Please sign in to comment.