diff --git a/content/canvas/src/CustomQS_Canvas2D.h b/content/canvas/src/CustomQS_Canvas2D.h index 89cd4d012d8..4fc27e8492a 100644 --- a/content/canvas/src/CustomQS_Canvas2D.h +++ b/content/canvas/src/CustomQS_Canvas2D.h @@ -161,9 +161,6 @@ static bool CreateImageData(JSContext* cx, uint32_t w, uint32_t h, - nsIDOMCanvasRenderingContext2D* self, - int32_t x, - int32_t y, jsval* vp) { using mozilla::CheckedInt; @@ -186,19 +183,6 @@ CreateImageData(JSContext* cx, return false; } - if (self) { - JSObject *tdest = js::TypedArray::getTypedArray(darray); - - // make the call - nsresult rv = - self->GetImageData_explicit(x, y, w, h, - static_cast(JS_GetTypedArrayData(tdest)), - JS_GetTypedArrayByteLength(tdest)); - 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); @@ -239,7 +223,7 @@ nsIDOMCanvasRenderingContext2D_CreateImageData(JSContext *cx, unsigned argc, jsv return false; } - return CreateImageData(cx, data_width, data_height, NULL, 0, 0, vp); + return CreateImageData(cx, data_width, data_height, vp); } double width, height; @@ -258,64 +242,7 @@ nsIDOMCanvasRenderingContext2D_CreateImageData(JSContext *cx, unsigned argc, jsv uint32_t w = NS_ABS(wi); uint32_t h = NS_ABS(hi); - return CreateImageData(cx, w, h, NULL, 0, 0, vp); -} - -static JSBool -nsIDOMCanvasRenderingContext2D_GetImageData(JSContext *cx, unsigned argc, jsval *vp) -{ - XPC_QS_ASSERT_CONTEXT_OK(cx); - - JSObject *obj = JS_THIS_OBJECT(cx, vp); - if (!obj) - return JS_FALSE; - - nsIDOMCanvasRenderingContext2D *self; - xpc_qsSelfRef selfref; - JS::AutoValueRooter tvr(cx); - if (!xpc_qsUnwrapThis(cx, obj, &self, &selfref.ptr, tvr.jsval_addr(), nsnull)) - return JS_FALSE; - - if (argc < 4) - return xpc_qsThrow(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS); - - jsval *argv = JS_ARGV(cx, vp); - - double xd, yd, width, height; - if (!JS_ValueToNumber(cx, argv[0], &xd) || - !JS_ValueToNumber(cx, argv[1], &yd) || - !JS_ValueToNumber(cx, argv[2], &width) || - !JS_ValueToNumber(cx, argv[3], &height)) - return false; - - if (!NS_finite(xd) || !NS_finite(yd) || - !NS_finite(width) || !NS_finite(height)) - return xpc_qsThrow(cx, NS_ERROR_DOM_NOT_SUPPORTED_ERR); - - if (!width || !height) - return xpc_qsThrow(cx, NS_ERROR_DOM_INDEX_SIZE_ERR); - - int32_t x = JS_DoubleToInt32(xd); - int32_t y = JS_DoubleToInt32(yd); - int32_t wi = JS_DoubleToInt32(width); - int32_t hi = JS_DoubleToInt32(height); - - // Handle negative width and height by flipping the rectangle over in the - // relevant direction. - uint32_t w, h; - if (width < 0) { - w = -wi; - x -= w; - } else { - w = wi; - } - if (height < 0) { - h = -hi; - y -= h; - } else { - h = hi; - } - return CreateImageData(cx, w, h, self, x, y, vp); + return CreateImageData(cx, w, h, vp); } static JSBool diff --git a/content/canvas/src/nsCanvasRenderingContext2D.cpp b/content/canvas/src/nsCanvasRenderingContext2D.cpp index 7b4fa8b7f3a..4483fa19493 100644 --- a/content/canvas/src/nsCanvasRenderingContext2D.cpp +++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp @@ -85,7 +85,6 @@ #include "nsIDocShellTreeItem.h" #include "nsIDocShellTreeNode.h" #include "nsIXPConnect.h" -#include "jsapi.h" #include "nsDisplayList.h" #include "nsTArray.h" @@ -109,12 +108,19 @@ #include "nsIMemoryReporter.h" #include "nsStyleUtil.h" #include "CanvasImageCache.h" +#include "CheckedInt.h" #include + +#include "jsapi.h" +#include "jstypedarray.h" + +#include "mozilla/Assertions.h" #include "mozilla/dom/ContentParent.h" -#include "mozilla/ipc/PDocumentRendererParent.h" +#include "mozilla/dom/ImageData.h" #include "mozilla/dom/PBrowserParent.h" #include "mozilla/ipc/DocumentRendererParent.h" +#include "mozilla/ipc/PDocumentRendererParent.h" // windows.h (included by chromium code) defines this, in its infinite wisdom #undef DrawText @@ -405,6 +411,10 @@ public: friend class PathAutoSaveRestore; protected: + nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY, + uint32_t aWidth, uint32_t aHeight, + JSObject** aRetval); + /** * The number of living nsCanvasRenderingContexts. When this goes down to * 0, we free the premultiply and unpremultiply tables, if they exist. @@ -3821,19 +3831,11 @@ nsCanvasRenderingContext2D::EnsureUnpremultiplyTable() { NS_IMETHODIMP -nsCanvasRenderingContext2D::GetImageData() +nsCanvasRenderingContext2D::GetImageData(double aSx, double aSy, + double aSw, double aSh, + JSContext* aCx, + nsIDOMImageData** aRetval) { - /* 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 (!EnsureSurface()) - return NS_ERROR_FAILURE; - if (!mCanvasElement && !mDocShell) { NS_ERROR("No canvas element and no docshell in GetImageData!!!"); return NS_ERROR_DOM_SECURITY_ERR; @@ -3843,26 +3845,97 @@ nsCanvasRenderingContext2D::GetImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 // then it's special internal use. if (mCanvasElement && HTMLCanvasElement()->IsWriteOnly() && - !nsContentUtils::IsCallerTrustedForRead()) - { + !nsContentUtils::IsCallerTrustedForRead()) { // XXX ERRMSG we need to report an error to developers here! (bug 329026) return NS_ERROR_DOM_SECURITY_ERR; } - if (w == 0 || h == 0 || aDataLen != w * h * 4) - return NS_ERROR_DOM_SYNTAX_ERR; + if (!EnsureSurface()) { + return NS_ERROR_FAILURE; + } - CheckedInt32 rightMost = CheckedInt32(x) + w; - CheckedInt32 bottomMost = CheckedInt32(y) + h; + if (!NS_finite(aSx) || !NS_finite(aSy) || + !NS_finite(aSw) || !NS_finite(aSh)) { + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + } - if (!rightMost.valid() || !bottomMost.valid()) + if (!aSw || !aSh) { + return NS_ERROR_DOM_INDEX_SIZE_ERR; + } + + int32_t x = JS_DoubleToInt32(aSx); + int32_t y = JS_DoubleToInt32(aSy); + int32_t wi = JS_DoubleToInt32(aSw); + int32_t hi = JS_DoubleToInt32(aSh); + + // Handle negative width and height by flipping the rectangle over in the + // relevant direction. + uint32_t w, h; + if (aSw < 0) { + w = -wi; + x -= w; + } else { + w = wi; + } + if (aSh < 0) { + h = -hi; + y -= h; + } else { + h = hi; + } + + if (w == 0) { + w = 1; + } + if (h == 0) { + h = 1; + } + + JSObject* array; + nsresult rv = GetImageDataArray(aCx, x, y, w, h, &array); + NS_ENSURE_SUCCESS(rv, rv); + MOZ_ASSERT(array); + + nsRefPtr imageData = new ImageData(w, h, *array); + imageData.forget(aRetval); + return NS_OK; +} + +nsresult +nsCanvasRenderingContext2D::GetImageDataArray(JSContext* aCx, + int32_t aX, + int32_t aY, + uint32_t aWidth, + uint32_t aHeight, + JSObject** aRetval) +{ + MOZ_ASSERT(aWidth && aHeight); + + CheckedInt len = CheckedInt(aWidth) * aHeight * 4; + if (!len.valid()) { + return NS_ERROR_DOM_INDEX_SIZE_ERR; + } + + CheckedInt rightMost = CheckedInt(aX) + aWidth; + CheckedInt bottomMost = CheckedInt(aY) + aHeight; + + if (!rightMost.valid() || !bottomMost.valid()) { return NS_ERROR_DOM_SYNTAX_ERR; + } + + JSObject* darray = + js_CreateTypedArray(aCx, js::TypedArray::TYPE_UINT8_CLAMPED, len.value()); + if (!darray) { + return NS_ERROR_OUT_OF_MEMORY; + } + + uint8_t* data = static_cast(JS_GetTypedArrayData(darray)); /* Copy the surface contents to the buffer */ nsRefPtr tmpsurf = - new gfxImageSurface(aData, - gfxIntSize(w, h), - w * 4, + new gfxImageSurface(data, + gfxIntSize(aWidth, aHeight), + aWidth * 4, gfxASurface::ImageFormatARGB32); if (tmpsurf->CairoStatus()) @@ -3875,7 +3948,7 @@ nsCanvasRenderingContext2D::GetImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 if (!mZero) { gfxRect srcRect(0, 0, mWidth, mHeight); - gfxRect destRect(x, y, w, h); + gfxRect destRect(aX, aY, aWidth, aHeight); bool finishedPainting = false; // In the common case, we want to avoid the Rectangle call. @@ -3892,7 +3965,7 @@ nsCanvasRenderingContext2D::GetImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 if (!finishedPainting) { tmpctx->SetOperator(gfxContext::OPERATOR_SOURCE); - tmpctx->SetSource(mSurface, gfxPoint(-x, -y)); + tmpctx->SetSource(mSurface, gfxPoint(-aX, -aY)); tmpctx->Paint(); } } @@ -3902,11 +3975,11 @@ nsCanvasRenderingContext2D::GetImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 // 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; + uint8_t *src = data; + uint8_t *dst = data; - for (PRUint32 j = 0; j < h; j++) { - for (PRUint32 i = 0; i < w; i++) { + for (uint32_t j = 0; j < aHeight; ++j) { + for (uint32_t i = 0; i < aWidth; ++i) { // XXX Is there some useful swizzle MMX we can use here? #ifdef IS_LITTLE_ENDIAN PRUint8 b = *src++; @@ -3927,6 +4000,7 @@ nsCanvasRenderingContext2D::GetImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 } } + *aRetval = darray; return NS_OK; } diff --git a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp index c96362ab625..95047722fe9 100644 --- a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp +++ b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -82,7 +82,6 @@ #include "nsIDocShellTreeItem.h" #include "nsIDocShellTreeNode.h" #include "nsIXPConnect.h" -#include "jsapi.h" #include "nsDisplayList.h" #include "nsTArray.h" @@ -106,15 +105,21 @@ #include "nsIMemoryReporter.h" #include "nsStyleUtil.h" #include "CanvasImageCache.h" +#include "CheckedInt.h" #include -#include "mozilla/dom/ContentParent.h" -#include "mozilla/ipc/PDocumentRendererParent.h" -#include "mozilla/dom/PBrowserParent.h" -#include "mozilla/ipc/DocumentRendererParent.h" +#include "jsapi.h" +#include "jstypedarray.h" + +#include "mozilla/Assertions.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/ImageData.h" +#include "mozilla/dom/PBrowserParent.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/PathHelpers.h" +#include "mozilla/ipc/DocumentRendererParent.h" +#include "mozilla/ipc/PDocumentRendererParent.h" #include "mozilla/Preferences.h" #ifdef XP_WIN @@ -443,6 +448,10 @@ public: nsresult BezierTo(const Point& aCP1, const Point& aCP2, const Point& aCP3); protected: + nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY, + uint32_t aWidth, uint32_t aHeight, + JSObject** aRetval); + nsresult InitializeWithTarget(DrawTarget *surface, PRInt32 width, PRInt32 height); /** @@ -3992,15 +4001,10 @@ nsCanvasRenderingContext2DAzure::EnsureUnpremultiplyTable() { NS_IMETHODIMP -nsCanvasRenderingContext2DAzure::GetImageData() -{ - /* Should never be called -- GetImageData_explicit is the QS entry point */ - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -nsCanvasRenderingContext2DAzure::GetImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 w, PRUint32 h, - PRUint8 *aData, PRUint32 aDataLen) +nsCanvasRenderingContext2DAzure::GetImageData(double aSx, double aSy, + double aSw, double aSh, + JSContext* aCx, + nsIDOMImageData** aRetval) { if (!mValid) return NS_ERROR_FAILURE; @@ -4020,33 +4024,97 @@ nsCanvasRenderingContext2DAzure::GetImageData_explicit(PRInt32 x, PRInt32 y, PRU return NS_ERROR_DOM_SECURITY_ERR; } - if (w == 0 || h == 0 || aDataLen != w * h * 4) - return NS_ERROR_DOM_SYNTAX_ERR; + if (!NS_finite(aSx) || !NS_finite(aSy) || + !NS_finite(aSw) || !NS_finite(aSh)) { + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + } - CheckedInt32 rightMost = CheckedInt32(x) + w; - CheckedInt32 bottomMost = CheckedInt32(y) + h; + if (!aSw || !aSh) { + return NS_ERROR_DOM_INDEX_SIZE_ERR; + } - if (!rightMost.valid() || !bottomMost.valid()) + int32_t x = JS_DoubleToInt32(aSx); + int32_t y = JS_DoubleToInt32(aSy); + int32_t wi = JS_DoubleToInt32(aSw); + int32_t hi = JS_DoubleToInt32(aSh); + + // Handle negative width and height by flipping the rectangle over in the + // relevant direction. + uint32_t w, h; + if (aSw < 0) { + w = -wi; + x -= w; + } else { + w = wi; + } + if (aSh < 0) { + h = -hi; + y -= h; + } else { + h = hi; + } + + if (w == 0) { + w = 1; + } + if (h == 0) { + h = 1; + } + + JSObject* array; + nsresult rv = GetImageDataArray(aCx, x, y, w, h, &array); + NS_ENSURE_SUCCESS(rv, rv); + MOZ_ASSERT(array); + + nsRefPtr imageData = new ImageData(w, h, *array); + imageData.forget(aRetval); + return NS_OK; +} + +nsresult +nsCanvasRenderingContext2DAzure::GetImageDataArray(JSContext* aCx, + int32_t aX, + int32_t aY, + uint32_t aWidth, + uint32_t aHeight, + JSObject** aRetval) +{ + MOZ_ASSERT(aWidth && aHeight); + + CheckedInt len = CheckedInt(aWidth) * aHeight * 4; + if (!len.valid()) { + return NS_ERROR_DOM_INDEX_SIZE_ERR; + } + + CheckedInt rightMost = CheckedInt(aX) + aWidth; + CheckedInt bottomMost = CheckedInt(aY) + aHeight; + + if (!rightMost.valid() || !bottomMost.valid()) { return NS_ERROR_DOM_SYNTAX_ERR; + } + + JSObject* darray = + js_CreateTypedArray(aCx, js::TypedArray::TYPE_UINT8_CLAMPED, len.value()); + if (!darray) { + return NS_ERROR_OUT_OF_MEMORY; + } if (mZero) { + *aRetval = darray; return NS_OK; } - IntRect srcRect(0, 0, mWidth, mHeight); - IntRect destRect(x, y, w, h); + uint8_t* data = static_cast(JS_GetTypedArrayData(darray)); - if (!srcRect.Contains(destRect)) { - // Some data is outside the canvas surface, clear the destination. - memset(aData, 0, aDataLen); - } + IntRect srcRect(0, 0, mWidth, mHeight); + IntRect destRect(aX, aY, aWidth, aHeight); IntRect srcReadRect = srcRect.Intersect(destRect); IntRect dstWriteRect = srcReadRect; - dstWriteRect.MoveBy(-x, -y); + dstWriteRect.MoveBy(-aX, -aY); - PRUint8 *src = aData; - PRUint32 srcStride = w * 4; + uint8_t* src = data; + uint32_t srcStride = aWidth * 4; RefPtr readback; if (!srcReadRect.IsEmpty()) { @@ -4063,10 +4131,10 @@ nsCanvasRenderingContext2DAzure::GetImageData_explicit(PRInt32 x, PRInt32 y, PRU // NOTE! dst is the same as src, and this relies on reading // from src and advancing that ptr before writing to dst. - PRUint8 *dst = aData + dstWriteRect.y * (w * 4) + dstWriteRect.x * 4; + uint8_t* dst = data + dstWriteRect.y * (aWidth * 4) + dstWriteRect.x * 4; - for (int j = 0; j < dstWriteRect.height; j++) { - for (int i = 0; i < dstWriteRect.width; i++) { + for (int32_t j = 0; j < dstWriteRect.height; ++j) { + for (int32_t i = 0; i < dstWriteRect.width; ++i) { // XXX Is there some useful swizzle MMX we can use here? #ifdef IS_LITTLE_ENDIAN PRUint8 b = *src++; @@ -4086,8 +4154,10 @@ nsCanvasRenderingContext2DAzure::GetImageData_explicit(PRInt32 x, PRInt32 y, PRU *dst++ = a; } src += srcStride - (dstWriteRect.width * 4); - dst += (w * 4) - (dstWriteRect.width * 4); + dst += (aWidth * 4) - (dstWriteRect.width * 4); } + + *aRetval = darray; return NS_OK; } diff --git a/content/canvas/test/test_canvas.html b/content/canvas/test/test_canvas.html index a3e7440dc45..7c3376b9fc1 100644 --- a/content/canvas/test/test_canvas.html +++ b/content/canvas/test/test_canvas.html @@ -8219,7 +8219,7 @@ ok(window.Uint8ClampedArray !== undefined, "window.Uint8ClampedArray !== undefin window.ImageData.prototype.thisImplementsImageData = true; window.Uint8ClampedArray.prototype.thisImplementsUint8ClampedArray = true; var imgdata = ctx.getImageData(0, 0, 1, 1); -todo(imgdata.thisImplementsImageData, "imgdata.thisImplementsImageData"); +ok(imgdata.thisImplementsImageData, "imgdata.thisImplementsImageData"); ok(imgdata.data.thisImplementsUint8ClampedArray, "imgdata.data.thisImplementsUint8ClampedArray"); diff --git a/dom/interfaces/canvas/nsIDOMCanvasRenderingContext2D.idl b/dom/interfaces/canvas/nsIDOMCanvasRenderingContext2D.idl index 6391d46ae79..efb81a7fda5 100644 --- a/dom/interfaces/canvas/nsIDOMCanvasRenderingContext2D.idl +++ b/dom/interfaces/canvas/nsIDOMCanvasRenderingContext2D.idl @@ -70,7 +70,7 @@ interface nsIDOMImageData : nsISupports readonly attribute jsval data; }; -[scriptable, uuid(274213a8-df51-4b52-bfad-d306a1d5f642)] +[scriptable, uuid(c835c768-2dcc-461c-82f5-3653710d2942)] interface nsIDOMCanvasRenderingContext2D : nsISupports { // back-reference to the canvas element for which @@ -198,15 +198,15 @@ 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(); + [implicit_jscontext] + nsIDOMImageData getImageData(in double sx, in double sy, in double sw, in double sh); + + // This is just a dummy function; for JS, it is implemented as a quickstub + // that call the _explicit methods below. Native callers should use the + // _explicit method directly. 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); + // dataLen must be == width*height*4 in this call [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, in boolean hasDirtyRect, in long dirtyX, in long dirtyY, in long dirtyWidth, in long dirtyHeight); diff --git a/js/xpconnect/src/dom_quickstubs.qsconf b/js/xpconnect/src/dom_quickstubs.qsconf index 1ff39d30877..fe33dd66731 100644 --- a/js/xpconnect/src/dom_quickstubs.qsconf +++ b/js/xpconnect/src/dom_quickstubs.qsconf @@ -1002,7 +1002,6 @@ customMethodCalls = { 'nsIDOMWebGLRenderingContext_VertexAttrib4fv': CUSTOM_QS, # Canvas 2D 'nsIDOMCanvasRenderingContext2D_CreateImageData': CUSTOM_QS, - 'nsIDOMCanvasRenderingContext2D_GetImageData': CUSTOM_QS, 'nsIDOMCanvasRenderingContext2D_PutImageData': CUSTOM_QS, # Nasty hack to make the ordering of |arc| and |arcTo| correct. # |arc| is not traceable because it has an optional argument.