diff --git a/content/canvas/src/CustomQS_Canvas2D.h b/content/canvas/src/CustomQS_Canvas2D.h index e78006a82897..136fe996b762 100644 --- a/content/canvas/src/CustomQS_Canvas2D.h +++ b/content/canvas/src/CustomQS_Canvas2D.h @@ -1,5 +1,6 @@ -/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** +/* -*- Mode: C++; tab-width: 40; 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 @@ -12,10 +13,10 @@ * for the specific language governing rights and limitations under the * License. * - * The Original Code is mozilla.org code. + * The Original Code is Gecko 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. * @@ -23,8 +24,8 @@ * Vladimir Vukicevic (original author) * * Alternatively, the contents of this file may be used under the terms of - * 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"), + * 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"), * 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 @@ -36,6 +37,7 @@ * * ***** END LICENSE BLOCK ***** */ +#include "nsDOMError.h" #include "nsIDOMCanvasRenderingContext2D.h" typedef nsresult (NS_STDCALL nsIDOMCanvasRenderingContext2D::*CanvasStyleSetterType)(const nsAString &, nsISupports *); @@ -142,3 +144,216 @@ 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 d369a6136ecd..c30f3369778e 100644 --- a/content/canvas/src/nsCanvasRenderingContext2D.cpp +++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp @@ -3426,9 +3426,16 @@ 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; @@ -3438,42 +3445,15 @@ nsCanvasRenderingContext2D::GetImageData() 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; - nsAutoArrayPtr surfaceData (new (std::nothrow) PRUint8[w * h * 4]); - int surfaceDataStride = w*4; - int surfaceDataOffset = 0; + PRUint32 len = w * h * 4; + if (aDataLen != len) + return NS_ERROR_DOM_SYNTAX_ERR; - if (!surfaceData) - return NS_ERROR_OUT_OF_MEMORY; - - nsRefPtr tmpsurf = new gfxImageSurface(surfaceData, + /* Copy the surface contents to the buffer */ + nsRefPtr tmpsurf = new gfxImageSurface(aData, gfxIntSize(w, h), w * 4, gfxASurface::ImageFormatARGB32); @@ -3489,99 +3469,39 @@ nsCanvasRenderingContext2D::GetImageData() tmpctx->SetSource(mSurface, gfxPoint(-(int)x, -(int)y)); tmpctx->Paint(); - 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); - + // make sure sUnpremultiplyTable has been created EnsureUnpremultiplyTable(); - PRUint8 *row; + // 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; + 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 = *row++; - PRUint8 g = *row++; - PRUint8 r = *row++; - PRUint8 a = *row++; + PRUint8 b = *src++; + PRUint8 g = *src++; + PRUint8 r = *src++; + PRUint8 a = *src++; #else - PRUint8 a = *row++; - PRUint8 r = *row++; - PRUint8 g = *row++; - PRUint8 b = *row++; + PRUint8 a = *src++; + PRUint8 r = *src++; + PRUint8 g = *src++; + PRUint8 b = *src++; #endif // Convert to non-premultiplied color - - *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); + *dst++ = sUnpremultiplyTable[a][r]; + *dst++ = sUnpremultiplyTable[a][g]; + *dst++ = sUnpremultiplyTable[a][b]; + *dst++ = 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) @@ -3604,163 +3524,60 @@ 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; - jsuint arrayLen; - if (!JS_IsArrayObject(ctx, dataArray) || - !JS_GetArrayLength(ctx, dataArray, &arrayLen) || - arrayLen < (jsuint)(w * h * 4)) + PRUint32 len = w * h * 4; + if (aDataLen != len) return NS_ERROR_DOM_SYNTAX_ERR; - 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, + nsRefPtr imgsurf = new gfxImageSurface(gfxIntSize(w, h), 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); @@ -3775,7 +3592,7 @@ nsCanvasRenderingContext2D::PutImageData() mThebes->SetOperator(gfxContext::OPERATOR_SOURCE); mThebes->Fill(); - return Redraw(); + return Redraw(gfxRect(x, y, w, h)); } NS_IMETHODIMP @@ -3795,79 +3612,8 @@ nsCanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface) NS_IMETHODIMP nsCanvasRenderingContext2D::CreateImageData() { - 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; + /* Should never be called; handled entirely in the quickstub */ + return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP diff --git a/dom/interfaces/canvas/nsIDOMCanvasRenderingContext2D.idl b/dom/interfaces/canvas/nsIDOMCanvasRenderingContext2D.idl index 597651ebc84d..d59a081b71dc 100644 --- a/dom/interfaces/canvas/nsIDOMCanvasRenderingContext2D.idl +++ b/dom/interfaces/canvas/nsIDOMCanvasRenderingContext2D.idl @@ -182,10 +182,22 @@ 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 096222b843e6..2b758f8aa41f 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -12089,6 +12089,7 @@ 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 16f832a7640d..f24525d5c3e6 100644 --- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -520,9 +520,18 @@ 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 4d23b2f7fe4a..2da054216421 100644 --- a/js/src/xpconnect/src/dom_quickstubs.qsconf +++ b/js/src/xpconnect/src/dom_quickstubs.qsconf @@ -92,11 +92,6 @@ members = [ 'nsIDOMTextMetrics.*', 'nsIDOMCanvasGradient.*', 'nsIDOMCanvasPattern.*', - # NOTE: createImageDate(), getImageData(), and putImageData() use - # GetCurrentNativeCallContext - '-nsIDOMCanvasRenderingContext2D.createImageData', - '-nsIDOMCanvasRenderingContext2D.getImageData', - '-nsIDOMCanvasRenderingContext2D.putImageData', # dom/interfaces/core 'nsIDOMCharacterData.data', @@ -625,6 +620,10 @@ customMethodCalls = { 'nsICanvasRenderingContextWebGL_VertexAttrib1fv': CUSTOM_QS, 'nsICanvasRenderingContextWebGL_VertexAttrib2fv': CUSTOM_QS, 'nsICanvasRenderingContextWebGL_VertexAttrib3fv': CUSTOM_QS, - 'nsICanvasRenderingContextWebGL_VertexAttrib4fv': CUSTOM_QS + 'nsICanvasRenderingContextWebGL_VertexAttrib4fv': CUSTOM_QS, + # Canvas 2D + 'nsIDOMCanvasRenderingContext2D_CreateImageData': CUSTOM_QS, + 'nsIDOMCanvasRenderingContext2D_GetImageData': CUSTOM_QS, + 'nsIDOMCanvasRenderingContext2D_PutImageData': CUSTOM_QS, }