diff --git a/mozilla/content/canvas/public/nsICanvasElement.h b/mozilla/content/canvas/public/nsICanvasElement.h index e437ecb9e10..832eb78876a 100644 --- a/mozilla/content/canvas/public/nsICanvasElement.h +++ b/mozilla/content/canvas/public/nsICanvasElement.h @@ -50,6 +50,8 @@ class nsIFrame; class nsIRenderingContext; +class nsICanvasRenderingContextInternal; + struct _cairo_surface; class nsICanvasElement : public nsISupports { @@ -92,6 +94,13 @@ public: * is relative to the origin of the canvas frame. */ NS_IMETHOD InvalidateFrameSubrect (const nsRect& damageRect) = 0; + + /* + * Get the number of contexts in this canvas, and request a context at + * an index. + */ + virtual PRInt32 CountContexts () = 0; + virtual nsICanvasRenderingContextInternal *GetContextAtIndex (PRInt32 index) = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsICanvasElement, NS_ICANVASELEMENT_IID) diff --git a/mozilla/content/canvas/public/nsICanvasRenderingContextInternal.h b/mozilla/content/canvas/public/nsICanvasRenderingContextInternal.h index cf4a121c97e..fc14f06ea77 100644 --- a/mozilla/content/canvas/public/nsICanvasRenderingContextInternal.h +++ b/mozilla/content/canvas/public/nsICanvasRenderingContextInternal.h @@ -47,6 +47,7 @@ { 0xeab854fd, 0xaa5e, 0x44bb, { 0x8c, 0xc5, 0x8d, 0x02, 0xf8, 0x4b, 0x02, 0x16 } } class gfxContext; +class gfxASurface; class nsICanvasRenderingContextInternal : public nsISupports { public: @@ -72,6 +73,10 @@ public: NS_IMETHOD GetInputStream(const char *aMimeType, const PRUnichar *aEncoderOptions, nsIInputStream **aStream) = 0; + + // If this canvas context can be represented with a simple Thebes surface, + // return the surface. Otherwise returns an error. + NS_IMETHOD GetThebesSurface(gfxASurface **surface) = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsICanvasRenderingContextInternal, diff --git a/mozilla/content/canvas/src/nsCanvasRenderingContext2D.cpp b/mozilla/content/canvas/src/nsCanvasRenderingContext2D.cpp index 2a18e8059c8..33c38b1005b 100644 --- a/mozilla/content/canvas/src/nsCanvasRenderingContext2D.cpp +++ b/mozilla/content/canvas/src/nsCanvasRenderingContext2D.cpp @@ -295,6 +295,7 @@ public: NS_IMETHOD GetInputStream(const char* aMimeType, const PRUnichar* aEncoderOptions, nsIInputStream **aStream); + NS_IMETHOD GetThebesSurface(gfxASurface **surface); // nsISupports interface NS_DECL_ISUPPORTS @@ -411,8 +412,8 @@ protected: // cairo helpers nsresult CairoSurfaceFromElement(nsIDOMElement *imgElt, + PRBool forceCopy, cairo_surface_t **aCairoSurface, - PRUint8 **imgDataOut, PRInt32 *widthOut, PRInt32 *heightOut, nsIPrincipal **prinOut, PRBool *forceWriteOnlyOut); @@ -1089,12 +1090,11 @@ nsCanvasRenderingContext2D::CreatePattern(nsIDOMHTMLElement *image, } cairo_surface_t *imgSurf = nsnull; - PRUint8 *imgData = nsnull; PRInt32 imgWidth, imgHeight; nsCOMPtr principal; PRBool forceWriteOnly = PR_FALSE; - rv = CairoSurfaceFromElement(image, &imgSurf, &imgData, - &imgWidth, &imgHeight, + rv = CairoSurfaceFromElement(image, PR_TRUE, + &imgSurf, &imgWidth, &imgHeight, getter_AddRefs(principal), &forceWriteOnly); if (NS_FAILED(rv)) return rv; @@ -1918,12 +1918,11 @@ nsCanvasRenderingContext2D::DrawImage() cairo_matrix_t surfMat; cairo_pattern_t *pat; cairo_path_t *old_path; - PRUint8 *imgData = nsnull; PRInt32 imgWidth, imgHeight; nsCOMPtr principal; PRBool forceWriteOnly = PR_FALSE; - rv = CairoSurfaceFromElement(imgElt, &imgSurf, &imgData, - &imgWidth, &imgHeight, + rv = CairoSurfaceFromElement(imgElt, PR_FALSE, + &imgSurf, &imgWidth, &imgHeight, getter_AddRefs(principal), &forceWriteOnly); if (NS_FAILED(rv)) return rv; @@ -2154,12 +2153,15 @@ nsCanvasRenderingContext2D::ConvertJSValToXPCObject(nsISupports** aSupports, REF /* cairo ARGB32 surfaces are ARGB stored as a packed 32-bit integer; on little-endian * platforms, they appear as BGRA bytes in the surface data. The color values are also * stored with premultiplied alpha. + * + * If forceCopy is FALSE, a surface may be returned that's only valid during the current + * operation. If it's TRUE, a copy will always be made that can safely be retained. */ nsresult nsCanvasRenderingContext2D::CairoSurfaceFromElement(nsIDOMElement *imgElt, + PRBool forceCopy, cairo_surface_t **aCairoSurface, - PRUint8 **imgData, PRInt32 *widthOut, PRInt32 *heightOut, nsIPrincipal **prinOut, @@ -2202,20 +2204,35 @@ nsCanvasRenderingContext2D::CairoSurfaceFromElement(nsIDOMElement *imgElt, rv = canvas->GetSize(&w, &h); NS_ENSURE_SUCCESS(rv, rv); - nsRefPtr surf = - new gfxImageSurface (gfxIntSize(w, h), gfxASurface::ImageFormatARGB32); - nsRefPtr ctx = new gfxContext(surf); - ctx->SetOperator(gfxContext::OPERATOR_CLEAR); - ctx->Paint(); - ctx->SetOperator(gfxContext::OPERATOR_OVER); + nsRefPtr sourceSurface; - rv = canvas->RenderContexts(ctx); - if (NS_FAILED(rv)) - return rv; + if (!forceCopy && canvas->CountContexts() == 1) { + nsICanvasRenderingContextInternal *srcCanvas = canvas->GetContextAtIndex(0); + rv = srcCanvas->GetThebesSurface(getter_AddRefs(sourceSurface)); + if (NS_FAILED(rv)) + sourceSurface = nsnull; + } - *aCairoSurface = surf->CairoSurface(); + if (sourceSurface == nsnull) { + nsRefPtr surf = + gfxPlatform::GetPlatform()->CreateOffscreenSurface + (gfxIntSize(w, h), gfxASurface::ImageFormatARGB32); + nsRefPtr ctx = new gfxContext(surf); + + // we have to clear first, since some platform surfaces (X11, I'm + // looking at you) don't follow the cleared-surface convention. + ctx->SetOperator(gfxContext::OPERATOR_CLEAR); + ctx->Paint(); + ctx->SetOperator(gfxContext::OPERATOR_OVER); + + rv = canvas->RenderContexts(ctx); + if (NS_FAILED(rv)) + return rv; + sourceSurface = surf; + } + + *aCairoSurface = sourceSurface->CairoSurface(); cairo_surface_reference(*aCairoSurface); - *imgData = surf->Data(); *widthOut = w; *heightOut = h; @@ -2264,7 +2281,6 @@ nsCanvasRenderingContext2D::CairoSurfaceFromElement(nsIDOMElement *imgElt, *aCairoSurface = gfxsurf->CairoSurface(); cairo_surface_reference (*aCairoSurface); - *imgData = nsnull; return NS_OK; } @@ -2647,3 +2663,18 @@ nsCanvasRenderingContext2D::PutImageData() return Redraw(); } + +NS_IMETHODIMP +nsCanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface) +{ + if (!mThebesSurface) { + *surface = nsnull; + return NS_ERROR_NOT_AVAILABLE; + } + + *surface = mThebesSurface.get(); + NS_ADDREF(*surface); + + return NS_OK; +} + diff --git a/mozilla/content/html/content/src/nsHTMLCanvasElement.cpp b/mozilla/content/html/content/src/nsHTMLCanvasElement.cpp index 97d47a30e90..692f4281b01 100644 --- a/mozilla/content/html/content/src/nsHTMLCanvasElement.cpp +++ b/mozilla/content/html/content/src/nsHTMLCanvasElement.cpp @@ -93,6 +93,8 @@ public: virtual void SetWriteOnly(); NS_IMETHOD InvalidateFrame (); NS_IMETHOD InvalidateFrameSubrect (const nsRect& damageRect); + virtual PRInt32 CountContexts(); + virtual nsICanvasRenderingContextInternal *GetContextAtIndex (PRInt32 index); NS_IMETHOD_(PRBool) IsAttributeMapped(const nsIAtom* aAttribute) const; nsMapRuleToAttributesFunc GetAttributeMappingFunction() const; @@ -553,3 +555,21 @@ nsHTMLCanvasElement::InvalidateFrameSubrect(const nsRect& damageRect) return NS_OK; } + +PRInt32 +nsHTMLCanvasElement::CountContexts() +{ + if (mCurrentContext) + return 1; + + return 0; +} + +nsICanvasRenderingContextInternal * +nsHTMLCanvasElement::GetContextAtIndex (PRInt32 index) +{ + if (mCurrentContext && index == 0) + return mCurrentContext.get(); + + return NULL; +}