diff --git a/content/canvas/src/CustomQS_Canvas2D.h b/content/canvas/src/CustomQS_Canvas2D.h index 136fe996b762..e78006a82897 100644 --- a/content/canvas/src/CustomQS_Canvas2D.h +++ b/content/canvas/src/CustomQS_Canvas2D.h @@ -1,6 +1,5 @@ -/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version @@ -13,10 +12,10 @@ * for the specific language governing rights and limitations under the * License. * - * The Original Code is Gecko code. + * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is - * Mozilla Corporation + * Mozilla Corporation. * Portions created by the Initial Developer are Copyright (C) 2010 * the Initial Developer. All Rights Reserved. * @@ -24,8 +23,8 @@ * Vladimir Vukicevic (original author) * * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to @@ -37,7 +36,6 @@ * * ***** END LICENSE BLOCK ***** */ -#include "nsDOMError.h" #include "nsIDOMCanvasRenderingContext2D.h" typedef nsresult (NS_STDCALL nsIDOMCanvasRenderingContext2D::*CanvasStyleSetterType)(const nsAString &, nsISupports *); @@ -144,216 +142,3 @@ nsIDOMCanvasRenderingContext2D_GetFillStyle(JSContext *cx, JSObject *obj, jsval { return Canvas2D_GetStyleHelper(cx, obj, id, vp, &nsIDOMCanvasRenderingContext2D::GetFillStyle_multi); } - -static JSBool -nsIDOMCanvasRenderingContext2D_CreateImageData(JSContext *cx, uintN argc, jsval *vp) -{ - XPC_QS_ASSERT_CONTEXT_OK(cx); - - /* Note: this doesn't need JS_THIS_OBJECT */ - - if (argc < 2) - return xpc_qsThrow(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS); - - jsval *argv = JS_ARGV(cx, vp); - - int32 wi, hi; - if (!JS_ValueToECMAInt32(cx, argv[0], &wi) || - !JS_ValueToECMAInt32(cx, argv[1], &hi)) - return JS_FALSE; - - if (wi <= 0 || hi <= 0) - return xpc_qsThrow(cx, NS_ERROR_DOM_INDEX_SIZE_ERR); - - uint32 w = (uint32) wi; - uint32 h = (uint32) hi; - - /* Sanity check w * h here */ - uint32 len0 = w * h; - if (len0 / w != (uint32) h) - return xpc_qsThrow(cx, NS_ERROR_DOM_INDEX_SIZE_ERR); - - uint32 len = len0 * 4; - if (len / 4 != len0) - return xpc_qsThrow(cx, NS_ERROR_DOM_INDEX_SIZE_ERR); - - // create the fast typed array; it's initialized to 0 by default - JSObject *darray = js_CreateTypedArray(cx, js::TypedArray::TYPE_UINT8_CLAMPED, len); - JSAutoTempValueRooter rd(cx, darray); - if (!darray) - return JS_FALSE; - - // Do JS_NewObject after CreateTypedArray, so that gc will get - // triggered here if necessary - JSObject *result = JS_NewObject(cx, NULL, NULL, NULL); - JSAutoTempValueRooter rr(cx, result); - if (!result) - return JS_FALSE; - - if (!JS_DefineProperty(cx, result, "width", INT_TO_JSVAL(w), NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT) || - !JS_DefineProperty(cx, result, "height", INT_TO_JSVAL(h), NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT) || - !JS_DefineProperty(cx, result, "data", OBJECT_TO_JSVAL(darray), NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) - return JS_FALSE; - - *vp = OBJECT_TO_JSVAL(result); - return JS_TRUE; -} - -static JSBool -nsIDOMCanvasRenderingContext2D_GetImageData(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; - - nsIDOMCanvasRenderingContext2D *self; - xpc_qsSelfRef selfref; - JSAutoTempValueRooter tvr(cx); - if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, tvr.addr(), nsnull)) - return JS_FALSE; - - if (argc < 4) - return xpc_qsThrow(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS); - - jsval *argv = JS_ARGV(cx, vp); - - int32 x, y; - int32 wi, hi; - if (!JS_ValueToECMAInt32(cx, argv[0], &x) || - !JS_ValueToECMAInt32(cx, argv[1], &y) || - !JS_ValueToECMAInt32(cx, argv[2], &wi) || - !JS_ValueToECMAInt32(cx, argv[3], &hi)) - return JS_FALSE; - - if (wi <= 0 || hi <= 0) - return xpc_qsThrow(cx, NS_ERROR_DOM_INDEX_SIZE_ERR); - - uint32 w = (uint32) wi; - uint32 h = (uint32) hi; - - // Sanity check w * h here - uint32 len0 = w * h; - if (len0 / w != (uint32) h) - return xpc_qsThrow(cx, NS_ERROR_DOM_INDEX_SIZE_ERR); - - uint32 len = len0 * 4; - if (len / 4 != len0) - return xpc_qsThrow(cx, NS_ERROR_DOM_INDEX_SIZE_ERR); - - // create the fast typed array - JSObject *darray = js_CreateTypedArray(cx, js::TypedArray::TYPE_UINT8_CLAMPED, len); - JSAutoTempValueRooter rd(cx, darray); - if (!darray) - return JS_FALSE; - - js::TypedArray *tdest = js::TypedArray::fromJSObject(darray); - - // make the call - rv = self->GetImageData_explicit(x, y, w, h, (PRUint8*) tdest->data, tdest->byteLength); - if (NS_FAILED(rv)) - return xpc_qsThrowMethodFailed(cx, rv, vp); - - // Do JS_NewObject after CreateTypedArray, so that gc will get - // triggered here if necessary - JSObject *result = JS_NewObject(cx, NULL, NULL, NULL); - JSAutoTempValueRooter rr(cx, result); - if (!result) - return JS_FALSE; - - if (!JS_DefineProperty(cx, result, "width", INT_TO_JSVAL(w), NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT) || - !JS_DefineProperty(cx, result, "height", INT_TO_JSVAL(h), NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT) || - !JS_DefineProperty(cx, result, "data", OBJECT_TO_JSVAL(darray), NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) - return JS_FALSE; - - *vp = OBJECT_TO_JSVAL(result); - return JS_TRUE; -} - -static JSBool -nsIDOMCanvasRenderingContext2D_PutImageData(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; - - nsIDOMCanvasRenderingContext2D *self; - xpc_qsSelfRef selfref; - JSAutoTempValueRooter tvr(cx); - if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, tvr.addr(), nsnull)) - return JS_FALSE; - - if (argc < 3) - return xpc_qsThrow(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS); - - jsval *argv = JS_ARGV(cx, vp); - - if (JSVAL_IS_PRIMITIVE(argv[0])) - return xpc_qsThrow(cx, NS_ERROR_DOM_TYPE_MISMATCH_ERR); - - JSObject *dataObject = JSVAL_TO_OBJECT(argv[0]); - int32 x, y; - if (!JS_ValueToECMAInt32(cx, argv[1], &x) || - !JS_ValueToECMAInt32(cx, argv[2], &y)) - return JS_FALSE; - - int32 wi, hi; - JSObject *darray; - - // grab width, height, and the dense array from the dataObject - JSAutoTempValueRooter tv(cx); - - if (!JS_GetProperty(cx, dataObject, "width", tv.addr()) || - !JS_ValueToECMAInt32(cx, tv.value(), &wi)) - return JS_FALSE; - - if (!JS_GetProperty(cx, dataObject, "height", tv.addr()) || - !JS_ValueToECMAInt32(cx, tv.value(), &hi)) - return JS_FALSE; - - if (wi <= 0 || hi <= 0) - return xpc_qsThrow(cx, NS_ERROR_DOM_INDEX_SIZE_ERR); - - uint32 w = (uint32) wi; - uint32 h = (uint32) hi; - - if (!JS_GetProperty(cx, dataObject, "data", tv.addr()) || - JSVAL_IS_PRIMITIVE(tv.value())) - return JS_FALSE; - darray = JSVAL_TO_OBJECT(tv.value()); - - JSAutoTempValueRooter tsrc_tvr(cx); - - js::TypedArray *tsrc = NULL; - if (darray->getClass() == &js::TypedArray::fastClasses[js::TypedArray::TYPE_UINT8] || - darray->getClass() == &js::TypedArray::fastClasses[js::TypedArray::TYPE_UINT8_CLAMPED]) - { - tsrc = js::TypedArray::fromJSObject(darray); - } else if (JS_IsArrayObject(cx, darray) || js_IsTypedArray(darray)) { - // ugh, this isn't a uint8 typed array, someone made their own object; convert it to a typed array - JSObject *nobj = js_CreateTypedArrayWithArray(cx, js::TypedArray::TYPE_UINT8, darray); - if (!nobj) - return JS_FALSE; - - *tsrc_tvr.addr() = OBJECT_TO_JSVAL(nobj); - tsrc = js::TypedArray::fromJSObject(nobj); - } else { - // yeah, no. - return xpc_qsThrow(cx, NS_ERROR_DOM_TYPE_MISMATCH_ERR); - } - - // make the call - rv = self->PutImageData_explicit(x, y, w, h, (PRUint8*) tsrc->data, tsrc->byteLength); - if (NS_FAILED(rv)) - return xpc_qsThrowMethodFailed(cx, rv, vp); - - *vp = JSVAL_VOID; - return JS_TRUE; -} diff --git a/content/canvas/src/nsCanvasRenderingContext2D.cpp b/content/canvas/src/nsCanvasRenderingContext2D.cpp index c30f3369778e..d369a6136ecd 100644 --- a/content/canvas/src/nsCanvasRenderingContext2D.cpp +++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp @@ -3426,16 +3426,9 @@ nsCanvasRenderingContext2D::EnsureUnpremultiplyTable() { } +// ImageData getImageData (in float x, in float y, in float width, in float height); NS_IMETHODIMP nsCanvasRenderingContext2D::GetImageData() -{ - /* Should never be called -- GetImageData_explicit is the QS entry point */ - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -nsCanvasRenderingContext2D::GetImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 w, PRUint32 h, - PRUint8 *aData, PRUint32 aDataLen) { if (!mValid) return NS_ERROR_FAILURE; @@ -3445,15 +3438,42 @@ nsCanvasRenderingContext2D::GetImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 return NS_ERROR_DOM_SECURITY_ERR; } + nsAXPCNativeCallContext *ncc = nsnull; + nsresult rv = nsContentUtils::XPConnect()-> + GetCurrentNativeCallContext(&ncc); + NS_ENSURE_SUCCESS(rv, rv); + + if (!ncc) + return NS_ERROR_FAILURE; + + JSContext *ctx = nsnull; + + rv = ncc->GetJSContext(&ctx); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 argc; + jsval *argv = nsnull; + + ncc->GetArgc(&argc); + ncc->GetArgvPtr(&argv); + + JSAutoRequest ar(ctx); + + int32 x, y, w, h; + if (!JS_ConvertArguments (ctx, argc, argv, "jjjj", &x, &y, &w, &h)) + return NS_ERROR_DOM_SYNTAX_ERR; + if (!CanvasUtils::CheckSaneSubrectSize (x, y, w, h, mWidth, mHeight)) return NS_ERROR_DOM_SYNTAX_ERR; - PRUint32 len = w * h * 4; - if (aDataLen != len) - return NS_ERROR_DOM_SYNTAX_ERR; + nsAutoArrayPtr surfaceData (new (std::nothrow) PRUint8[w * h * 4]); + int surfaceDataStride = w*4; + int surfaceDataOffset = 0; - /* Copy the surface contents to the buffer */ - nsRefPtr tmpsurf = new gfxImageSurface(aData, + if (!surfaceData) + return NS_ERROR_OUT_OF_MEMORY; + + nsRefPtr tmpsurf = new gfxImageSurface(surfaceData, gfxIntSize(w, h), w * 4, gfxASurface::ImageFormatARGB32); @@ -3469,39 +3489,99 @@ nsCanvasRenderingContext2D::GetImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 tmpctx->SetSource(mSurface, gfxPoint(-(int)x, -(int)y)); tmpctx->Paint(); - // make sure sUnpremultiplyTable has been created + tmpctx = nsnull; + tmpsurf = nsnull; + + PRUint32 len = w * h * 4; + if (len > (((PRUint32)0xfff00000)/sizeof(jsval))) + return NS_ERROR_INVALID_ARG; + + jsval *dest; + JSObject *dataArray = js_NewArrayObjectWithCapacity(ctx, len, &dest); + if (!dataArray) + return NS_ERROR_OUT_OF_MEMORY; + + nsAutoGCRoot arrayGCRoot(&dataArray, &rv); + NS_ENSURE_SUCCESS(rv, rv); + EnsureUnpremultiplyTable(); - // NOTE! dst is the same as src, and this relies on reading - // from src and advancing that ptr before writing to dst. - PRUint8 *src = aData; - PRUint8 *dst = aData; - + PRUint8 *row; for (int j = 0; j < h; j++) { + row = surfaceData + surfaceDataOffset + (surfaceDataStride * j); for (int i = 0; i < w; i++) { - // XXX Is there some useful swizzle MMX we can use here? #ifdef IS_LITTLE_ENDIAN - PRUint8 b = *src++; - PRUint8 g = *src++; - PRUint8 r = *src++; - PRUint8 a = *src++; + PRUint8 b = *row++; + PRUint8 g = *row++; + PRUint8 r = *row++; + PRUint8 a = *row++; #else - PRUint8 a = *src++; - PRUint8 r = *src++; - PRUint8 g = *src++; - PRUint8 b = *src++; + PRUint8 a = *row++; + PRUint8 r = *row++; + PRUint8 g = *row++; + PRUint8 b = *row++; #endif // Convert to non-premultiplied color - *dst++ = sUnpremultiplyTable[a][r]; - *dst++ = sUnpremultiplyTable[a][g]; - *dst++ = sUnpremultiplyTable[a][b]; - *dst++ = a; + + *dest++ = INT_TO_JSVAL(sUnpremultiplyTable[a][r]); + *dest++ = INT_TO_JSVAL(sUnpremultiplyTable[a][g]); + *dest++ = INT_TO_JSVAL(sUnpremultiplyTable[a][b]); + *dest++ = INT_TO_JSVAL(a); } } + // Allocate result object after array, so if we have to trigger gc + // we do it now. + JSObject *result = JS_NewObject(ctx, NULL, NULL, NULL); + if (!result) + return NS_ERROR_OUT_OF_MEMORY; + + nsAutoGCRoot resultGCRoot(&result, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + if (!JS_DefineProperty(ctx, result, "width", INT_TO_JSVAL(w), NULL, NULL, 0) || + !JS_DefineProperty(ctx, result, "height", INT_TO_JSVAL(h), NULL, NULL, 0) || + !JS_DefineProperty(ctx, result, "data", OBJECT_TO_JSVAL(dataArray), NULL, NULL, 0)) + return NS_ERROR_FAILURE; + + jsval *retvalPtr; + ncc->GetRetValPtr(&retvalPtr); + *retvalPtr = OBJECT_TO_JSVAL(result); + ncc->SetReturnValueWasSet(PR_TRUE); + return NS_OK; } +static inline PRUint8 ToUint8(jsint aInput) +{ + if (PRUint32(aInput) > 255) + return (aInput < 0) ? 0 : 255; + return PRUint8(aInput); +} + +static inline PRUint8 ToUint8(double aInput) +{ + if (!(aInput >= 0)) /* Not < so that NaN coerces to 0 */ + return 0; + if (aInput > 255) + return 255; + double toTruncate = aInput + 0.5; + PRUint8 retval = PRUint8(toTruncate); + + // now retval is rounded to nearest, ties rounded up. We want + // rounded to nearest ties to even, so check whether we had a tie. + if (retval == toTruncate) { + // It was a tie (since adding 0.5 gave us the exact integer we want). + // Since we rounded up, we either already have an even number or we + // have an odd number but the number we want is one less. So just + // unconditionally masking out the ones bit should do the trick to get + // us the value we want. + return (retval & ~1); + } + + return retval; +} + void nsCanvasRenderingContext2D::EnsurePremultiplyTable() { if (sPremultiplyTable) @@ -3524,60 +3604,163 @@ nsCanvasRenderingContext2D::EnsurePremultiplyTable() { // void putImageData (in ImageData d, in float x, in float y); NS_IMETHODIMP nsCanvasRenderingContext2D::PutImageData() -{ - /* Should never be called -- PutImageData_explicit is the QS entry point */ - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -nsCanvasRenderingContext2D::PutImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 w, PRUint32 h, - unsigned char *aData, PRUint32 aDataLen) { nsresult rv; if (!mValid) return NS_ERROR_FAILURE; + nsAXPCNativeCallContext *ncc = nsnull; + rv = nsContentUtils::XPConnect()-> + GetCurrentNativeCallContext(&ncc); + NS_ENSURE_SUCCESS(rv, rv); + + if (!ncc) + return NS_ERROR_FAILURE; + + JSContext *ctx = nsnull; + + rv = ncc->GetJSContext(&ctx); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 argc; + jsval *argv = nsnull; + + ncc->GetArgc(&argc); + ncc->GetArgvPtr(&argv); + + JSAutoRequest ar(ctx); + + JSObject *dataObject; + int32 x, y; + + if (!JS_ConvertArguments (ctx, argc, argv, "ojj", &dataObject, &x, &y)) + return NS_ERROR_DOM_SYNTAX_ERR; + + if (!dataObject) + return NS_ERROR_DOM_SYNTAX_ERR; + + int32 w, h; + JSObject *dataArray; + jsval v; + + if (!JS_GetProperty(ctx, dataObject, "width", &v) || + !JS_ValueToInt32(ctx, v, &w)) + return NS_ERROR_DOM_SYNTAX_ERR; + + if (!JS_GetProperty(ctx, dataObject, "height", &v) || + !JS_ValueToInt32(ctx, v, &h)) + return NS_ERROR_DOM_SYNTAX_ERR; + + if (!JS_GetProperty(ctx, dataObject, "data", &v) || + !JSVAL_IS_OBJECT(v)) + return NS_ERROR_DOM_SYNTAX_ERR; + dataArray = JSVAL_TO_OBJECT(v); + if (!CanvasUtils::CheckSaneSubrectSize (x, y, w, h, mWidth, mHeight)) return NS_ERROR_DOM_SYNTAX_ERR; - PRUint32 len = w * h * 4; - if (aDataLen != len) + jsuint arrayLen; + if (!JS_IsArrayObject(ctx, dataArray) || + !JS_GetArrayLength(ctx, dataArray, &arrayLen) || + arrayLen < (jsuint)(w * h * 4)) return NS_ERROR_DOM_SYNTAX_ERR; - nsRefPtr imgsurf = new gfxImageSurface(gfxIntSize(w, h), + nsAutoArrayPtr imageBuffer(new (std::nothrow) PRUint8[w * h * 4]); + if (!imageBuffer) + return NS_ERROR_OUT_OF_MEMORY; + + PRUint8 *imgPtr = imageBuffer.get(); + + EnsurePremultiplyTable(); + + JSBool canFastPath = + js_CoerceArrayToCanvasImageData(dataArray, 0, w*h*4, imageBuffer); + + // no fast path? go slow. We sadly need this for now, instead of just + // throwing, because dataArray might not be dense in case someone stuck + // their own array on the imageData. + // FIXME: it'd be awfully nice if we could prevent such modification of + // imageData objects, since it's likely the spec won't allow it anyway. + // Bug 497110 covers this. + if (!canFastPath) { + jsval vr, vg, vb, va; + PRUint8 ir, ig, ib, ia; + for (int32 j = 0; j < h; j++) { + int32 lineOffset = (j*w*4); + for (int32 i = 0; i < w; i++) { + int32 pixelOffset = lineOffset + i*4; + if (!JS_GetElement(ctx, dataArray, pixelOffset + 0, &vr) || + !JS_GetElement(ctx, dataArray, pixelOffset + 1, &vg) || + !JS_GetElement(ctx, dataArray, pixelOffset + 2, &vb) || + !JS_GetElement(ctx, dataArray, pixelOffset + 3, &va)) + return NS_ERROR_DOM_SYNTAX_ERR; + + if (JSVAL_IS_INT(vr)) ir = ToUint8(JSVAL_TO_INT(vr)); + else if (JSVAL_IS_DOUBLE(vr)) ir = ToUint8(*JSVAL_TO_DOUBLE(vr)); + else return NS_ERROR_DOM_SYNTAX_ERR; + + if (JSVAL_IS_INT(vg)) ig = ToUint8(JSVAL_TO_INT(vg)); + else if (JSVAL_IS_DOUBLE(vg)) ig = ToUint8(*JSVAL_TO_DOUBLE(vg)); + else return NS_ERROR_DOM_SYNTAX_ERR; + + if (JSVAL_IS_INT(vb)) ib = ToUint8(JSVAL_TO_INT(vb)); + else if (JSVAL_IS_DOUBLE(vb)) ib = ToUint8(*JSVAL_TO_DOUBLE(vb)); + else return NS_ERROR_DOM_SYNTAX_ERR; + + if (JSVAL_IS_INT(va)) ia = ToUint8(JSVAL_TO_INT(va)); + else if (JSVAL_IS_DOUBLE(va)) ia = ToUint8(*JSVAL_TO_DOUBLE(va)); + else return NS_ERROR_DOM_SYNTAX_ERR; + + // Convert to premultiplied color (losslessly if the input came from getImageData) + ir = sPremultiplyTable[ia][ir]; + ig = sPremultiplyTable[ia][ig]; + ib = sPremultiplyTable[ia][ib]; + +#ifdef IS_LITTLE_ENDIAN + *imgPtr++ = ib; + *imgPtr++ = ig; + *imgPtr++ = ir; + *imgPtr++ = ia; +#else + *imgPtr++ = ia; + *imgPtr++ = ir; + *imgPtr++ = ig; + *imgPtr++ = ib; +#endif + } + } + } else { + /* Walk through and premultiply and swap rgba */ + PRUint8 ir, ig, ib, ia; + PRUint8 *ptr = imgPtr; + for (int32 i = 0; i < w*h; i++) { + ir = ptr[0]; + ig = ptr[1]; + ib = ptr[2]; + ia = ptr[3]; + +#ifdef IS_LITTLE_ENDIAN + ptr[0] = sPremultiplyTable[ia][ib]; + ptr[1] = sPremultiplyTable[ia][ig]; + ptr[2] = sPremultiplyTable[ia][ir]; +#else + ptr[0] = ia; + ptr[1] = sPremultiplyTable[ia][ir]; + ptr[2] = sPremultiplyTable[ia][ig]; + ptr[3] = sPremultiplyTable[ia][ib]; +#endif + ptr += 4; + } + } + + nsRefPtr imgsurf = new gfxImageSurface(imageBuffer.get(), + gfxIntSize(w, h), + w * 4, gfxASurface::ImageFormatARGB32); if (!imgsurf || imgsurf->CairoStatus()) return NS_ERROR_FAILURE; - // ensure premultiply table has been created - EnsurePremultiplyTable(); - - PRUint8 *src = aData; - PRUint8 *dst = imgsurf->Data(); - - for (int j = 0; j < h; j++) { - for (int i = 0; i < w; i++) { - PRUint8 r = *src++; - PRUint8 g = *src++; - PRUint8 b = *src++; - PRUint8 a = *src++; - - // Convert to premultiplied color (losslessly if the input came from getImageData) -#ifdef IS_LITTLE_ENDIAN - *dst++ = sPremultiplyTable[a][b]; - *dst++ = sPremultiplyTable[a][g]; - *dst++ = sPremultiplyTable[a][r]; - *dst++ = a; -#else - *dst++ = a; - *dst++ = sPremultiplyTable[a][r]; - *dst++ = sPremultiplyTable[a][g]; - *dst++ = sPremultiplyTable[a][b]; -#endif - } - } - gfxContextPathAutoSaveRestore pathSR(mThebes); gfxContextAutoSaveRestore autoSR(mThebes); @@ -3592,7 +3775,7 @@ nsCanvasRenderingContext2D::PutImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 mThebes->SetOperator(gfxContext::OPERATOR_SOURCE); mThebes->Fill(); - return Redraw(gfxRect(x, y, w, h)); + return Redraw(); } NS_IMETHODIMP @@ -3612,8 +3795,79 @@ nsCanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface) NS_IMETHODIMP nsCanvasRenderingContext2D::CreateImageData() { - /* Should never be called; handled entirely in the quickstub */ - return NS_ERROR_NOT_IMPLEMENTED; + if (!mValid) + return NS_ERROR_FAILURE; + + nsAXPCNativeCallContext *ncc = nsnull; + nsresult rv = nsContentUtils::XPConnect()-> + GetCurrentNativeCallContext(&ncc); + NS_ENSURE_SUCCESS(rv, rv); + + if (!ncc) + return NS_ERROR_FAILURE; + + JSContext *ctx = nsnull; + + rv = ncc->GetJSContext(&ctx); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 argc; + jsval *argv = nsnull; + + ncc->GetArgc(&argc); + ncc->GetArgvPtr(&argv); + + JSAutoRequest ar(ctx); + + int32 width, height; + if (!JS_ConvertArguments (ctx, argc, argv, "jj", &width, &height)) + return NS_ERROR_DOM_SYNTAX_ERR; + + if (width <= 0 || height <= 0) + return NS_ERROR_DOM_INDEX_SIZE_ERR; + + PRUint32 w = (PRUint32) width; + PRUint32 h = (PRUint32) height; + + // check for overflow when calculating len + PRUint32 len0 = w * h; + if (len0 / w != (PRUint32) h) + return NS_ERROR_DOM_INDEX_SIZE_ERR; + PRUint32 len = len0 * 4; + if (len / 4 != len0) + return NS_ERROR_DOM_INDEX_SIZE_ERR; + + jsval *dest; + JSObject *dataArray = js_NewArrayObjectWithCapacity(ctx, len, &dest); + if (!dataArray) + return NS_ERROR_OUT_OF_MEMORY; + + nsAutoGCRoot arrayGCRoot(&dataArray, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + for (PRUint32 i = 0; i < len; i++) + *dest++ = JSVAL_ZERO; + + // Allocate result object after array, so if we have to trigger gc + // we do it now. + JSObject *result = JS_NewObject(ctx, NULL, NULL, NULL); + if (!result) + return NS_ERROR_OUT_OF_MEMORY; + + nsAutoGCRoot resultGCRoot(&result, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + if (!JS_DefineProperty(ctx, result, "width", INT_TO_JSVAL(w), NULL, NULL, 0) || + !JS_DefineProperty(ctx, result, "height", INT_TO_JSVAL(h), NULL, NULL, 0) || + !JS_DefineProperty(ctx, result, "data", OBJECT_TO_JSVAL(dataArray), NULL, NULL, 0)) + return NS_ERROR_FAILURE; + + jsval *retvalPtr; + ncc->GetRetValPtr(&retvalPtr); + *retvalPtr = OBJECT_TO_JSVAL(result); + ncc->SetReturnValueWasSet(PR_TRUE); + + return NS_OK; } NS_IMETHODIMP diff --git a/dom/interfaces/canvas/nsIDOMCanvasRenderingContext2D.idl b/dom/interfaces/canvas/nsIDOMCanvasRenderingContext2D.idl index d59a081b71dc..597651ebc84d 100644 --- a/dom/interfaces/canvas/nsIDOMCanvasRenderingContext2D.idl +++ b/dom/interfaces/canvas/nsIDOMCanvasRenderingContext2D.idl @@ -182,22 +182,10 @@ enum CanvasMultiGetterType { // void putImageData (in ImageData d, in float x, in float y); // ImageData = { width: #, height: #, data: [r, g, b, a, ...] } - // These are just dummy functions; for JS, they are implemented as quickstubs - // that call the _explicit methods below. Native callers should use the _explit - // methods directly. void getImageData(); void putImageData(); - // dataLen must be == width*height*4 in both of these calls - [noscript] void getImageData_explicit(in long x, in long y, in unsigned long width, in unsigned long height, - [array, size_is(dataLen)] in octet dataPtr, in unsigned long dataLen); - [noscript] void putImageData_explicit(in long x, in long y, in unsigned long width, in unsigned long height, - [array, size_is(dataLen)] in octet dataPtr, in unsigned long dataLen); - // ImageData createImageData(in float w, in float h); - // Note: this is basically script-only (and really, quickstub-only). Native callers - // should just use the noscript 'explicit' get/put methods above, instead of using - // a separate ImageData object. void createImageData(); // image smoothing mode -- if disabled, images won't be smoothed diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 2b758f8aa41f..096222b843e6 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -12089,7 +12089,6 @@ TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex) case js::TypedArray::TYPE_UINT8_CLAMPED: addr_ins = lir->ins2(LIR_piadd, data_ins, pidx_ins); lir->insStore(LIR_stb, lir->insCall(&js_TypedArray_uint8_clamp_double_ci, &v_ins), addr_ins, 0); - break; default: JS_NOT_REACHED("Unknown typed array type in tracer"); } diff --git a/js/src/jstypedarray.cpp b/js/src/jstypedarray.cpp index a2b2efdbb30b..7d250537070a 100644 --- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -520,18 +520,9 @@ class TypedArrayTemplate jsuint index; // We can't just chain to js_SetProperty, because we're not a normal object. if (!tarray->isArrayIndex(cx, id, &index)) { -#if 0 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_INDEX); return false; -#endif - // Silent ignore is better than an exception here, because - // at some point we may want to support other properties on - // these objects. This is especially true when these arrays - // are used to implement HTML Canvas 2D's PixelArray objects, - // which used to be plain old arrays. - *vp = JSVAL_VOID; - return true; } if (JSVAL_IS_INT(*vp)) { diff --git a/js/src/xpconnect/src/dom_quickstubs.qsconf b/js/src/xpconnect/src/dom_quickstubs.qsconf index 2da054216421..4d23b2f7fe4a 100644 --- a/js/src/xpconnect/src/dom_quickstubs.qsconf +++ b/js/src/xpconnect/src/dom_quickstubs.qsconf @@ -92,6 +92,11 @@ members = [ 'nsIDOMTextMetrics.*', 'nsIDOMCanvasGradient.*', 'nsIDOMCanvasPattern.*', + # NOTE: createImageDate(), getImageData(), and putImageData() use + # GetCurrentNativeCallContext + '-nsIDOMCanvasRenderingContext2D.createImageData', + '-nsIDOMCanvasRenderingContext2D.getImageData', + '-nsIDOMCanvasRenderingContext2D.putImageData', # dom/interfaces/core 'nsIDOMCharacterData.data', @@ -620,10 +625,6 @@ customMethodCalls = { 'nsICanvasRenderingContextWebGL_VertexAttrib1fv': CUSTOM_QS, 'nsICanvasRenderingContextWebGL_VertexAttrib2fv': CUSTOM_QS, 'nsICanvasRenderingContextWebGL_VertexAttrib3fv': CUSTOM_QS, - 'nsICanvasRenderingContextWebGL_VertexAttrib4fv': CUSTOM_QS, - # Canvas 2D - 'nsIDOMCanvasRenderingContext2D_CreateImageData': CUSTOM_QS, - 'nsIDOMCanvasRenderingContext2D_GetImageData': CUSTOM_QS, - 'nsIDOMCanvasRenderingContext2D_PutImageData': CUSTOM_QS, + 'nsICanvasRenderingContextWebGL_VertexAttrib4fv': CUSTOM_QS }