[qpainter] Use drawImage/drawPixmap in fill() for non-repeating sources
authorVladimir Vukicevic <vladimir@pobox.com>
Thu, 01 May 2008 11:54:35 -0700
changeset 16940 476a84e20d5e501b010c6ee7118074a54e4c097d
parent 16939 3c75a8221f6f4d480de0ea4fc0d9a601e0a0ad5a
child 16941 8cf6849a071b2e5e77ead0b592131441f42809e3
push id1298
push userpavlov@mozilla.com
push dateSun, 17 Aug 2008 05:03:09 +0000
treeherderautoland@4a506fa751d8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone2.0a1pre
[qpainter] Use drawImage/drawPixmap in fill() for non-repeating sources
gfx/cairo/cairo/src/cairo-qpainter-surface.cpp
--- a/gfx/cairo/cairo/src/cairo-qpainter-surface.cpp
+++ b/gfx/cairo/cairo/src/cairo-qpainter-surface.cpp
@@ -87,16 +87,20 @@ typedef struct {
     QPixmap *pixmap;
     QImage *image;
 
     QRect window;
 
     bool has_clipping;
     QRect clip_bounds;
 
+    // if this is true, calls to intersect_clip_path won't
+    // update the clip_bounds rect
+    bool no_update_clip_bounds;
+
     cairo_image_surface_t *image_equiv;
 
 #if defined(Q_WS_X11) && defined(CAIRO_HAS_XLIB_XRENDER_SURFACE)
     /* temporary, so that we can share the xlib surface's glyphs code */
     cairo_surface_t *xlib_equiv;
     bool xlib_has_clipping;
     QRect xlib_clip_bounds;
     int xlib_clip_serial;
@@ -649,20 +653,20 @@ static cairo_int_status_t
     extents->width = qs->window.width();
     extents->height = qs->window.height();
 
     return CAIRO_INT_STATUS_SUCCESS;
 }
 
 static cairo_int_status_t
 _cairo_qpainter_surface_intersect_clip_path (void *abstract_surface,
-        cairo_path_fixed_t *path,
-        cairo_fill_rule_t fill_rule,
-        double tolerance,
-        cairo_antialias_t antialias)
+					     cairo_path_fixed_t *path,
+					     cairo_fill_rule_t fill_rule,
+					     double tolerance,
+					     cairo_antialias_t antialias)
 {
     cairo_qpainter_surface_t *qs = (cairo_qpainter_surface_t *) abstract_surface;
 
     D(fprintf(stderr, "q[%p] intersect_clip_path %s\n", abstract_surface, path ? "(path)" : "(clear)"));
 
     if (!qs->p)
         return CAIRO_INT_STATUS_SUCCESS;
 
@@ -672,18 +676,20 @@ static cairo_int_status_t
         if (qs->pixmap || qs->image) {
             // we own p
             qs->p->setClipping (false);
         } else {
             qs->p->restore ();
             qs->p->save ();
         }
 
-	qs->clip_bounds.setRect(0, 0, 0, 0);
-	qs->has_clipping = false;
+	if (!qs->no_update_clip_bounds) {
+	    qs->clip_bounds.setRect(0, 0, 0, 0);
+	    qs->has_clipping = false;
+	}
 
         return CAIRO_INT_STATUS_SUCCESS;
     }
 
     // Qt will implicity enable clipping, and will use ReplaceClip
     // instead of IntersectClip if clipping was disabled before
 
     // Note: Qt is really bad at dealing with clip paths.  It doesn't
@@ -772,23 +778,25 @@ static cairo_int_status_t
 
 	    clip_bounds = qpath.boundingRect().toAlignedRect();
 
 	    // XXX Antialiasing is ignored
 	    qs->p->setClipPath (qpath, Qt::IntersectClip);
 	}
     }
 
-    clip_bounds = qs->p->worldTransform().mapRect(clip_bounds);
+    if (!qs->no_update_clip_bounds) {
+	clip_bounds = qs->p->worldTransform().mapRect(clip_bounds);
 
-    if (qs->has_clipping) {
-	qs->clip_bounds = qs->clip_bounds.intersect(clip_bounds);
-    } else {
-	qs->clip_bounds = clip_bounds;
-	qs->has_clipping = true;
+	if (qs->has_clipping) {
+	    qs->clip_bounds = qs->clip_bounds.intersect(clip_bounds);
+	} else {
+	    qs->clip_bounds = clip_bounds;
+	    qs->has_clipping = true;
+	}
     }
 
     tend("clip");
 
     return CAIRO_INT_STATUS_SUCCESS;
 }
 
 /**
@@ -1137,60 +1145,83 @@ static cairo_int_status_t
                               cairo_operator_t op,
                               cairo_pattern_t *source,
                               cairo_path_fixed_t *path,
                               cairo_fill_rule_t fill_rule,
                               double tolerance,
                               cairo_antialias_t antialias)
 {
     cairo_qpainter_surface_t *qs = (cairo_qpainter_surface_t *) abstract_surface;
+    cairo_qpainter_surface_t *qsSrc = NULL;
 
     D(fprintf(stderr, "q[%p] fill op:%s\n", abstract_surface, _opstr(op)));
 
     if (!qs->p)
         return CAIRO_INT_STATUS_SUCCESS;
 
     tstart();
 
-    QPainterPath qpath;
-    if (_cairo_quartz_cairo_path_to_qpainterpath (path, &qpath, fill_rule) != CAIRO_STATUS_SUCCESS)
-        return CAIRO_INT_STATUS_UNSUPPORTED;
-
     if (qs->supports_porter_duff)
         qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
 
     // XXX Qt4.3, 4.4 misrenders some complex paths if antialiasing is
     // enabled
     //qs->p->setRenderHint (QPainter::Antialiasing, antialias == CAIRO_ANTIALIAS_NONE ? false : true);
     qs->p->setRenderHint (QPainter::SmoothPixmapTransform, source->filter != CAIRO_FILTER_FAST);
 
-    if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
-            source->extend == CAIRO_EXTEND_NONE &&
-            ((cairo_surface_pattern_t*)source)->surface->type == CAIRO_SURFACE_TYPE_QPAINTER)
+    // Check if the source is a qpainter surface for which we have a QImage
+    // or QPixmap
+    if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+	cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) source;
+	if (spattern->surface->type == CAIRO_SURFACE_TYPE_QPAINTER) {
+	    cairo_qpainter_surface_t *p = (cairo_qpainter_surface_t*) spattern->surface;
+
+	    if (p->image || p->pixmap)
+		qsSrc = p;
+	}
+    }
+
+    if (qsSrc && source->extend == CAIRO_EXTEND_NONE)
     {
-        cairo_qpainter_surface_t *qsSrc = (cairo_qpainter_surface_t*) ((cairo_surface_pattern_t*)source)->surface;
+	QMatrix sourceMatrix = _qmatrix_from_cairo_matrix (source->matrix);
+	cairo_int_status_t status;
 
-        QMatrix savedMatrix = qs->p->worldMatrix();
-        cairo_matrix_t m = source->matrix;
-        cairo_matrix_invert (&m);
-        qs->p->setWorldMatrix (_qmatrix_from_cairo_matrix (m), true);
+	// We can draw this faster by clipping and calling drawImage/drawPixmap.
+	// Use our own clipping function so that we can get the
+	// region handling to end up with the fastest possible clip.
+	//
+	// XXX Antialiasing will fail pretty hard here, since we can't clip with AA
+	// with QPainter.
+	qs->p->save();
 
-        if (qsSrc->image) {
-            qs->p->drawImage (0, 0, *qsSrc->image);
-        } else if (qsSrc->pixmap) {
-            qs->p->drawPixmap (0, 0, *qsSrc->pixmap);
-        }
+	qs->no_update_clip_bounds = true;
+	status = _cairo_qpainter_surface_intersect_clip_path (qs, path, fill_rule, tolerance, antialias);
+	qs->no_update_clip_bounds = false;
+
+	if (status != CAIRO_INT_STATUS_SUCCESS) {
+	    qs->p->restore();
+	    return status;
+	}
+
+	qs->p->setWorldMatrix (sourceMatrix.inverted(), true);
 
-        qs->p->setWorldMatrix (savedMatrix, false);
-    } else {
+	if (qsSrc->image) {
+	    qs->p->drawImage (0, 0, *qsSrc->image);
+	} else if (qsSrc->pixmap) {
+	    qs->p->drawPixmap (0, 0, *qsSrc->pixmap);
+	}
 
-    PatternToBrushConverter brush(source);
+	qs->p->restore();
+    } else {
+	QPainterPath qpath;
+	if (_cairo_quartz_cairo_path_to_qpainterpath (path, &qpath, fill_rule) != CAIRO_STATUS_SUCCESS)
+	    return CAIRO_INT_STATUS_UNSUPPORTED;
 
-    qs->p->fillPath (qpath, brush);
-    
+	PatternToBrushConverter brush(source);
+	qs->p->fillPath (qpath, brush);
     }
 
     if (qs->supports_porter_duff)
         qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
 
     tend("fill");
 
     return CAIRO_INT_STATUS_SUCCESS;