Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

clampToMaxSize function in WebGLTextures may need type check #13454

Closed
2 tasks done
WJsjtu opened this issue Feb 28, 2018 · 16 comments
Closed
2 tasks done

clampToMaxSize function in WebGLTextures may need type check #13454

WJsjtu opened this issue Feb 28, 2018 · 16 comments

Comments

@WJsjtu
Copy link
Contributor

WJsjtu commented Feb 28, 2018

Description of the problem

In uploadTexture function for WebGLTextures

var image = clampToMaxSize( texture.image, capabilities.maxTextureSize );
if ( textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( image ) === false ) {
image = makePowerOfTwo( image );
}

clampToMaxSize is called for any texture on texture.image.
I've figure out the texture type could only be one of CanvasTexture , VideoTexture and DataTexture for uploadTexture.
So the logic problem comes here, clampToMaxSize function given an DataTexture.image ( an object with width, height, data properties ) will cause error on drawImage if the size is larger than capabilities.maxTextureSize.
if ( image.width > maxSize || image.height > maxSize ) {
// Warning: Scaling through the canvas will only work with images that use
// premultiplied alpha.
var scale = maxSize / Math.max( image.width, image.height );
var canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );
canvas.width = Math.floor( image.width * scale );
canvas.height = Math.floor( image.height * scale );
var context = canvas.getContext( '2d' );
context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height );
console.warn( 'THREE.WebGLRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image );

The example for SkinnedMesh in doc will go to clampToMaxSize with DataTexture.image as image argument but its size is not larger than capabilities.maxTextureSize which makes it not trigger the error.
Maybe it is not practical to set extreme large number of bones, but I think logically this should be fixed.

makePowerOfTwo function do this well.

Three.js version
  • Dev
  • r90

Any thoughts?

@Mugen87
Copy link
Collaborator

Mugen87 commented Feb 28, 2018

You mean this error, right?

https://jsfiddle.net/f2Lommf5/2175/

@WJsjtu
Copy link
Contributor Author

WJsjtu commented Feb 28, 2018

Yes, you are right!
I guess such code as (image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || image instanceof ImageBitmap) is needed.

@Mugen87
Copy link
Collaborator

Mugen87 commented Feb 28, 2018

I guess this would fix the issue. We only use canvas for specific image types.

function clampToMaxSize( image, maxSize ) {

	if ( image.width > maxSize || image.height > maxSize ) {

		var scale = maxSize / Math.max( image.width, image.height );

		if ( image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || image instanceof ImageBitmap ) {

			// Warning: Scaling through the canvas will only work with images that use
			// premultiplied alpha.

			var canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );
			canvas.width = Math.max( Math.floor( image.width * scale ), 1 );
			canvas.height = Math.max( Math.floor( image.height * scale ), 1 );

			var context = canvas.getContext( '2d' );
			context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height );

			console.warn( 'THREE.WebGLRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image );

			return canvas;

		} else {

			var width = Math.max( Math.floor( image.width * scale ), 1 );
			var height = Math.max( Math.floor( image.height * scale ), 1 );

			console.warn( 'THREE.WebGLRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + width + 'x' + height, image );

			image.width = width;
			image.height = height;

		}

	}

	return image;

}

@WJsjtu
Copy link
Contributor Author

WJsjtu commented Feb 28, 2018

Does makePowerOfTwo also need to care about DataTexture.image if textureNeedsPowerOfTwo(image) === true ( Is this possible ? ) and isPowerOfTwo(image) === false?

function makePowerOfTwo( image ) {
if ( image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || image instanceof ImageBitmap ) {
if ( _canvas === undefined ) _canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );
_canvas.width = _Math.floorPowerOfTwo( image.width );
_canvas.height = _Math.floorPowerOfTwo( image.height );
var context = _canvas.getContext( '2d' );
context.drawImage( image, 0, 0, _canvas.width, _canvas.height );
console.warn( 'THREE.WebGLRenderer: image is not power of two (' + image.width + 'x' + image.height + '). Resized to ' + _canvas.width + 'x' + _canvas.height, image );
return _canvas;
}
return image;
}

Currently it only deals with HTMLImageElement, HTMLCanvasElement and ImageBitmap.

@Mugen87
Copy link
Collaborator

Mugen87 commented Feb 28, 2018

Maybe. @WestLangley @mrdoob What do you think about this?

@mrdoob
Copy link
Owner

mrdoob commented Mar 1, 2018

So the problem is that clampToMaxSize() and makePowerOfTwo() breaks when passing a DataTexture.image?

@WJsjtu
Copy link
Contributor Author

WJsjtu commented Mar 1, 2018

clampToMaxSize() breaks and makePowerOfTwo() does nothing to DataTexture.image.

@WestLangley
Copy link
Collaborator

POT is only required for mipmapping. DataTextures are typically neither mipmapped nor resized -- right?. So I do not understand what the issue is...

@WJsjtu
Copy link
Contributor Author

WJsjtu commented Mar 1, 2018

@WestLangley Well , thank you for pointing out that the DataTextures do not need power-of-two process. So you can take care of the first three comments of this issue and ignore others.

The issue is about clampToMaxSize function which is called on any texture's image including DataTextures.image without the type check of the image.
When a DataTextures.image with size larger than capabilities.maxTextureSize is passed to clampToMaxSize. clampToMaxSize will call Canvas.drawImage on an JS object ( DataTextures.image ). And this lets the console reporting the error:

Uncaught TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'

@WestLangley
Copy link
Collaborator

I do not think DataTextures should ever be resized.

@WJsjtu
Copy link
Contributor Author

WJsjtu commented Mar 1, 2018

@WestLangley Even the DataTextures are larger than capabilities.maxTextureSize?
This will cause warning in console.

WebGL: INVALID_VALUE: texImage2D: width or height out of range

@WestLangley
Copy link
Collaborator

Resizing a DataTexture would destroy the data. If the DataTexture is too big, then that is a user error.

@Mugen87
Copy link
Collaborator

Mugen87 commented Mar 1, 2018

It's not just a warning, clampToMaxSize() throws a runtime error: https://jsfiddle.net/f2Lommf5/2175/

My suggestion was to add at least a more robust version of clampToMaxSize() that avoids the runtime error and reports a warning instead (image is too big...).

@Mugen87
Copy link
Collaborator

Mugen87 commented Mar 13, 2018

Closing. If more users complain, we can still make clampToMaxSize() more robust. For now let's keep the code as it is.

@Mugen87 Mugen87 closed this as completed Mar 13, 2018
@mrdoob
Copy link
Owner

mrdoob commented Mar 14, 2018

My suggestion was to add at least a more robust version of clampToMaxSize() that avoids the runtime error and reports a warning instead (image is too big...).

That sounds good to me 👍

@Mugen87
Copy link
Collaborator

Mugen87 commented Mar 14, 2018

Okay, i'll make a PR then 😊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants