Bug 333250. Speed up native theme rendering in cairo by a) not having a clip rect set on the backbuffer surface, so we don't usually don't need to clip native theme rendering and b) creating the backbuffer with GDK so that we don't need to create a GdkPixmap with expensive XGetGeometry round-trip every time a theme wants to draw into it. r=vlad

This commit is contained in:
roc+%cs.cmu.edu 2006-04-12 21:58:14 +00:00
parent 6abbc7a264
commit 4e96e616ab
3 changed files with 111 additions and 54 deletions

View File

@ -55,15 +55,22 @@
#define CAIRO_XLIB_DRAWING_NOTE(m) do {} while (0)
#endif
static cairo_surface_t *_get_current_target (cairo_t *cr, double *dx, double *dy)
static cairo_surface_t *_get_current_target (cairo_t *cr,
double *device_offset_x,
double *device_offset_y)
{
cairo_surface_t *target = cairo_get_group_target (cr, dx, dy);
if (target == NULL) {
target = cairo_get_target (cr);
*dx = 0.0;
*dy = 0.0;
}
return target;
cairo_surface_t *target = cairo_get_target (cr);
double group_device_offset_x, group_device_offset_y;
cairo_surface_t *group_target =
cairo_get_group_target (cr, &group_device_offset_x, &group_device_offset_y);
cairo_surface_get_device_offset (target, device_offset_x, device_offset_y);
if (group_target == NULL)
return target;
*device_offset_x += group_device_offset_x;
*device_offset_y += group_device_offset_y;
return group_target;
}
static cairo_user_data_key_t pixmap_free_key;
@ -224,8 +231,8 @@ _draw_with_xlib_direct (cairo_t *cr,
}
/* Check that the matrix translation offsets (adjusted for
device offset) are integers */
if (!_convert_coord_to_short (matrix.x0 - device_offset_x, &offset_x) ||
!_convert_coord_to_short (matrix.y0 - device_offset_y, &offset_y)) {
if (!_convert_coord_to_short (matrix.x0 + device_offset_x, &offset_x) ||
!_convert_coord_to_short (matrix.y0 + device_offset_y, &offset_y)) {
CAIRO_XLIB_DRAWING_NOTE("TAKING SLOW PATH: non-integer offset\n");
return False;
}

View File

@ -454,17 +454,11 @@ ThemeRenderer::NativeDraw(Display* dpy, Drawable drawable, Visual* visual,
GdkRectangle gdk_clip = mGDKClip;
gdk_clip.x += offsetX;
gdk_clip.y += offsetY;
if (numClipRects > 0) {
NS_ASSERTION(numClipRects == 1, "gfxXlibNativeRenderer cheated on us");
GdkRectangle extraClip = {clipRects->x, clipRects->y,
clipRects->width, clipRects->height};
if (!gdk_rectangle_intersect(&gdk_clip, &extraClip, &gdk_clip))
return NS_OK;
}
GdkDisplay* gdkDpy = gdk_x11_lookup_xdisplay(dpy);
if (!gdkDpy)
return NS_ERROR_FAILURE;
GdkPixmap* gdkPixmap = gdk_pixmap_lookup_for_display(gdkDpy, drawable);
if (gdkPixmap) {
g_object_ref(G_OBJECT(gdkPixmap));
@ -483,6 +477,7 @@ ThemeRenderer::NativeDraw(Display* dpy, Drawable drawable, Visual* visual,
}
}
NS_ASSERTION(numClipRects == 0, "We don't support clipping!!!");
moz_gtk_widget_paint(mGTKWidgetType, gdkPixmap, &gdk_rect, &gdk_clip, &mState,
mFlags);
@ -592,8 +587,7 @@ nsNativeThemeGTK::DrawWidgetBackground(nsIRenderingContext* aContext,
// This is the rectangle that will actually be drawn, in appunits
nsRect drawingRect(aClipRect);
nsIntMargin extraSize;
// remember whether the widget might draw outside its given clip rect
PRBool overDrawing = GetExtraSizeForWidget(aWidgetType, &extraSize);
GetExtraSizeForWidget(aWidgetType, &extraSize);
// inflate drawing rect to account for the overdraw
nsMargin extraSizeInTwips(NSToCoordRound(extraSize.left*p2t),
NSToCoordRound(extraSize.top*p2t),
@ -615,13 +609,13 @@ nsNativeThemeGTK::DrawWidgetBackground(nsIRenderingContext* aContext,
oldHandler = XSetErrorHandler(NativeThemeErrorHandler);
}
// We do not support clip lists (because we can't currently tweak the actual
// Gdk GC used), and we require the use of the default display and visual
// We require the use of the default display and visual
// because I'm afraid that otherwise the GTK theme may explode.
// Some themes (e.g. Clearlooks) just don't clip properly to any
// clip rect we provide, so we cannot advertise support for clipping within the
// widget bounds. The gdk_clip is just advisory here, meanining "you don't
// need to draw outside this rect if you don't feel like it!"
PRUint32 rendererFlags = gfxXlibNativeRenderer::DRAW_SUPPORTS_OFFSET;
if (!overDrawing) {
rendererFlags |= gfxXlibNativeRenderer::DRAW_SUPPORTS_CLIP_RECT;
}
GdkRectangle gdk_rect = ConvertToGdkRect(aRect - drawingRect.TopLeft(), t2p);
GdkRectangle gdk_clip = ConvertToGdkRect(aClipRect - drawingRect.TopLeft(), t2p);
ThemeRenderer renderer(state, gtkWidgetType, flags, gdk_rect, gdk_clip);

View File

@ -1458,34 +1458,73 @@ nsWindow::OnExposeEvent(GtkWidget *aWidget, GdkEventExpose *aEvent)
#ifdef MOZ_CAIRO_GFX
PRBool translucent;
GetWindowTranslucency(translucent);
GdkRectangle collapsedRect;
nsIntRect boundsRect;
GdkPixmap* bufferPixmap = nsnull;
nsRefPtr<gfxXlibSurface> bufferPixmapSurface;
updateRegion->GetBoundingBox(&boundsRect.x, &boundsRect.y,
&boundsRect.width, &boundsRect.height);
// do double-buffering and clipping here
nsRefPtr<gfxContext> ctx =
(gfxContext*)rc->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT);
nsRefPtr<gfxContext> ctx
= (gfxContext*)rc->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT);
ctx->Save();
// clip to Gdk region
ctx->NewPath();
if (translucent) {
// Collapse update area to the bounding box. This is so we only have to
// call UpdateTranslucentWindowAlpha once. After we have dropped
// support for non-Thebes graphics, UpdateTranslucentWindowAlpha will be
// our private interface so we can rework things to avoid this.
updateRegion->GetBoundingBox(&collapsedRect.x, &collapsedRect.y,
&collapsedRect.width, &collapsedRect.height);
ctx->Rectangle(gfxRect(collapsedRect.x, collapsedRect.y,
collapsedRect.width, collapsedRect.height));
// Collapse update area to the bounding box. This is so we only have to
// call UpdateTranslucentWindowAlpha once. After we have dropped
// support for non-Thebes graphics, UpdateTranslucentWindowAlpha will be
// our private interface so we can rework things to avoid this.
ctx->Rectangle(gfxRect(boundsRect.x, boundsRect.y,
boundsRect.width, boundsRect.height));
} else {
for (r = rects; r < r_end; ++r) {
ctx->Rectangle(gfxRect(r->x, r->y, r->width, r->height));
}
for (r = rects; r < r_end; ++r) {
ctx->Rectangle(gfxRect(r->x, r->y, r->width, r->height));
}
}
ctx->Clip();
// double buffer
ctx->PushGroup(translucent ? gfxContext::CONTENT_COLOR_ALPHA : gfxContext::CONTENT_COLOR);
if (translucent) {
ctx->PushGroup(gfxContext::CONTENT_COLOR_ALPHA);
} else {
#ifdef MOZ_ENABLE_GLITZ
ctx->PushGroup(gfxContext::CONTENT_COLOR);
#else
// Instead of just doing PushGroup we're going to do a little dance
// to ensure that GDK creates the pixmap, so it doesn't go all
// XGetGeometry on us in gdk_pixmap_foreign_new_for_display when we
// paint native themes
GdkDrawable* d = GDK_DRAWABLE(mDrawingarea->inner_window);
gint depth = gdk_drawable_get_depth(d);
bufferPixmap = gdk_pixmap_new(d, boundsRect.width, boundsRect.height, depth);
if (bufferPixmap) {
GdkVisual* visual = gdk_drawable_get_visual(GDK_DRAWABLE(bufferPixmap));
Visual* XVisual = gdk_x11_visual_get_xvisual(visual);
Display* display = gdk_x11_drawable_get_xdisplay(GDK_DRAWABLE(bufferPixmap));
Drawable drawable = gdk_x11_drawable_get_xid(GDK_DRAWABLE(bufferPixmap));
bufferPixmapSurface =
new gfxXlibSurface(display, drawable, XVisual,
boundsRect.width, boundsRect.height);
if (bufferPixmapSurface) {
bufferPixmapSurface->SetDeviceOffset(-boundsRect.x, -boundsRect.y);
nsCOMPtr<nsIRenderingContext> newRC;
nsresult rv = GetDeviceContext()->
CreateRenderingContextInstance(*getter_AddRefs(newRC));
if (NS_FAILED(rv)) {
bufferPixmapSurface = nsnull;
} else {
rv = newRC->Init(GetDeviceContext(), bufferPixmapSurface);
if (NS_FAILED(rv)) {
bufferPixmapSurface = nsnull;
} else {
rc = newRC;
}
}
}
}
#endif
}
#endif
nsPaintEvent event(PR_TRUE, NS_PAINT, this);
@ -1500,32 +1539,49 @@ nsWindow::OnExposeEvent(GtkWidget *aWidget, GdkEventExpose *aEvent)
#ifdef MOZ_CAIRO_GFX
if (status != nsEventStatus_eIgnore) {
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
if (!translucent) {
ctx->PopGroupToSource();
ctx->Paint();
} else {
if (translucent) {
nsRefPtr<gfxPattern> pattern = ctx->PopGroup();
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
ctx->SetPattern(pattern);
ctx->Paint();
nsRefPtr<gfxImageSurface> img =
new gfxImageSurface(gfxImageSurface::ImageFormatA8,
collapsedRect.width, collapsedRect.height);
img->SetDeviceOffset(-collapsedRect.x, -collapsedRect.y);
boundsRect.width, boundsRect.height);
img->SetDeviceOffset(-boundsRect.x, -boundsRect.y);
nsRefPtr<gfxContext> imgCtx = new gfxContext(img);
imgCtx->SetPattern(pattern);
imgCtx->SetOperator(gfxContext::OPERATOR_SOURCE);
imgCtx->Paint();
UpdateTranslucentWindowAlphaInternal(nsRect(collapsedRect.x, collapsedRect.y,
collapsedRect.width, collapsedRect.height),
UpdateTranslucentWindowAlphaInternal(nsRect(boundsRect.x, boundsRect.y,
boundsRect.width, boundsRect.height),
img->Data(), img->Stride());
} else {
#ifdef MOZ_ENABLE_GLITZ
ctx->PopGroupToSource();
ctx->Paint();
#else
if (bufferPixmapSurface) {
ctx->SetSource(bufferPixmapSurface);
ctx->Paint();
}
#endif
}
} else {
// ignore
ctx->PopGroup();
if (translucent) {
ctx->PopGroup();
} else {
#ifdef MOZ_ENABLE_GLITZ
ctx->PopGroup();
#endif
}
}
if (bufferPixmap) {
g_object_unref(G_OBJECT(bufferPixmap));
}
ctx->Restore();