Skip to content

Commit

Permalink
Handle the case of async loading and the user changing
Browse files Browse the repository at this point in the history
the pack state.

Before

    const t = twgl.createTexture({src: 'https://some/img.jpg'});
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
    // t is flipped

After

    const t = twgl.createTexture({src: 'https://some/img.jpg'});
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
    // t is not flipped
  • Loading branch information
greggman committed Sep 8, 2024
1 parent eaa4b13 commit 46355ae
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 5 deletions.
70 changes: 66 additions & 4 deletions src/textures.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -1835,6 +1896,7 @@ export {
setSamplerParameters,

createTexture,
createTextureAsync,
setEmptyTexture,
setTextureFromArray,
loadTextureFromUrl,
Expand Down
19 changes: 18 additions & 1 deletion test/tests/texture-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand All @@ -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);
});

Expand Down

0 comments on commit 46355ae

Please sign in to comment.