mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 05:41:12 +00:00
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:
parent
6abbc7a264
commit
4e96e616ab
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user