b=580440 use native-rendering temp surfaces only as large as necessary r=roc a2.0=beltzner
authorKarl Tomlinson <karlt+@karlt.net>
Fri, 23 Jul 2010 10:25:19 +1200
changeset 48107 a171b4faa517e9bda444757ee7c153b9438a3daf
parent 48106 bdfeac0b9d4a7aacd751d38d049fd3434409cfe9
child 48108 92559a5573bd3ef6159f8dab92931107ff581440
push id14574
push userktomlinson@mozilla.com
push dateThu, 22 Jul 2010 23:01:26 +0000
treeherdermozilla-central@02d92df7381d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs580440
milestone2.0b3pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
b=580440 use native-rendering temp surfaces only as large as necessary r=roc a2.0=beltzner
gfx/thebes/gfxXlibNativeRenderer.cpp
gfx/thebes/gfxXlibNativeRenderer.h
layout/generic/nsObjectFrame.cpp
widget/src/gtk2/nsNativeThemeGTK.cpp
--- a/gfx/thebes/gfxXlibNativeRenderer.cpp
+++ b/gfx/thebes/gfxXlibNativeRenderer.cpp
@@ -77,186 +77,99 @@
 static cairo_bool_t
 _convert_coord_to_int (double coord, PRInt32 *v)
 {
     *v = (PRInt32)coord;
     /* XXX allow some tolerance here? */
     return *v == coord;
 }
 
-static cairo_bool_t
-_intersect_interval (double a_begin, double a_end, double b_begin, double b_end,
-                     double *out_begin, double *out_end)
-{
-    *out_begin = a_begin;
-    if (*out_begin < b_begin) {
-        *out_begin = b_begin;
-    }
-    *out_end = a_end;
-    if (*out_end > b_end) {
-        *out_end = b_end;
-    }
-    return *out_begin < *out_end;
-}
-
-static cairo_bool_t
+static PRBool
 _get_rectangular_clip (cairo_t *cr,
                        const nsIntRect& bounds,
-                       cairo_bool_t *need_clip,
+                       PRBool *need_clip,
                        nsIntRect *rectangles, int max_rectangles,
                        int *num_rectangles)
 {
     cairo_rectangle_list_t *cliplist;
     cairo_rectangle_t *clips;
     int i;
-    double b_x = bounds.x;
-    double b_y = bounds.y;
-    double b_x_most = bounds.XMost();
-    double b_y_most = bounds.YMost();
-    int rect_count = 0;
-    cairo_bool_t retval = True;
+    PRBool retval = PR_TRUE;
 
     cliplist = cairo_copy_clip_rectangle_list (cr);
     if (cliplist->status != CAIRO_STATUS_SUCCESS) {
-        retval = False;
+        retval = PR_FALSE;
+        NATIVE_DRAWING_NOTE("TAKING SLOW PATH: non-rectangular clip\n");
         goto FINISH;
     }
 
-    if (cliplist->num_rectangles == 0) {
-        *num_rectangles = 0;
-        *need_clip = True;
-        goto FINISH;
-    }
-
+    /* the clip is always in surface backend coordinates (i.e. native backend coords) */
     clips = cliplist->rectangles;
 
     for (i = 0; i < cliplist->num_rectangles; ++i) {
-        double intersect_x, intersect_y, intersect_x_most, intersect_y_most;
         
-        /* the clip is always in surface backend coordinates (i.e. native backend coords) */
-        if (b_x >= clips[i].x && b_x_most <= clips[i].x + clips[i].width &&
-            b_y >= clips[i].y && b_y_most <= clips[i].y + clips[i].height) {
-            /* the bounds are entirely inside the clip region so we don't need to clip. */
-            *need_clip = False;
+        nsIntRect rect;
+        if (!_convert_coord_to_int (clips[i].x, &rect.x) ||
+            !_convert_coord_to_int (clips[i].y, &rect.y) ||
+            !_convert_coord_to_int (clips[i].width, &rect.width) ||
+            !_convert_coord_to_int (clips[i].height, &rect.height))
+        {
+            retval = PR_FALSE;
+            NATIVE_DRAWING_NOTE("TAKING SLOW PATH: non-integer clip\n");
             goto FINISH;
         }
-        
-        if (_intersect_interval (b_x, b_x_most, clips[i].x, clips[i].x + clips[i].width,
-                                 &intersect_x, &intersect_x_most) &&
-            _intersect_interval (b_y, b_y_most, clips[i].y, clips[i].y + clips[i].height,
-                                 &intersect_y, &intersect_y_most)) {
-            nsIntRect *rect = &rectangles[rect_count];
 
-            if (rect_count >= max_rectangles) {
-                retval = False;
-                goto FINISH;
-            }
+        if (rect == bounds) {
+            /* the bounds are entirely inside the clip region so we don't need to clip. */
+            *need_clip = PR_FALSE;
+            goto FINISH;
+        }            
 
-            if (!_convert_coord_to_int (intersect_x, &rect->x) ||
-                !_convert_coord_to_int (intersect_y, &rect->y) ||
-                !_convert_coord_to_int (intersect_x_most - intersect_x, &rect->width) ||
-                !_convert_coord_to_int (intersect_y_most - intersect_y, &rect->height))
-            {
-                retval = False;
-                goto FINISH;
-            }
+        NS_ASSERTION(bounds.Contains(rect),
+                     "Was expecting to be clipped to bounds");
 
-            ++rect_count;
+        if (i >= max_rectangles) {
+            retval = PR_FALSE;
+            NATIVE_DRAWING_NOTE("TAKING SLOW PATH: unsupported clip rectangle count\n");
+            goto FINISH;
         }
+
+        rectangles[i] = rect;
     }
   
-    *need_clip = True;
-    *num_rectangles = rect_count;
+    *need_clip = PR_TRUE;
+    *num_rectangles = cliplist->num_rectangles;
 
 FINISH:
     cairo_rectangle_list_destroy (cliplist);
 
     return retval;
 }
 
 #define MAX_STATIC_CLIP_RECTANGLES 50
 
 /**
  * Try the direct path.
  * @return True if we took the direct path
  */
 PRBool
-gfxXlibNativeRenderer::DrawDirect(gfxContext *ctx, nsIntSize bounds,
+gfxXlibNativeRenderer::DrawDirect(gfxContext *ctx, nsIntSize size,
                                   PRUint32 flags,
                                   Screen *screen, Visual *visual)
 {
-    cairo_surface_t *target;
-    cairo_matrix_t matrix;
-    cairo_bool_t needs_clip;
-    nsIntRect rectangles[MAX_STATIC_CLIP_RECTANGLES];
-    int rect_count;
-    double device_offset_x, device_offset_y;
-    int max_rectangles;
-    cairo_bool_t have_rectangular_clip;
+    cairo_t *cr = ctx->GetCairo();
 
-    cairo_t *cr = ctx->GetCairo();
-    target = cairo_get_group_target (cr);
-    cairo_surface_get_device_offset (target, &device_offset_x, &device_offset_y);
-    cairo_get_matrix (cr, &matrix);
-    
-    /* Check that the matrix is a pure translation */
-    /* XXX test some approximation to == 1.0 here? */
-    if (matrix.xx != 1.0 || matrix.yy != 1.0 || matrix.xy != 0.0 || matrix.yx != 0.0) {
-        NATIVE_DRAWING_NOTE("TAKING SLOW PATH: matrix not a pure translation\n");
-        return PR_FALSE;
-    }
-    /* Check that the matrix translation offsets (adjusted for
-       device offset) are integers */
-    nsIntPoint offset;
-    if (!_convert_coord_to_int (matrix.x0 + device_offset_x, &offset.x) ||
-        !_convert_coord_to_int (matrix.y0 + device_offset_y, &offset.y)) {
-        NATIVE_DRAWING_NOTE("TAKING SLOW PATH: non-integer offset\n");
-        return PR_FALSE;
-    }
-    
-    max_rectangles = 0;
-    if (flags & DRAW_SUPPORTS_CLIP_RECT) {
-      max_rectangles = 1;
-    }
-    if (flags & DRAW_SUPPORTS_CLIP_LIST) {
-      max_rectangles = MAX_STATIC_CLIP_RECTANGLES;
-    }
-    
-    /* Check that the clip is rectangular and aligned on unit boundaries. */
-    /* Temporarily set the matrix for _get_rectangular_clip. It's basically
-       the identity matrix, but we must adjust for the fact that our
-       offset-rect is in device coordinates. */
-    cairo_identity_matrix (cr);
-    cairo_translate (cr, -device_offset_x, -device_offset_y);
-    have_rectangular_clip =
-        _get_rectangular_clip (cr,
-                               nsIntRect(offset, bounds),
-                               &needs_clip,
-                               rectangles, max_rectangles, &rect_count);
-    cairo_set_matrix (cr, &matrix);
-    if (!have_rectangular_clip) {
-        NATIVE_DRAWING_NOTE("TAKING SLOW PATH: unsupported clip\n");
-        return PR_FALSE;
-    }
-
-    /* Stop now if everything is clipped out */
-    if (needs_clip && rect_count == 0) {
-        NATIVE_DRAWING_NOTE("TAKING FAST PATH: all clipped\n");
-        return PR_TRUE;
-    }
-      
     /* Check that the operator is OVER */
     if (cairo_get_operator (cr) != CAIRO_OPERATOR_OVER) {
         NATIVE_DRAWING_NOTE("TAKING SLOW PATH: non-OVER operator\n");
         return PR_FALSE;
     }
     
-    /* Check that the target surface is an xlib surface. Do this late because
-       we might complete early above when when the object to be drawn is
-       completely clipped out. */
+    /* Check that the target surface is an xlib surface. */
+    cairo_surface_t *target = cairo_get_group_target (cr);
     if (cairo_surface_get_type (target) != CAIRO_SURFACE_TYPE_XLIB) {
         NATIVE_DRAWING_NOTE("TAKING SLOW PATH: non-X surface\n");
         return PR_FALSE;
     }
     
     /* Check that the screen is supported.
        Visuals belong to screens, so, if alternate visuals are not supported,
        then alternate screens cannot be supported. */  
@@ -285,16 +198,67 @@ gfxXlibNativeRenderer::DrawDirect(gfxCon
         if (!target_format ||
             (target_format !=
              XRenderFindVisualFormat (DisplayOfScreen(screen), visual))) {
             NATIVE_DRAWING_NOTE("TAKING SLOW PATH: unsupported Visual\n");
             return PR_FALSE;
         }
     }
   
+    cairo_matrix_t matrix;
+    cairo_get_matrix (cr, &matrix);
+    double device_offset_x, device_offset_y;
+    cairo_surface_get_device_offset (target, &device_offset_x, &device_offset_y);
+
+    /* Draw() checked that the matrix contained only a very-close-to-integer
+       translation.  Here (and in several other places and thebes) device
+       offsets are assumed to be integer. */
+    NS_ASSERTION(PRUint32(device_offset_x) == device_offset_x &&
+                 PRUint32(device_offset_y) == device_offset_y,
+                 "Expected integer device offsets");
+    nsIntPoint offset(NS_lroundf(matrix.x0 + device_offset_x),
+                      NS_lroundf(matrix.y0 + device_offset_y));
+    
+    int max_rectangles = 0;
+    if (flags & DRAW_SUPPORTS_CLIP_RECT) {
+      max_rectangles = 1;
+    }
+    if (flags & DRAW_SUPPORTS_CLIP_LIST) {
+      max_rectangles = MAX_STATIC_CLIP_RECTANGLES;
+    }
+
+    /* The client won't draw outside the surface so consider this when
+       analysing clip rectangles. */
+    nsIntRect bounds(offset, size);
+    bounds.IntersectRect(bounds,
+                         nsIntRect(0, 0,
+                                   cairo_xlib_surface_get_width(target),
+                                   cairo_xlib_surface_get_height(target)));
+
+    PRBool needs_clip;
+    nsIntRect rectangles[MAX_STATIC_CLIP_RECTANGLES];
+    int rect_count;
+
+    /* Check that the clip is rectangular and aligned on unit boundaries. */
+    /* Temporarily set the matrix for _get_rectangular_clip. It's basically
+       the identity matrix, but we must adjust for the fact that our
+       offset-rect is in device coordinates. */
+    cairo_identity_matrix (cr);
+    cairo_translate (cr, -device_offset_x, -device_offset_y);
+    PRBool have_rectangular_clip =
+        _get_rectangular_clip (cr, bounds, &needs_clip,
+                               rectangles, max_rectangles, &rect_count);
+    cairo_set_matrix (cr, &matrix);
+    if (!have_rectangular_clip)
+        return PR_FALSE;
+
+    /* Draw only calls this function when the clip region is not empty. */
+    NS_ASSERTION(!needs_clip || rect_count != 0,
+                 "Where did the clip region go?");
+      
     /* we're good to go! */
     NATIVE_DRAWING_NOTE("TAKING FAST PATH\n");
     cairo_surface_flush (target);
     nsRefPtr<gfxASurface> surface = gfxASurface::Wrap(target);
     nsresult rv = DrawWithXlib(static_cast<gfxXlibSurface*>(surface.get()),
                                offset, rectangles,
                                needs_clip ? rect_count : 0);
     if (NS_SUCCEEDED(rv)) {
@@ -355,31 +319,23 @@ static already_AddRefed<gfxXlibSurface>
     }
     return gfxXlibSurface::Create(screen, visual,
                                   gfxIntSize(size.width, size.height),
                                   drawable);
 }
 
 PRBool
 gfxXlibNativeRenderer::DrawOntoTempSurface(gfxXlibSurface *tempXlibSurface,
-                                           double background_gray_value)
+                                           nsIntPoint offset)
 {
     cairo_surface_t *temp_xlib_surface = tempXlibSurface->CairoSurface();
-
-    cairo_t *cr = cairo_create (temp_xlib_surface);
-    cairo_set_source_rgb (cr, background_gray_value, background_gray_value,
-                          background_gray_value);
-    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
-    cairo_paint (cr);
-    cairo_destroy (cr);
-    
     cairo_surface_flush (temp_xlib_surface);
     /* no clipping is needed because the callback can't draw outside the native
        surface anyway */
-    nsresult rv = DrawWithXlib(tempXlibSurface, nsIntPoint(0, 0), NULL, 0);
+    nsresult rv = DrawWithXlib(tempXlibSurface, offset, NULL, 0);
     cairo_surface_mark_dirty (temp_xlib_surface);
     return NS_SUCCEEDED(rv);
 }
 
 static cairo_surface_t *
 _copy_xlib_surface_to_image (gfxXlibSurface *tempXlibSurface,
                              cairo_format_t format,
                              int width, int height,
@@ -496,70 +452,121 @@ gfxXlibNativeRenderer::Draw(gfxContext* 
     unsigned char *white_data;
   
     if (result) {
         result->mSurface = NULL;
         result->mUniformAlpha = PR_FALSE;
         result->mUniformColor = PR_FALSE;
     }
     
-    /* exit early if there's no work to do. This is actually important
-       because we'll die with an X error if we try to create an empty temporary
-       pixmap */
-    if (size.width == 0 || size.height == 0)
-        return;
+    PRBool matrixIsIntegerTranslation =
+        !ctx->CurrentMatrix().HasNonIntegerTranslation();
+
+    // The padding of 0.5 for non-pixel-exact transformations used here is
+    // the same as what _cairo_pattern_analyze_filter uses.
+    const gfxFloat filterRadius = 0.5;
+    gfxRect affectedRect(0.0, 0.0, size.width, size.height);
+    if (!matrixIsIntegerTranslation) {
+        // The filter footprint means that the affected rectangle is a
+        // little larger than the drawingRect;
+        affectedRect.Outset(filterRadius);
+
+        NATIVE_DRAWING_NOTE("TAKING SLOW PATH: matrix not integer translation\n");
+    }
+    // Clipping to the region affected by drawing allows us to consider only
+    // the portions of the clip region that will be affected by drawing.
+    gfxRect clipExtents;
+    {
+        gfxContextAutoSaveRestore autoSR(ctx);
+        ctx->Clip(affectedRect);
+
+        clipExtents = ctx->GetClipExtents();
+        if (clipExtents.IsEmpty())
+            return; // nothing to do
 
-    if (DrawDirect(ctx, size, flags, screen, visual))
-        return;
+        if (matrixIsIntegerTranslation &&
+            DrawDirect(ctx, size, flags, screen, visual))
+            return;
+    }
+
+    nsIntRect drawingRect(nsIntPoint(0, 0), size);
+    PRBool drawIsOpaque = (flags & DRAW_IS_OPAQUE) != 0;
+    if (drawIsOpaque || !result) {
+        // Drawing need only be performed within the clip extents
+        // (and padding for the filter).
+        if (!matrixIsIntegerTranslation) {
+            // The source surface may need to be a little larger than the clip
+            // extents due to the filter footprint.  
+            clipExtents.Outset(filterRadius);
+        }
+        clipExtents.RoundOut();
+
+        nsIntRect intExtents(PRInt32(clipExtents.X()),
+                             PRInt32(clipExtents.Y()),
+                             PRInt32(clipExtents.Width()),
+                             PRInt32(clipExtents.Height()));
+        drawingRect.IntersectRect(drawingRect, intExtents);
+    }
+    gfxPoint offset(drawingRect.x, drawingRect.y);
 
     cairo_t *cr = ctx->GetCairo();
     nsRefPtr<gfxXlibSurface> tempXlibSurface = 
-        _create_temp_xlib_surface (cr, size, flags, screen, visual);
+        _create_temp_xlib_surface (cr, drawingRect.Size(),
+                                   flags, screen, visual);
     if (tempXlibSurface == NULL)
         return;
   
-    if (!DrawOntoTempSurface(tempXlibSurface, 0.0)) {
+    nsRefPtr<gfxContext> tmpCtx;
+    if (!drawIsOpaque) {
+        tmpCtx = new gfxContext(tempXlibSurface);
+        tmpCtx->SetOperator(gfxContext::OPERATOR_CLEAR);
+        tmpCtx->Paint();
+    }
+
+    if (!DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft())) {
         return;
     }
   
-    if (flags & DRAW_IS_OPAQUE) {
-        cairo_set_source_surface (cr, tempXlibSurface->CairoSurface(),
-                                  0.0, 0.0);
-        cairo_paint (cr);
+    if (drawIsOpaque) {
+        ctx->SetSource(tempXlibSurface, offset);
+        ctx->Paint();
         if (result) {
             result->mSurface = tempXlibSurface;
             /* fill in the result with what we know, which is really just what our
                assumption was */
             result->mUniformAlpha = PR_TRUE;
             result->mColor.a = 1.0;
         }
         return;
     }
     
-    int width = size.width;
-    int height = size.height;
+    int width = drawingRect.width;
+    int height = drawingRect.height;
     black_image_surface =
         _copy_xlib_surface_to_image (tempXlibSurface, CAIRO_FORMAT_ARGB32,
                                      width, height, &black_data);
     
-    DrawOntoTempSurface(tempXlibSurface, 1.0);
+    tmpCtx->SetDeviceColor(gfxRGBA(1.0, 1.0, 1.0));
+    tmpCtx->SetOperator(gfxContext::OPERATOR_SOURCE);
+    tmpCtx->Paint();
+    DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft());
     white_image_surface =
         _copy_xlib_surface_to_image (tempXlibSurface, CAIRO_FORMAT_RGB24,
                                      width, height, &white_data);
   
     if (black_image_surface && white_image_surface &&
         cairo_surface_status (black_image_surface) == CAIRO_STATUS_SUCCESS &&
         cairo_surface_status (white_image_surface) == CAIRO_STATUS_SUCCESS &&
         black_data != NULL && white_data != NULL) {
         cairo_surface_flush (black_image_surface);
         cairo_surface_flush (white_image_surface);
         _compute_alpha_values ((uint32_t*)black_data, (uint32_t*)white_data, width, height, result);
         cairo_surface_mark_dirty (black_image_surface);
         
-        cairo_set_source_surface (cr, black_image_surface, 0.0, 0.0);
+        cairo_set_source_surface (cr, black_image_surface, offset.x, offset.y);
         /* if the caller wants to retrieve the rendered image, put it into
            a 'similar' surface, and use that as the source for the drawing right
            now. This means we always return a surface similar to the surface
            used for 'cr', which is ideal if it's going to be cached and reused.
            We do not return an image if the result has uniform color and alpha. */
         if (result && (!result->mUniformAlpha || !result->mUniformColor)) {
             cairo_surface_t *target = cairo_get_group_target (cr);
             cairo_surface_t *similar_surface =
--- a/gfx/thebes/gfxXlibNativeRenderer.h
+++ b/gfx/thebes/gfxXlibNativeRenderer.h
@@ -52,21 +52,27 @@ class gfxContext;
  * use it to draw into any Thebes context. The user should subclass this class,
  * override DrawWithXib, and then call Draw(). The drawing will be subjected
  * to all Thebes transformations, clipping etc.
  */
 class THEBES_API gfxXlibNativeRenderer {
 public:
     /**
      * Perform the native drawing.
+     * @param surface the drawable for drawing.
+     *                The extents of this surface do not necessarily cover the
+     *                entire rectangle with size provided to Draw().
      * @param offsetX draw at this offset into the given drawable
      * @param offsetY draw at this offset into the given drawable
-     * @param clipRects an array of rects; clip to the union
+     * @param clipRects an array of rectangles; clip to the union.
+     *                  Any rectangles provided will be contained by the
+     *                  rectangle with size provided to Draw and by the
+     *                  surface extents.
      * @param numClipRects the number of rects in the array, or zero if
-     * no clipping is required
+     *                     no clipping is required.
      */
     virtual nsresult DrawWithXlib(gfxXlibSurface* surface,
                                   nsIntPoint offset,
                                   nsIntRect* clipRects, PRUint32 numClipRects) = 0;
   
     enum {
         // If set, then Draw() is opaque, i.e., every pixel in the intersection
         // of the clipRect and (offset.x,offset.y,bounds.width,bounds.height)
@@ -111,13 +117,13 @@ public:
               PRUint32 flags, Screen *screen, Visual *visual,
               DrawOutput* result);
 
 private:
     PRBool DrawDirect(gfxContext *ctx, nsIntSize bounds,
                       PRUint32 flags, Screen *screen, Visual *visual);
 
     PRBool DrawOntoTempSurface(gfxXlibSurface *tempXlibSurface,
-                               double background_gray_value);
+                               nsIntPoint offset);
 
 };
 
 #endif /*GFXXLIBNATIVERENDER_H_*/
--- a/layout/generic/nsObjectFrame.cpp
+++ b/layout/generic/nsObjectFrame.cpp
@@ -5491,26 +5491,32 @@ nsPluginInstanceOwner::Renderer::DrawWit
   // The clip rect is relative to drawable top-left.
   NS_ASSERTION(numClipRects <= 1, "We don't support multiple clip rectangles!");
   nsIntRect clipRect;
   if (numClipRects) {
     clipRect.x = clipRects[0].x;
     clipRect.y = clipRects[0].y;
     clipRect.width  = clipRects[0].width;
     clipRect.height = clipRects[0].height;
+    // NPRect members are unsigned, but clip rectangles should be contained by
+    // the surface.
+    NS_ASSERTION(clipRect.x >= 0 && clipRect.y >= 0,
+                 "Clip rectangle offsets are negative!");
   }
   else {
-    // NPRect members are unsigned, but
-    // we should have been given a clip if an offset is -ve.
-    NS_ASSERTION(offset.x >= 0 && offset.y >= 0,
-                 "Clip rectangle offsets are negative!");
     clipRect.x = offset.x;
     clipRect.y = offset.y;
     clipRect.width  = mWindow->width;
     clipRect.height = mWindow->height;
+    // Don't ask the plugin to draw outside the drawable.
+    // This also ensures that the unsigned clip rectangle offsets won't be -ve.
+    gfxIntSize surfaceSize = xsurface->GetSize();
+    clipRect.IntersectRect(clipRect,
+                           nsIntRect(0, 0,
+                                     surfaceSize.width, surfaceSize.height));
   }
 
   NPRect newClipRect;
   newClipRect.left = clipRect.x;
   newClipRect.top = clipRect.y;
   newClipRect.right = clipRect.XMost();
   newClipRect.bottom = clipRect.YMost();
   if (mWindow->clipRect.left    != newClipRect.left   ||
--- a/widget/src/gtk2/nsNativeThemeGTK.cpp
+++ b/widget/src/gtk2/nsNativeThemeGTK.cpp
@@ -650,16 +650,22 @@ ThemeRenderer::DrawWithGDK(GdkDrawable *
 {
   GdkRectangle gdk_rect = mGDKRect;
   gdk_rect.x += offsetX;
   gdk_rect.y += offsetY;
 
   GdkRectangle gdk_clip = mGDKClip;
   gdk_clip.x += offsetX;
   gdk_clip.y += offsetY;
+
+  GdkRectangle surfaceRect;
+  surfaceRect.x = 0;
+  surfaceRect.y = 0;
+  gdk_drawable_get_size(drawable, &surfaceRect.width, &surfaceRect.height);
+  gdk_rectangle_intersect(&gdk_clip, &surfaceRect, &gdk_clip);
   
   NS_ASSERTION(numClipRects == 0, "We don't support clipping!!!");
   moz_gtk_widget_paint(mGTKWidgetType, drawable, &gdk_rect, &gdk_clip,
                        &mState, mFlags, mDirection);
 
   return NS_OK;
 }