Skip to content

Commit 4fb9fbb

Browse files
authored
Merge pull request #9624 from CesiumGS/texture-encoding-gltf-bug
Add option to ignore colorspace information in texture images
2 parents d9ab862 + 99f7a7b commit 4fb9fbb

25 files changed

+623
-132
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- Added checks for supported 3D Tiles extensions. [#9552](https://github.com/CesiumGS/cesium/issues/9552)
1111
- Added documentation clarifying that the `outlineWidth` property will be ignored on all major browsers on Windows platforms. [#9600](https://github.com/CesiumGS/cesium/pull/9600)
1212
- Added documentation for `KmlTour`, `KmlTourFlyTo`, and `KmlTourWait`. Added documentation and a `kmlTours` getter to `KmlDataSource`. Removed references to `KmlTourSoundCues`. [#8073](https://github.com/CesiumGS/cesium/issues/8073)
13+
- Added option to ignore extraneous colorspace information in glTF textures and `ImageBitmap`. [#9624](https://github.com/CesiumGS/cesium/pull/9624)
1314

1415
##### Fixes :wrench:
1516

Source/Core/Resource.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@ Resource.supportsImageBitmapOptions = function () {
379379
return createImageBitmap(blob, {
380380
imageOrientation: "flipY",
381381
premultiplyAlpha: "none",
382+
colorSpaceConversion: "none",
382383
});
383384
})
384385
.then(function (imageBitmap) {
@@ -856,6 +857,7 @@ Resource.fetchBlob = function (options) {
856857
* @param {Boolean} [options.preferBlob=false] If true, we will load the image via a blob.
857858
* @param {Boolean} [options.preferImageBitmap=false] If true, image will be decoded during fetch and an <code>ImageBitmap</code> is returned.
858859
* @param {Boolean} [options.flipY=false] If true, image will be vertically flipped during decode. Only applies if the browser supports <code>createImageBitmap</code>.
860+
* @param {Boolean} [options.skipColorSpaceConversion=false] If true, any custom gamma or color profiles in the image will be ignored. Only applies if the browser supports <code>createImageBitmap</code>.
859861
* @returns {Promise.<ImageBitmap>|Promise.<HTMLImageElement>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if <code>request.throttle</code> is true and the request does not have high enough priority.
860862
*
861863
*
@@ -880,9 +882,12 @@ Resource.prototype.fetchImage = function (options) {
880882
var preferImageBitmap = defaultValue(options.preferImageBitmap, false);
881883
var preferBlob = defaultValue(options.preferBlob, false);
882884
var flipY = defaultValue(options.flipY, false);
885+
var skipColorSpaceConversion = defaultValue(
886+
options.skipColorSpaceConversion,
887+
false
888+
);
883889

884890
checkAndResetRequest(this.request);
885-
886891
// We try to load the image normally if
887892
// 1. Blobs aren't supported
888893
// 2. It's a data URI
@@ -897,6 +902,7 @@ Resource.prototype.fetchImage = function (options) {
897902
return fetchImage({
898903
resource: this,
899904
flipY: flipY,
905+
skipColorSpaceConversion: skipColorSpaceConversion,
900906
preferImageBitmap: preferImageBitmap,
901907
});
902908
}
@@ -925,6 +931,7 @@ Resource.prototype.fetchImage = function (options) {
925931
return Resource.createImageBitmapFromBlob(blob, {
926932
flipY: flipY,
927933
premultiplyAlpha: false,
934+
skipColorSpaceConversion: skipColorSpaceConversion,
928935
});
929936
}
930937
var blobUrl = window.URL.createObjectURL(blob);
@@ -935,6 +942,7 @@ Resource.prototype.fetchImage = function (options) {
935942
return fetchImage({
936943
resource: generatedBlobResource,
937944
flipY: flipY,
945+
skipColorSpaceConversion: skipColorSpaceConversion,
938946
preferImageBitmap: false,
939947
});
940948
})
@@ -976,12 +984,13 @@ Resource.prototype.fetchImage = function (options) {
976984
* @param {Resource} [options.resource] Resource object that points to an image to fetch.
977985
* @param {Boolean} [options.preferImageBitmap] If true, image will be decoded during fetch and an <code>ImageBitmap</code> is returned.
978986
* @param {Boolean} [options.flipY] If true, image will be vertically flipped during decode. Only applies if the browser supports <code>createImageBitmap</code>.
979-
*
987+
* @param {Boolean} [options.skipColorSpaceConversion=false] If true, any custom gamma or color profiles in the image will be ignored. Only applies if the browser supports <code>createImageBitmap</code>.
980988
* @private
981989
*/
982990
function fetchImage(options) {
983991
var resource = options.resource;
984992
var flipY = options.flipY;
993+
var skipColorSpaceConversion = options.skipColorSpaceConversion;
985994
var preferImageBitmap = options.preferImageBitmap;
986995

987996
var request = resource.request;
@@ -1000,6 +1009,7 @@ function fetchImage(options) {
10001009
crossOrigin,
10011010
deferred,
10021011
flipY,
1012+
skipColorSpaceConversion,
10031013
preferImageBitmap
10041014
);
10051015

@@ -1026,6 +1036,7 @@ function fetchImage(options) {
10261036
return fetchImage({
10271037
resource: resource,
10281038
flipY: flipY,
1039+
skipColorSpaceConversion: skipColorSpaceConversion,
10291040
preferImageBitmap: preferImageBitmap,
10301041
});
10311042
}
@@ -1050,12 +1061,14 @@ function fetchImage(options) {
10501061
* @param {Request} [options.request] A Request object that will be used. Intended for internal use only.
10511062
* @param {Boolean} [options.preferBlob=false] If true, we will load the image via a blob.
10521063
* @param {Boolean} [options.preferImageBitmap=false] If true, image will be decoded during fetch and an <code>ImageBitmap</code> is returned.
1064+
* @param {Boolean} [options.skipColorSpaceConversion=false] If true, any custom gamma or color profiles in the image will be ignored. Only applies when requesting an image and the browser supports <code>createImageBitmap</code>.
10531065
* @returns {Promise.<ImageBitmap>|Promise.<HTMLImageElement>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if <code>request.throttle</code> is true and the request does not have high enough priority.
10541066
*/
10551067
Resource.fetchImage = function (options) {
10561068
var resource = new Resource(options);
10571069
return resource.fetchImage({
10581070
flipY: options.flipY,
1071+
skipColorSpaceConversion: options.skipColorSpaceConversion,
10591072
preferBlob: options.preferBlob,
10601073
preferImageBitmap: options.preferImageBitmap,
10611074
});
@@ -1888,6 +1901,7 @@ Resource._Implementations.createImage = function (
18881901
crossOrigin,
18891902
deferred,
18901903
flipY,
1904+
skipColorSpaceConversion,
18911905
preferImageBitmap
18921906
) {
18931907
var url = request.url;
@@ -1940,6 +1954,7 @@ Resource._Implementations.createImage = function (
19401954
return Resource.createImageBitmapFromBlob(blob, {
19411955
flipY: flipY,
19421956
premultiplyAlpha: false,
1957+
skipColorSpaceConversion: skipColorSpaceConversion,
19431958
});
19441959
})
19451960
.then(deferred.resolve);
@@ -1956,10 +1971,15 @@ Resource.createImageBitmapFromBlob = function (blob, options) {
19561971
Check.defined("options", options);
19571972
Check.typeOf.bool("options.flipY", options.flipY);
19581973
Check.typeOf.bool("options.premultiplyAlpha", options.premultiplyAlpha);
1974+
Check.typeOf.bool(
1975+
"options.skipColorSpaceConversion",
1976+
options.skipColorSpaceConversion
1977+
);
19591978

19601979
return createImageBitmap(blob, {
19611980
imageOrientation: options.flipY ? "flipY" : "none",
19621981
premultiplyAlpha: options.premultiplyAlpha ? "premultiply" : "none",
1982+
colorSpaceConversion: options.skipColorSpaceConversion ? "none" : "default",
19631983
});
19641984
};
19651985

Source/Core/loadImageFromTypedArray.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ function loadImageFromTypedArray(options) {
1212
var format = options.format;
1313
var request = options.request;
1414
var flipY = defaultValue(options.flipY, false);
15+
var skipColorSpaceConversion = defaultValue(
16+
options.skipColorSpaceConversion,
17+
false
18+
);
1519
//>>includeStart('debug', pragmas.debug);
1620
Check.typeOf.object("uint8Array", uint8Array);
1721
Check.typeOf.string("format", format);
@@ -29,6 +33,7 @@ function loadImageFromTypedArray(options) {
2933
Resource.createImageBitmapFromBlob(blob, {
3034
flipY: flipY,
3135
premultiplyAlpha: false,
36+
skipColorSpaceConversion: skipColorSpaceConversion,
3237
})
3338
);
3439
}
@@ -41,6 +46,7 @@ function loadImageFromTypedArray(options) {
4146

4247
return resource.fetchImage({
4348
flipY: flipY,
49+
skipColorSpaceConversion: skipColorSpaceConversion,
4450
});
4551
})
4652
.then(function (result) {

Source/Renderer/CubeMap.js

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,10 @@ function CubeMap(options) {
148148
pixelFormat === PixelFormat.RGB ||
149149
pixelFormat === PixelFormat.LUMINANCE;
150150
var flipY = defaultValue(options.flipY, true);
151+
var skipColorSpaceConversion = defaultValue(
152+
options.skipColorSpaceConversion,
153+
false
154+
);
151155

152156
var gl = context._gl;
153157
var textureTarget = gl.TEXTURE_CUBE_MAP;
@@ -156,7 +160,13 @@ function CubeMap(options) {
156160
gl.activeTexture(gl.TEXTURE0);
157161
gl.bindTexture(textureTarget, texture);
158162

159-
function createFace(target, sourceFace, preMultiplyAlpha, flipY) {
163+
function createFace(
164+
target,
165+
sourceFace,
166+
preMultiplyAlpha,
167+
flipY,
168+
skipColorSpaceConversion
169+
) {
160170
var arrayBufferView = sourceFace.arrayBufferView;
161171
if (!defined(arrayBufferView)) {
162172
arrayBufferView = sourceFace.bufferView;
@@ -173,6 +183,15 @@ function CubeMap(options) {
173183

174184
gl.pixelStorei(gl.UNPACK_ALIGNMENT, unpackAlignment);
175185

186+
if (skipColorSpaceConversion) {
187+
gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
188+
} else {
189+
gl.pixelStorei(
190+
gl.UNPACK_COLORSPACE_CONVERSION_WEBGL,
191+
gl.BROWSER_DEFAULT_WEBGL
192+
);
193+
}
194+
176195
if (defined(arrayBufferView)) {
177196
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
178197
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
@@ -219,37 +238,43 @@ function CubeMap(options) {
219238
gl.TEXTURE_CUBE_MAP_POSITIVE_X,
220239
source.positiveX,
221240
preMultiplyAlpha,
222-
flipY
241+
flipY,
242+
skipColorSpaceConversion
223243
);
224244
createFace(
225245
gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
226246
source.negativeX,
227247
preMultiplyAlpha,
228-
flipY
248+
flipY,
249+
skipColorSpaceConversion
229250
);
230251
createFace(
231252
gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
232253
source.positiveY,
233254
preMultiplyAlpha,
234-
flipY
255+
flipY,
256+
skipColorSpaceConversion
235257
);
236258
createFace(
237259
gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
238260
source.negativeY,
239261
preMultiplyAlpha,
240-
flipY
262+
flipY,
263+
skipColorSpaceConversion
241264
);
242265
createFace(
243266
gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
244267
source.positiveZ,
245268
preMultiplyAlpha,
246-
flipY
269+
flipY,
270+
skipColorSpaceConversion
247271
);
248272
createFace(
249273
gl.TEXTURE_CUBE_MAP_NEGATIVE_Z,
250274
source.negativeZ,
251275
preMultiplyAlpha,
252-
flipY
276+
flipY,
277+
skipColorSpaceConversion
253278
);
254279
} else {
255280
gl.texImage2D(

Source/Renderer/CubeMapFace.js

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,12 @@ Object.defineProperties(CubeMapFace.prototype, {
5454

5555
/**
5656
* Copies texels from the source to the cubemap's face.
57-
*
58-
* @param {Object} source The source ImageData, HTMLImageElement, HTMLCanvasElement, HTMLVideoElement, or an object with a width, height, and typed array as shown in the example.
59-
* @param {Number} [xOffset=0] An offset in the x direction in the cubemap where copying begins.
60-
* @param {Number} [yOffset=0] An offset in the y direction in the cubemap where copying begins.
61-
*
57+
* @param {Object} options Object with the following properties:
58+
* @param {Object} options.source The source {@link ImageData}, {@link HTMLImageElement}, {@link HTMLCanvasElement}, {@link HTMLVideoElement},
59+
* or an object with a width, height, and arrayBufferView properties.
60+
* @param {Number} [options.xOffset=0] An offset in the x direction in the cubemap where copying begins.
61+
* @param {Number} [options.yOffset=0] An offset in the y direction in the cubemap where copying begins.
62+
* @param {Boolean} [options.skipColorSpaceConversion=false] If true, any custom gamma or color profiles in the texture will be ignored.
6263
* @exception {DeveloperError} xOffset must be greater than or equal to zero.
6364
* @exception {DeveloperError} yOffset must be greater than or equal to zero.
6465
* @exception {DeveloperError} xOffset + source.width must be less than or equal to width.
@@ -73,31 +74,39 @@ Object.defineProperties(CubeMapFace.prototype, {
7374
* height : 1
7475
* });
7576
* cubeMap.positiveX.copyFrom({
76-
* width : 1,
77-
* height : 1,
78-
* arrayBufferView : new Uint8Array([255, 0, 0, 255])
77+
* source: {
78+
* width : 1,
79+
* height : 1,
80+
* arrayBufferView : new Uint8Array([255, 0, 0, 255])
81+
* }
7982
* });
8083
*/
81-
CubeMapFace.prototype.copyFrom = function (source, xOffset, yOffset) {
82-
xOffset = defaultValue(xOffset, 0);
83-
yOffset = defaultValue(yOffset, 0);
84+
CubeMapFace.prototype.copyFrom = function (options) {
85+
//>>includeStart('debug', pragmas.debug);
86+
Check.defined("options", options);
87+
//>>includeEnd('debug');
88+
89+
var xOffset = defaultValue(options.xOffset, 0);
90+
var yOffset = defaultValue(options.yOffset, 0);
8491

8592
//>>includeStart('debug', pragmas.debug);
86-
Check.defined("source", source);
93+
Check.defined("options.source", options.source);
8794
Check.typeOf.number.greaterThanOrEquals("xOffset", xOffset, 0);
8895
Check.typeOf.number.greaterThanOrEquals("yOffset", yOffset, 0);
89-
if (xOffset + source.width > this._size) {
96+
if (xOffset + options.source.width > this._size) {
9097
throw new DeveloperError(
91-
"xOffset + source.width must be less than or equal to width."
98+
"xOffset + options.source.width must be less than or equal to width."
9299
);
93100
}
94-
if (yOffset + source.height > this._size) {
101+
if (yOffset + options.source.height > this._size) {
95102
throw new DeveloperError(
96-
"yOffset + source.height must be less than or equal to height."
103+
"yOffset + options.source.height must be less than or equal to height."
97104
);
98105
}
99106
//>>includeEnd('debug');
100107

108+
var source = options.source;
109+
101110
var gl = this._context._gl;
102111
var target = this._textureTarget;
103112
var targetFace = this._targetFace;
@@ -116,6 +125,10 @@ CubeMapFace.prototype.copyFrom = function (source, xOffset, yOffset) {
116125

117126
var preMultiplyAlpha = this._preMultiplyAlpha;
118127
var flipY = this._flipY;
128+
var skipColorSpaceConversion = defaultValue(
129+
options.skipColorSpaceConversion,
130+
false
131+
);
119132

120133
var unpackAlignment = 4;
121134
if (defined(arrayBufferView)) {
@@ -128,6 +141,15 @@ CubeMapFace.prototype.copyFrom = function (source, xOffset, yOffset) {
128141

129142
gl.pixelStorei(gl.UNPACK_ALIGNMENT, unpackAlignment);
130143

144+
if (skipColorSpaceConversion) {
145+
gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
146+
} else {
147+
gl.pixelStorei(
148+
gl.UNPACK_COLORSPACE_CONVERSION_WEBGL,
149+
gl.BROWSER_DEFAULT_WEBGL
150+
);
151+
}
152+
131153
var uploaded = false;
132154
if (!this._initialized) {
133155
if (xOffset === 0 && yOffset === 0 && width === size && height === size) {

0 commit comments

Comments
 (0)