diff --git a/src/textures.js b/src/textures.js index 340307b9..6e6b19df 100644 --- a/src/textures.js +++ b/src/textures.js @@ -612,8 +612,8 @@ function setDefaults(newDefaults) { /** * Saves the current packing state, sets the packing state as specified * then calls a function, after which the packing state will be restored. - * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set. * @param {WebGLRenderingContext} gl the WebGLRenderingContext + * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set. * @param {function():void} [fn] A function to call, after which the packing state will be restored. * @private */ @@ -648,6 +648,36 @@ function scopedSetPackState(gl, options, fn) { } } +/** + * returns the property if set or the corresponding state if undefined + * @param {WebGLRenderingContext} gl the WebGLRenderingContext + * @param {module:twgl.TextureOptions} options + * @param {string} property the name of the property to copy + * @param {number} pname + * @return {module:twgl.TextureOptions} + */ +function getPackStateOption(gl, options, property, pname) { + const v = options[property]; + return { + [property]: v === undefined ? gl.getParameter(pname) : v, + }; +} + +/** + * Copy the options object and apply pack state + * @param {WebGLRenderingContext} gl the WebGLRenderingContext + * @param {module:twgl.TextureOptions} options + * @return {module:twgl.TextureOptions} + */ +function copyOptionsAndApplyPackState(gl, options) { + return { + ...options, + ...getPackStateOption(gl, options, 'flipY', UNPACK_FLIP_Y_WEBGL), + ...getPackStateOption(gl, options, 'premultiplyAlpha', UNPACK_PREMULTIPLY_ALPHA_WEBGL), + ...getPackStateOption(gl, options, 'colorspaceConversion', UNPACK_COLORSPACE_CONVERSION_WEBGL), + }; +} + /** * Set skip state to defaults * @param {WebGLRenderingContext} gl the WebGLRenderingContext @@ -1282,7 +1312,7 @@ function loadTextureFromUrl(gl, tex, options, callback) { options = options || defaults.textureOptions; setTextureTo1PixelColor(gl, tex, options); // Because it's async we need to copy the options. - options = Object.assign({}, options); + options = copyOptionsAndApplyPackState(gl, options); const img = loadAndUseImage(options.src, options.crossOrigin, function(err, img) { if (err) { callback(err, tex, img); @@ -1322,7 +1352,7 @@ function loadCubemapFromUrls(gl, tex, options, callback) { } setTextureTo1PixelColor(gl, tex, options); // Because it's async we need to copy the options. - options = Object.assign({}, options); + options = copyOptionsAndApplyPackState(gl, options); let numToLoad = 6; const errors = []; const faces = getCubeFaceOrder(gl, options); @@ -1403,7 +1433,7 @@ function loadSlicesFromUrls(gl, tex, options, callback) { } setTextureTo1PixelColor(gl, tex, options); // Because it's async we need to copy the options. - options = Object.assign({}, options); + options = copyOptionsAndApplyPackState(gl, options); let numToLoad = urls.length; const errors = []; let imgs; // eslint-disable-line @@ -1593,6 +1623,25 @@ function setEmptyTexture(gl, tex, options) { * Note: may reset UNPACK_ALIGNMENT, UNPACK_ROW_LENGTH, UNPACK_IMAGE_HEIGHT, UNPACK_SKIP_IMAGES * UNPACK_SKIP_PIXELS, and UNPACK_SKIP_ROWS * + * UNPACK_FLIP_Y_WEBGL, UNPACK_PREMULTIPLY_ALPHA_WEBGL, UNPACK_COLORSPACE_CONVERSION_WEBGL + * are left as is though you can pass in options for flipY, premultiplyAlpha, and colorspaceConversion + * to override them. + * + * As for the behavior of these settings + * + * ```js + * gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + * t1 = twgl.createTexture({src: someImage }); // flipped + * t2 = twgl.createTexture({src: someImage, flipY: true }); // flipped + * t3 = twgl.createTexture({src: someImage, flipY: false }); // not flipped + * t4 = twgl.createTexture({src: someImage }); // flipped + * ``` + * + * * t1 is flipped because UNPACK_FLIP_Y_WEBGL is true + * * t2 is flipped because it was requested + * * t3 is not flipped because it was requested + * * t4 is flipped because UNPACK_FLIP_Y_WEBGL has been restored to true + * * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set. * @param {module:twgl.TextureReadyCallback} [callback] A callback called when an image has been downloaded and uploaded to the texture. @@ -1651,6 +1700,18 @@ function createTexture(gl, options, callback) { return tex; } +function createTextureAsync(gl, options) { + return new Promise((resolve, reject) => { + createTexture(gl, options, (err, texture, source) => { + if (err) { + reject(err); + } else { + resolve({ texture, source }); + } + }); + }); +} + /** * Resizes a texture based on the options passed in. * @@ -1835,6 +1896,7 @@ export { setSamplerParameters, createTexture, + createTextureAsync, setEmptyTexture, setTextureFromArray, loadTextureFromUrl, diff --git a/test/tests/texture-tests.js b/test/tests/texture-tests.js index af459c39..7041c63e 100644 --- a/test/tests/texture-tests.js +++ b/test/tests/texture-tests.js @@ -18,9 +18,20 @@ function assertPixelFromTexture(gl, texture, expected) { assertEqual(actual, expected); } +function createRedGreenURL() { + const ctx = document.createElement('canvas').getContext('2d'); + ctx.canvas.width = 1; + ctx.canvas.height = 2; + ctx.fillStyle = '#F00'; + ctx.fillRect(0, 0, 1, 1); + ctx.fillStyle = '#0F0'; + ctx.fillRect(0, 1, 1, 1); + return ctx.canvas.toDataURL(); +} + describe('texture tests', () => { - itWebGL(`test y flips correctly`, () => { + itWebGL(`test y flips correctly`, async() => { const {gl} = createContext(); const red = [255, 0, 0, 255]; const green = [0, 255, 0, 255]; @@ -35,6 +46,12 @@ describe('texture tests', () => { assertPixelFromTexture(gl, t2rg, red); assertPixelFromTexture(gl, t3gr, green); + // Test that the state is saved when async. + const p = twgl.createTextureAsync(gl, createRedGreenURL()); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + const { texture: t4gr } = await p; + assertPixelFromTexture(gl, t4gr, green); + assertNoWebGLError(gl); });