bug 473443 - drawImage of canvas onto self is incorrect r=jrmuizel,vlad FENNEC_M11
authorBrad Lassey <blassey@mozilla.com>
Tue, 10 Feb 2009 18:44:40 -0500
changeset 24903 f817a4378f32b1ad0a7c4b5a9949586dba816da5
parent 24902 4e85d5b5e08758e1ee14b3c263c0fc1126f8104a
child 24904 325db1661cbb7936a689f10b5894f594bcec27a2
push id5285
push userblassey@mozilla.com
push dateTue, 10 Feb 2009 23:46:10 +0000
treeherdermozilla-central@f817a4378f32 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel, vlad
bugs473443
milestone1.9.2a1pre
bug 473443 - drawImage of canvas onto self is incorrect r=jrmuizel,vlad
content/canvas/src/nsCanvasRenderingContext2D.cpp
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -2795,16 +2795,115 @@ nsCanvasRenderingContext2D::IsPointInPat
 {
     if (!FloatValidate(x,y))
         return NS_ERROR_DOM_SYNTAX_ERR;
 
     *retVal = mThebes->PointInFill(gfxPoint(x,y));
     return NS_OK;
 }
 
+#ifdef WINCE
+/* A simple bitblt for self copies that ensures that we don't overwrite any
+ * area before we've read from it. */
+static void
+bitblt(gfxImageSurface *s, int src_x, int src_y, int width, int height,
+                int dest_x, int dest_y) {
+    unsigned char *data = s->Data();
+    int stride = s->Stride()/4;
+    int x, y;
+    unsigned int *dest = (unsigned int *)data;
+    unsigned int *src  = (unsigned int *)data;
+
+    int surface_width  = s->Width();
+    int surface_height = s->Height();
+
+    /* clip to the surface size */
+    if (src_x < 0) {
+        dest_x += -src_x;
+        width  -= -src_x;
+        src_x = 0;
+    }
+    if (src_y < 0) {
+        dest_y += -src_y;
+        height -= -src_y;
+        src_y = 0;
+    }
+    if (dest_x < 0) {
+        src_x += -dest_x;
+        width -= -dest_x;
+        dest_x = 0;
+    }
+    if (dest_y < 0) {
+        src_y += -dest_y;
+        width -= -dest_y;
+        dest_y = 0;
+    }
+
+    /*XXX: we might want to check for overflow? */
+    if (src_x + width > surface_width)
+        width = surface_width - src_x;
+    if (dest_x + width > surface_width)
+        width = surface_width - dest_x;
+    if (src_y + height > surface_height)
+        height = surface_height - src_y;
+    if (dest_y + height > surface_height)
+        height = surface_height - dest_y;
+
+    if (dest_x < src_x) {
+        if (dest_y < src_y) {
+            dest = dest + dest_y*stride + dest_x;
+            src  = src  +  src_y*stride + src_x;
+            /* copy right to left, top to bottom */
+            for (y=0; y<height; y++) {
+                for (x=0; x<width; x++) {
+                    *dest++ = *src++;
+                }
+                dest += stride - width;
+                src  += stride - width;
+            }
+        } else {
+            dest = dest + (dest_y+height-1)*stride + dest_x;
+            src  = src  + (src_y +height-1)*stride + src_x;
+            /* copy right to left, bottom to top */
+            for (y=0; y<height; y++) {
+                for (x=0; x<width; x++) {
+                    *dest++ = *src++;
+                }
+                dest += -stride - width;
+                src  += -stride - width;
+            }
+        }
+    } else {
+        if (dest_y < src_y) {
+            dest = dest + dest_y*stride + (dest_x+width-1);
+            src  = src  +  src_y*stride + (src_x +width-1);
+            /* copy left to right, top to bottom */
+            for (y=0; y<height; y++) {
+                for (x=0; x<width; x++) {
+                    *dest-- = *src--;
+                }
+                dest += stride + width;
+                src  += stride + width;
+            }
+        } else {
+            dest = dest + (dest_y+height-1)*stride + (dest_x+width-1);
+            src  = src  + (src_y +height-1)*stride + (src_x +width-1);
+            /* copy left to right, bottom to top */
+            for (y=0; y<height; y++) {
+                for (x=0; x<width; x++) {
+                    *dest-- = *src--;
+                }
+                dest += -stride + width;
+                src  += -stride + width;
+            }
+        }
+    }
+}
+#endif
+
 //
 // image
 //
 
 // drawImage(in HTMLImageElement image, in float dx, in float dy);
 //   -- render image from 0,0 at dx,dy top-left coords
 // drawImage(in HTMLImageElement image, in float dx, in float dy, in float sw, in float sh);
 //   -- render image from 0,0 at dx,dy top-left coords clipping it to sw,sh
@@ -2857,16 +2956,19 @@ nsCanvasRenderingContext2D::DrawImage()
 
     PRInt32 imgWidth, imgHeight;
     nsCOMPtr<nsIPrincipal> principal;
     PRBool forceWriteOnly = PR_FALSE;
     gfxMatrix matrix;
     nsRefPtr<gfxPattern> pattern;
     nsRefPtr<gfxPath> path;
     nsRefPtr<gfxASurface> imgsurf;
+#ifdef WINCE
+    nsRefPtr<gfxASurface> currentSurface;
+#endif
     rv = ThebesSurfaceFromElement(imgElt, PR_FALSE,
                                   getter_AddRefs(imgsurf), &imgWidth, &imgHeight,
                                   getter_AddRefs(principal), &forceWriteOnly);
     if (NS_FAILED(rv))
         return rv;
     DoDrawImageSecurityCheck(principal, forceWriteOnly);
 
     gfxContextPathAutoSaveRestore pathSR(mThebes, PR_FALSE);
@@ -2926,16 +3028,44 @@ nsCanvasRenderingContext2D::DrawImage()
     {
         // XXX ERRMSG we need to report an error to developers here! (bug 329026)
         rv = NS_ERROR_DOM_INDEX_SIZE_ERR;
         goto FINISH;
     }
     
     matrix.Translate(gfxPoint(sx, sy));
     matrix.Scale(sw/dw, sh/dh);
+#ifdef WINCE
+    currentSurface = getter_AddRefs(mThebes->CurrentSurface());
+
+    /* cairo doesn't have consistent semantics for drawing a surface onto
+     * itself. Specifically, pixman will not preserve the contents when doing
+     * the copy. So to get the desired semantics a temporary copy would be needed.
+     * Instead we optimize opaque self copies here */
+    if (currentSurface == imgsurf) {
+        if (imgsurf->GetType() == gfxASurface::SurfaceTypeImage) {
+            gfxImageSurface *surf = static_cast<gfxImageSurface*>(imgsurf.get());
+            gfxContext::GraphicsOperator op = mThebes->CurrentOperator();
+            PRBool opaque, unscaled;
+
+            opaque  = surf->Format() == gfxASurface::ImageFormatARGB32 &&
+                (op == gfxContext::OPERATOR_SOURCE);
+            opaque |= surf->Format() == gfxASurface::ImageFormatRGB24  &&
+                (op == gfxContext::OPERATOR_SOURCE || op == gfxContext::OPERATOR_OVER);
+
+            unscaled = sw == dw && sh == dh;
+
+            if (opaque && unscaled) {
+                bitblt(surf, sx, sy, sw, sh, dx, dy);
+                rv = NS_OK;
+                goto FINISH;
+            }
+        }
+    }
+#endif
 
     pattern = new gfxPattern(imgsurf);
     pattern->SetMatrix(matrix);
 
     pathSR.Save();
 
     {
         gfxContextAutoSaveRestore autoSR(mThebes);
@@ -3146,19 +3276,24 @@ nsCanvasRenderingContext2D::ThebesSurfac
         rv = canvas->GetSize(&w, &h);
         NS_ENSURE_SUCCESS(rv, rv);
 
         nsRefPtr<gfxASurface> sourceSurface;
 
         if (!forceCopy && canvas->CountContexts() == 1) {
             nsICanvasRenderingContextInternal *srcCanvas = canvas->GetContextAtIndex(0);
             rv = srcCanvas->GetThebesSurface(getter_AddRefs(sourceSurface));
+#ifndef WINCE
             // force a copy if we couldn't get the surface, or if it's
             // the same as what we have
             if (sourceSurface == mSurface || NS_FAILED(rv))
+#else
+            // force a copy if we couldn't get the surface
+            if (NS_FAILED(rv))
+#endif
                 sourceSurface = nsnull;
         }
 
         if (sourceSurface == nsnull) {
             nsRefPtr<gfxASurface> surf =
                 gfxPlatform::GetPlatform()->CreateOffscreenSurface
                 (gfxIntSize(w, h), gfxASurface::ImageFormatARGB32);
             nsRefPtr<gfxContext> ctx = new gfxContext(surf);