mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-14 13:55:43 +00:00
b=571029; readPixels API change, support both old and new signatures, also fix 572797 (allow non-fitting rectangles), and check that sizes are nonnegative; r=vladimir
This commit is contained in:
parent
54306dde5e
commit
daa03541ae
@ -209,6 +209,101 @@ nsICanvasRenderingContextWebGL_BufferSubData(JSContext *cx, uintN argc, jsval *v
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* ReadPixels takes:
|
||||
* TexImage2D(int, int, int, int, uint, uint, ArrayBufferView)
|
||||
*/
|
||||
static JSBool
|
||||
nsICanvasRenderingContextWebGL_ReadPixels(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
XPC_QS_ASSERT_CONTEXT_OK(cx);
|
||||
JSObject *obj = JS_THIS_OBJECT(cx, vp);
|
||||
if (!obj)
|
||||
return JS_FALSE;
|
||||
|
||||
nsresult rv;
|
||||
|
||||
nsICanvasRenderingContextWebGL *self;
|
||||
xpc_qsSelfRef selfref;
|
||||
js::AutoValueRooter tvr(cx);
|
||||
if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, tvr.addr(), nsnull))
|
||||
return JS_FALSE;
|
||||
|
||||
// XXX we currently allow passing only 6 args to support the API. Eventually drop that.
|
||||
if (argc < 6)
|
||||
return xpc_qsThrow(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
|
||||
|
||||
jsval *argv = JS_ARGV(cx, vp);
|
||||
|
||||
// arguments common to all cases
|
||||
GET_INT32_ARG(argv0, 0);
|
||||
GET_INT32_ARG(argv1, 1);
|
||||
GET_INT32_ARG(argv2, 2);
|
||||
GET_INT32_ARG(argv3, 3);
|
||||
GET_UINT32_ARG(argv4, 4);
|
||||
GET_UINT32_ARG(argv5, 5);
|
||||
|
||||
if (argc == 6) {
|
||||
/*** BEGIN old API deprecated code. Eventually drop that. ***/
|
||||
// the code here is ugly, but temporary. It comes from the old ReadPixels implementation.
|
||||
// Remove it as soon as it's OK to drop the old API.
|
||||
|
||||
PRInt32 byteLength;
|
||||
rv = self->ReadPixels_byteLength_old_API_deprecated(argv2, argv3, argv4, argv5, &byteLength);
|
||||
if (NS_FAILED(rv)) {
|
||||
xpc_qsThrow(cx, NS_ERROR_FAILURE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
JSObject *abufObject = js_CreateArrayBuffer(cx, byteLength);
|
||||
if (!abufObject) {
|
||||
xpc_qsThrow(cx, NS_ERROR_FAILURE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
js::ArrayBuffer *abuf = js::ArrayBuffer::fromJSObject(abufObject);
|
||||
|
||||
rv = self->ReadPixels_buf(
|
||||
argv0, argv1, argv2, argv3, argv4, argv5, abuf);
|
||||
if (NS_FAILED(rv)) {
|
||||
xpc_qsThrow(cx, NS_ERROR_FAILURE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
JSObject *retval = js_CreateTypedArrayWithBuffer(cx, js::TypedArray::TYPE_UINT8,
|
||||
abufObject, 0, byteLength);
|
||||
|
||||
*vp = OBJECT_TO_JSVAL(retval);
|
||||
return JS_TRUE; // return here to be unaffected by the *vp = JSVAL_VOID; below
|
||||
|
||||
/*** END old API deprecated code ***/
|
||||
} else if ( argc == 7
|
||||
&& JSVAL_IS_OBJECT(argv[6])
|
||||
&& !JSVAL_IS_PRIMITIVE(argv[6]))
|
||||
{
|
||||
JSObject *argv6 = JSVAL_TO_OBJECT(argv[6]);
|
||||
if (js_IsArrayBuffer(argv6)) {
|
||||
rv = self->ReadPixels_buf(argv0, argv1, argv2, argv3,
|
||||
argv4, argv5, js::ArrayBuffer::fromJSObject(argv6));
|
||||
} else if (js_IsTypedArray(argv6)) {
|
||||
rv = self->ReadPixels_array(argv0, argv1, argv2, argv3,
|
||||
argv4, argv5,
|
||||
js::TypedArray::fromJSObject(argv6));
|
||||
} else {
|
||||
xpc_qsThrowBadArg(cx, NS_ERROR_FAILURE, vp, 6);
|
||||
return JS_FALSE;
|
||||
}
|
||||
} else {
|
||||
xpc_qsThrow(cx, NS_ERROR_FAILURE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return xpc_qsThrowMethodFailed(cx, rv, vp);
|
||||
|
||||
*vp = JSVAL_VOID;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* TexImage2D takes:
|
||||
* TexImage2D(uint, int, uint, int, int, int, uint, uint, ArrayBufferView)\
|
||||
|
@ -332,6 +332,8 @@ protected:
|
||||
WebGLsizei width, WebGLsizei height,
|
||||
WebGLenum format, WebGLenum type,
|
||||
void *pixels, PRUint32 byteLength);
|
||||
nsresult ReadPixels_base(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height,
|
||||
WebGLenum format, WebGLenum type, void *data, PRUint32 byteLength);
|
||||
|
||||
nsresult DOMElementToImageSurface(nsIDOMElement *imageOrCanvas,
|
||||
gfxImageSurface **imageOut,
|
||||
|
@ -2088,21 +2088,25 @@ WebGLContext::PixelStorei(WebGLenum pname, WebGLint param)
|
||||
GL_SAME_METHOD_2(PolygonOffset, PolygonOffset, float, float)
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebGLContext::ReadPixels(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height, WebGLenum format, WebGLenum type)
|
||||
WebGLContext::ReadPixels(PRInt32 dummy)
|
||||
{
|
||||
NativeJSContext js;
|
||||
if (NS_FAILED(js.error))
|
||||
return js.error;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebGLContext::ReadPixels_base(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height,
|
||||
WebGLenum format, WebGLenum type, void *data, PRUint32 byteLength)
|
||||
{
|
||||
if (HTMLCanvasElement()->IsWriteOnly() && !nsContentUtils::IsCallerTrustedForRead()) {
|
||||
LogMessage("ReadPixels: Not allowed");
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
if (width < 0 || height < 0)
|
||||
return ErrorInvalidValue("ReadPixels: negative size passed");
|
||||
|
||||
WebGLsizei boundWidth = mBoundFramebuffer ? mBoundFramebuffer->width() : mWidth;
|
||||
WebGLsizei boundHeight = mBoundFramebuffer ? mBoundFramebuffer->height() : mHeight;
|
||||
if (!CanvasUtils::CheckSaneSubrectSize(x, y, width, height, boundWidth, boundHeight))
|
||||
return ErrorInvalidOperation("ReadPixels: invalid dimensions (outside of framebuffer)");
|
||||
|
||||
PRUint32 size = 0;
|
||||
switch (format) {
|
||||
@ -2141,18 +2145,129 @@ WebGLContext::ReadPixels(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei he
|
||||
PRUint32 alignedRowSize = (plainRowSize + packAlignment-1) &
|
||||
~PRUint32(packAlignment-1);
|
||||
|
||||
PRUint32 len = (height-1)*alignedRowSize + plainRowSize;
|
||||
PRUint32 neededByteLength = (height-1)*alignedRowSize + plainRowSize;
|
||||
|
||||
JSObject *abufObject = js_CreateArrayBuffer(js.ctx, len);
|
||||
if (!abufObject)
|
||||
return SynthesizeGLError(LOCAL_GL_OUT_OF_MEMORY, "readPixels: could not allocate buffer");
|
||||
if(neededByteLength > byteLength)
|
||||
return ErrorInvalidOperation("ReadPixels: buffer too small");
|
||||
|
||||
js::ArrayBuffer *abuf = js::ArrayBuffer::fromJSObject(abufObject);
|
||||
if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, boundWidth, boundHeight)) {
|
||||
// the easy case: we're not reading out-of-range pixels
|
||||
gl->fReadPixels(x, y, width, height, format, type, data);
|
||||
} else {
|
||||
// the rectangle doesn't fit entirely in the bound buffer. We then have to set to zero the part
|
||||
// of the buffer that correspond to out-of-range pixels. We don't want to rely on system OpenGL
|
||||
// to do that for us, because passing out of range parameters to a buggy OpenGL implementation
|
||||
// could conceivably allow to read memory we shouldn't be allowed to read. So we manually initialize
|
||||
// the buffer to zero and compute the parameters to pass to OpenGL. We have to use an intermediate buffer
|
||||
// to accomodate the potentially different strides (widths).
|
||||
|
||||
gl->fReadPixels((GLint) x, (GLint) y, width, height, format, type, (GLvoid *) abuf->data);
|
||||
// zero the whole destination buffer. Too bad for the part that's going to be overwritten, we're not
|
||||
// 100% efficient here, but in practice this is a quite rare case anyway.
|
||||
memset(data, 0, byteLength);
|
||||
|
||||
if ( x >= boundWidth
|
||||
|| x+width <= 0
|
||||
|| y >= boundHeight
|
||||
|| y+height <= 0)
|
||||
{
|
||||
// we are completely outside of range, can exit now with buffer filled with zeros
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// compute the parameters of the subrect we're actually going to call glReadPixels on
|
||||
GLint subrect_x = PR_MAX(x, 0);
|
||||
GLint subrect_end_x = PR_MIN(x+width, boundWidth);
|
||||
GLsizei subrect_width = subrect_end_x - subrect_x;
|
||||
|
||||
GLint subrect_y = PR_MAX(y, 0);
|
||||
GLint subrect_end_y = PR_MIN(y+height, boundHeight);
|
||||
GLsizei subrect_height = subrect_end_y - subrect_y;
|
||||
|
||||
// now, same computation as above to find the size of the intermediate buffer to allocate for the subrect
|
||||
PRUint32 subrect_plainRowSize = subrect_width * size;
|
||||
PRUint32 subrect_alignedRowSize = (subrect_plainRowSize + packAlignment-1) &
|
||||
~PRUint32(packAlignment-1);
|
||||
PRUint32 subrect_byteLength = (subrect_height-1)*subrect_alignedRowSize + subrect_plainRowSize;
|
||||
|
||||
// create subrect buffer, call glReadPixels, copy pixels into destination buffer, delete subrect buffer
|
||||
GLubyte *subrect_data = new GLubyte[subrect_byteLength];
|
||||
gl->fReadPixels(subrect_x, subrect_y, subrect_width, subrect_height, format, type, subrect_data);
|
||||
for (GLint y_inside_subrect = 0; y_inside_subrect < subrect_height; ++y_inside_subrect) {
|
||||
GLint subrect_x_in_dest_buffer = subrect_x - x;
|
||||
GLint subrect_y_in_dest_buffer = subrect_y - y;
|
||||
memcpy(static_cast<GLubyte*>(data)
|
||||
+ alignedRowSize * (subrect_y_in_dest_buffer + y_inside_subrect)
|
||||
+ size * subrect_x_in_dest_buffer, // destination
|
||||
subrect_data + subrect_alignedRowSize * y_inside_subrect, // source
|
||||
subrect_plainRowSize); // size
|
||||
}
|
||||
delete [] subrect_data;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebGLContext::ReadPixels_array(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height,
|
||||
WebGLenum format, WebGLenum type, js::TypedArray *pixels)
|
||||
{
|
||||
return ReadPixels_base(x, y, width, height, format, type,
|
||||
pixels ? pixels->data : 0,
|
||||
pixels ? pixels->byteLength : 0);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebGLContext::ReadPixels_buf(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height,
|
||||
WebGLenum format, WebGLenum type, js::ArrayBuffer *pixels)
|
||||
{
|
||||
return ReadPixels_base(x, y, width, height, format, type,
|
||||
pixels ? pixels->data : 0,
|
||||
pixels ? pixels->byteLength : 0);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebGLContext::ReadPixels_byteLength_old_API_deprecated(WebGLsizei width, WebGLsizei height,
|
||||
WebGLenum format, WebGLenum type, WebGLsizei *retval)
|
||||
{
|
||||
*retval = 0;
|
||||
if (width < 0 || height < 0)
|
||||
return ErrorInvalidValue("ReadPixels: negative size passed");
|
||||
|
||||
PRUint32 size = 0;
|
||||
switch (format) {
|
||||
case LOCAL_GL_ALPHA:
|
||||
size = 1;
|
||||
break;
|
||||
case LOCAL_GL_RGB:
|
||||
size = 3;
|
||||
break;
|
||||
case LOCAL_GL_RGBA:
|
||||
size = 4;
|
||||
break;
|
||||
default:
|
||||
return ErrorInvalidEnum("ReadPixels: unsupported pixel format");
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
// case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
|
||||
// case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
|
||||
// case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
|
||||
case LOCAL_GL_UNSIGNED_BYTE:
|
||||
break;
|
||||
default:
|
||||
return ErrorInvalidEnum("ReadPixels: unsupported pixel type");
|
||||
}
|
||||
PRUint32 packAlignment;
|
||||
gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, (GLint*) &packAlignment);
|
||||
|
||||
PRUint32 plainRowSize = width*size;
|
||||
|
||||
// alignedRowSize = row size rounded up to next multiple of
|
||||
// packAlignment which is a power of 2
|
||||
PRUint32 alignedRowSize = (plainRowSize + packAlignment-1) &
|
||||
~PRUint32(packAlignment-1);
|
||||
|
||||
*retval = (height-1)*alignedRowSize + plainRowSize;
|
||||
|
||||
JSObject *retval = js_CreateTypedArrayWithBuffer(js.ctx, js::TypedArray::TYPE_UINT8, abufObject, 0, len);
|
||||
js.SetRetVal(retval);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -2874,14 +2989,6 @@ NS_IMETHODIMP
|
||||
WebGLContext::TexImage2D_dom_old_API_deprecated(WebGLenum target, WebGLint level, nsIDOMElement *elt,
|
||||
PRBool flipY, PRBool premultiplyAlpha)
|
||||
{
|
||||
static PRBool firsttime = PR_TRUE;
|
||||
|
||||
if (firsttime) {
|
||||
LogMessage("The WebGL spec changed, TexImage2D is now taking at least 6 parameters, please "
|
||||
"adapt your JavaScript code as support for the old API will soon be dropped!");
|
||||
firsttime = PR_FALSE;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxImageSurface> isurf;
|
||||
|
||||
nsresult rv = DOMElementToImageSurface(elt, getter_AddRefs(isurf),
|
||||
|
@ -721,9 +721,13 @@ interface nsICanvasRenderingContextWebGL : nsISupports
|
||||
void pixelStorei(in WebGLenum pname, in WebGLint param);
|
||||
void polygonOffset(in WebGLfloat factor, in WebGLfloat units);
|
||||
|
||||
// TBD
|
||||
//ZZ void glReadPixels(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height, WebGLenum format, WebGLenum type, void* pixels);
|
||||
void readPixels(in WebGLint x, in WebGLint y, in WebGLsizei width, in WebGLsizei height, in WebGLenum format, in WebGLenum type);
|
||||
void readPixels([optional] in long dummy);
|
||||
[noscript] void readPixels_array(in WebGLint x, in WebGLint y, in WebGLsizei width, in WebGLsizei height,
|
||||
in WebGLenum format, in WebGLenum type, in WebGLArrayPtr pixels);
|
||||
[noscript] void readPixels_buf(in WebGLint x, in WebGLint y, in WebGLsizei width, in WebGLsizei height,
|
||||
in WebGLenum format, in WebGLenum type, in WebGLArrayBufferPtr pixels);
|
||||
[noscript] WebGLsizei readPixels_byteLength_old_API_deprecated(
|
||||
in WebGLsizei width, in WebGLsizei height, in WebGLenum format, in WebGLenum type);
|
||||
|
||||
//void glReleaseShaderCompiler();
|
||||
|
||||
|
@ -457,7 +457,6 @@ members = [
|
||||
'-nsICanvasRenderingContextWebGL.texParameteri',
|
||||
'-nsICanvasRenderingContextWebGL.getUniform',
|
||||
'-nsICanvasRenderingContextWebGL.getVertexAttrib',
|
||||
'-nsICanvasRenderingContextWebGL.readPixels',
|
||||
'-nsICanvasRenderingContextWebGL.getShaderParameter',
|
||||
]
|
||||
|
||||
@ -813,6 +812,7 @@ customMethodCalls = {
|
||||
# WebGL
|
||||
'nsICanvasRenderingContextWebGL_BufferData': CUSTOM_QS,
|
||||
'nsICanvasRenderingContextWebGL_BufferSubData': CUSTOM_QS,
|
||||
'nsICanvasRenderingContextWebGL_ReadPixels': CUSTOM_QS,
|
||||
'nsICanvasRenderingContextWebGL_TexImage2D': CUSTOM_QS,
|
||||
'nsICanvasRenderingContextWebGL_TexSubImage2D': CUSTOM_QS,
|
||||
'nsICanvasRenderingContextWebGL_Uniform1iv': CUSTOM_QS_TN,
|
||||
|
Loading…
Reference in New Issue
Block a user