* * * diff --git a/gfx/thebes/public/gfxPlatform.h b/gfx/thebes/public/gfxPlatform.h --- a/gfx/thebes/public/gfxPlatform.h +++ b/gfx/thebes/public/gfxPlatform.h @@ -296,6 +296,12 @@ public: */ static cmsHTRANSFORM GetCMSRGBATransform(); + /** + * If true, image surfaces should be favored over platform-native + * surfaces if possible. + */ + static PRBool UseImageSurfaces(); + protected: gfxPlatform() { } virtual ~gfxPlatform(); diff --git a/gfx/thebes/src/gfxPlatform.cpp b/gfx/thebes/src/gfxPlatform.cpp --- a/gfx/thebes/src/gfxPlatform.cpp +++ b/gfx/thebes/src/gfxPlatform.cpp @@ -92,6 +92,8 @@ static const char *CMProfilePrefName = " static const char *CMProfilePrefName = "gfx.color_management.display_profile"; static const char *CMForceSRGBPrefName = "gfx.color_management.force_srgb"; +static PRBool gUseImageSurfaces = PR_FALSE; + static void ShutdownCMS(); static void MigratePrefs(); @@ -234,6 +236,19 @@ gfxPlatform::Init() #endif } +#if defined(WINCE) || defined(MOZ_PLATFORM_HILDON) + gUseImageSurfaces = PR_TRUE; +#endif + + char *s = getenv("MOZ_USE_IMAGE_SURFACES"); + if (s) { + if (s[0] == '0') { + gUseImageSurfaces = PR_FALSE; + } else { + gUseImageSurfaces = PR_TRUE; + } + } + return NS_OK; } @@ -286,6 +301,9 @@ gfxPlatform::OptimizeImage(gfxImageSurfa gfxPlatform::OptimizeImage(gfxImageSurface *aSurface, gfxASurface::gfxImageFormat format) { + if (gUseImageSurfaces) + return nsnull; + const gfxIntSize& surfaceSize = aSurface->GetSize(); nsRefPtr optSurface = CreateOffscreenSurface(surfaceSize, format); @@ -763,3 +781,9 @@ static void MigratePrefs() } } + +PRBool +gfxPlatform::UseImageSurfaces() +{ + return gUseImageSurfaces; +} diff --git a/gfx/thebes/src/gfxPlatformGtk.cpp b/gfx/thebes/src/gfxPlatformGtk.cpp --- a/gfx/thebes/src/gfxPlatformGtk.cpp +++ b/gfx/thebes/src/gfxPlatformGtk.cpp @@ -152,6 +152,12 @@ gfxPlatformGtk::CreateOffscreenSurface(c gfxASurface::gfxImageFormat imageFormat) { nsRefPtr newSurface = nsnull; + + if (UseImageSurfaces()) { + newSurface = new gfxImageSurface(size, imageFormat); + return newSurface.forget(); + } + PRBool sizeOk = PR_TRUE; if (size.width >= GDK_PIXMAP_SIZE_MAX || diff --git a/gfx/thebes/src/gfxWindowsPlatform.cpp b/gfx/thebes/src/gfxWindowsPlatform.cpp --- a/gfx/thebes/src/gfxWindowsPlatform.cpp +++ b/gfx/thebes/src/gfxWindowsPlatform.cpp @@ -132,11 +132,14 @@ gfxWindowsPlatform::CreateOffscreenSurfa gfxWindowsPlatform::CreateOffscreenSurface(const gfxIntSize& size, gfxASurface::gfxImageFormat imageFormat) { -#ifndef WINCE - gfxASurface *surf = new gfxWindowsSurface(size, imageFormat); -#else - gfxASurface *surf = new gfxImageSurface(size, imageFormat); -#endif + gfxASurface *surf = nsnull; + + if (UseImageSurfaces()) { + gfxASurface *surf = new gfxImageSurface(size, imageFormat); + } else { + gfxASurface *surf = new gfxWindowsSurface(size, imageFormat); + } + NS_IF_ADDREF(surf); return surf; } diff --git a/widget/src/gtk2/Makefile.in b/widget/src/gtk2/Makefile.in --- a/widget/src/gtk2/Makefile.in +++ b/widget/src/gtk2/Makefile.in @@ -69,6 +69,7 @@ REQUIRES = xpcom \ locale \ thebes \ cairo \ + libpixman \ $(NULL) ifdef MOZ_X11 diff --git a/widget/src/gtk2/nsWindow.cpp b/widget/src/gtk2/nsWindow.cpp --- a/widget/src/gtk2/nsWindow.cpp +++ b/widget/src/gtk2/nsWindow.cpp @@ -117,6 +117,18 @@ static const char sAccessibilityKey [] = #ifdef MOZ_X11 #include "gfxXlibSurface.h" + +#include +#include +#include +#include +#include + +#include + +extern "C" { +#include "pixman.h" +} #endif #if defined(MOZ_PLATFORM_HILDON) && defined(MOZ_ENABLE_GCONF) @@ -348,6 +360,18 @@ static gfxIntSize gBufferPixmapMaxSize(0 static gfxIntSize gBufferPixmapMaxSize(0,0); static int gBufferPixmapUsageCount = 0; +#ifdef MOZ_X11 +static PRBool gUseSHM = PR_TRUE; +static XImage *gXImage = nsnull; +static XShmSegmentInfo gShmInfo; +static nsAutoPtr gShmSurfaceData; +static pixman_image_t *gSrcPixmanImage = NULL; +static pixman_image_t *gDstPixmanImage = NULL; +static PRBool gShmTargetNeedsDoubleBuffer = PR_FALSE; + +static already_AddRefed EnsureSHMImage(); +#endif + // imported in nsWidgetFactory.cpp PRBool gDisableNativeTheme = PR_FALSE; @@ -664,6 +688,25 @@ nsWindow::Destroy(void) gBufferPixmap = nsnull; gBufferPixmapSize.width = 0; gBufferPixmapSize.height = 0; + + if (gXImage) { + gShmSurfaceData = nsnull; + XShmDetach(gdk_x11_get_default_xdisplay(), &gShmInfo); + XDestroyImage(gXImage); + shmdt(gShmInfo.shmaddr); + shmctl(gShmInfo.shmid, IPC_RMID, 0); + gXImage = nsnull; + + if (gSrcPixmanImage) { + pixman_image_unref(gSrcPixmanImage); + gSrcPixmanImage = nsnull; + } + + if (gDstPixmanImage) { + pixman_image_unref(gDstPixmanImage); + gDstPixmanImage = nsnull; + } + } } g_signal_handlers_disconnect_by_func(gtk_settings_get_default(), @@ -2171,12 +2214,6 @@ nsWindow::OnExposeEvent(GtkWidget *aWidg #endif #ifdef MOZ_X11 - nsCOMPtr rc = getter_AddRefs(GetRenderingContext()); - if (NS_UNLIKELY(!rc)) { - g_free(rects); - return FALSE; - } - PRBool translucent; translucent = eTransparencyTransparent == GetTransparencyMode(); nsIntRect boundsRect; @@ -2189,8 +2226,85 @@ nsWindow::OnExposeEvent(GtkWidget *aWidg updateRegion->GetBoundingBox(&boundsRect.x, &boundsRect.y, &boundsRect.width, &boundsRect.height); + nsCOMPtr rc; + nsRefPtr targetSurface; + nsRefPtr ctx; + + nsIntPoint screenPos(WidgetToScreenOffset()); + + if (gUseSHM && !translucent && + boundsRect.width <= gBufferPixmapSize.width && + boundsRect.height <= gBufferPixmapSize.height && + (targetSurface = EnsureSHMImage()) != nsnull) + { + targetSurface->SetDeviceOffset(gfxPoint(screenPos.x, screenPos.y)); + + nsresult rv = mContext->CreateRenderingContextInstance (*getter_AddRefs(rc)); + if (NS_FAILED(rv)) { + NS_WARNING("CreateRenderingContextInstance failed"); + return PR_FALSE; + } + + ctx = new gfxContext(targetSurface); + + rv = rc->Init(mContext, ctx); + if (NS_FAILED(rv)) { + NS_WARNING("RC::Init failed"); + return PR_FALSE; + } + + // first add all the update regions + for (r = rects; r < r_end; ++r) + ctx->Rectangle(gfxRect(r->x, r->y, r->width, r->height)); + + // This doesn't seem to be needed, but it's left here if we do + // end up needing it. We might also need to subtract toplevel + // windows that overlay us as well. If we do need this, it + // would probably be better to take updateRegion and subtract + // all the negative rects, then pull out the optimized set of + // rects and clip to those. +#if 0 + // subtract all the bounds rectangles of our children + for (nsIWidget *child = mFirstChild; child; child = child->GetNextSibling()) { + const nsIntRect& bounds = static_cast(child)->mBounds; + + // default fill rule is winding, so we do this CCW to create holes + // these bounds are all relative to us + ctx->MoveTo(gfxPoint(bounds.x, bounds.y)); + ctx->LineTo(gfxPoint(bounds.x, bounds.y + bounds.height)); + ctx->LineTo(gfxPoint(bounds.x + bounds.width, bounds.y + bounds.height)); + ctx->LineTo(gfxPoint(bounds.x + bounds.width, bounds.y)); + ctx->ClosePath(); + } +#endif + + // and finally Clip + ctx->Clip(); + + ctx->SetFillRule(gfxContext::FILL_RULE_WINDING); + + ctx->UpdateSurfaceClip(); + + // this Save is only needed to match the corresponding Restore at the end; + // we should get rid of it once we clean up this function + ctx->Save(); + + // if we're rendering straight to the shm area, double buffer so that we don't + // get partial updates + if (gShmTargetNeedsDoubleBuffer) { + ctx->PushGroup(gfxASurface::CONTENT_COLOR); + } + } + + if (!rc) { + rc = getter_AddRefs(GetRenderingContext()); + if (NS_UNLIKELY(!rc)) { + g_free(rects); + return FALSE; + } + // do double-buffering and clipping here - nsRefPtr ctx = rc->ThebesContext(); + ctx = rc->ThebesContext(); ctx->Save(); ctx->NewPath(); if (translucent) { @@ -2302,6 +2416,8 @@ nsWindow::OnExposeEvent(GtkWidget *aWidg #endif #endif + } + #endif // MOZ_X11 nsPaintEvent event(PR_TRUE, NS_PAINT, this); @@ -2314,11 +2430,90 @@ nsWindow::OnExposeEvent(GtkWidget *aWidg nsEventStatus status; DispatchEvent(&event, status); + if (status == nsEventStatus_eIgnore && !gShmTargetNeedsDoubleBuffer) { + // this is somewhat bad -- we drew straight into our back buffer, + // but we were told to ignore this. Let's hope that we didn't actually + // draw anything. + + //fprintf (stderr, "got eIgnore for update at %d,%d %dx%d (@%d,%d)!\n", + // boundsRect.x, boundsRect.y, boundsRect.width, boundsRect.height, screenPos.x, screenPos.y); + } + #ifdef MOZ_X11 // DispatchEvent can Destroy us (bug 378273), avoid doing any paint // operations below if that happened - it will lead to XError and exit(). if (NS_LIKELY(!mIsDestroyed)) { - if (status != nsEventStatus_eIgnore) { + if (targetSurface) { + if (status != nsEventStatus_eIgnore) { + GdkDrawable* d; + gint dx, dy; + gint sx = screenPos.x, sy = screenPos.y; + gint w = boundsRect.width, h = boundsRect.height; + gdk_window_get_internal_paint_info(mDrawingarea->inner_window, + &d, &dx, &dy); + + sx += boundsRect.x; + sy += boundsRect.y; + + dx += boundsRect.x; + dy += boundsRect.y; + + if (sx < 0) { + w += sx; + sx = 0; + } + + if (sy < 0) { + h += sy; + sy = 0; + } + + if (sx + w > gXImage->width) + w = gXImage->width - sx; + + if (sy + h > gXImage->height) + h = gXImage->height - sy; + + if (gShmTargetNeedsDoubleBuffer) { + // we had to double buffer, so drop the chunk we just rendered into place. + nsRefPtr pattern = ctx->PopGroup(); + ctx->SetOperator(gfxContext::OPERATOR_SOURCE); + ctx->SetPattern(pattern); + ctx->Paint(); + } + + if (gSrcPixmanImage && gDstPixmanImage) { + // We had pixman images created, so blit from + // source to dest. This will do 24bpp to 16bpp + // conversion if necessary. Only convert the + // rects that we actually rendered, not the entire + // bounding rectangle since that might contain + // unrendered pieces. + for (r = rects; r < r_end; r++) { + gint rsx = screenPos.x + r->x; + gint rsy = screenPos.y + r->y; + pixman_image_composite(PIXMAN_OP_SRC, + gSrcPixmanImage, + NULL, + gDstPixmanImage, + rsx, rsy, + 0, 0, + rsx, rsy, + r->width, r->height); + } + } + + + GC gc = XCreateGC(gdk_x11_get_default_xdisplay(), GDK_DRAWABLE_XID(d), 0, 0); + XShmPutImage(gdk_x11_get_default_xdisplay(), GDK_DRAWABLE_XID(d), gc, + gXImage, + sx, sy, + dx, dy, + w, h, + False); + XFreeGC(gdk_x11_get_default_xdisplay(), gc); + } + } else if (status != nsEventStatus_eIgnore) { if (translucent) { nsRefPtr pattern = ctx->PopGroup(); ctx->SetOperator(gfxContext::OPERATOR_SOURCE); @@ -7153,3 +7348,81 @@ nsWindow::BeginResizeDrag(nsGUIEvent* aE return NS_OK; } + +/*static*/ already_AddRefed +EnsureSHMImage() +{ + XVisualInfo vinfo; + + if (!gXImage) { + int foundVisual = XMatchVisualInfo(gdk_x11_get_default_xdisplay(), gdk_x11_get_default_screen(), + XDefaultDepth(gdk_x11_get_default_xdisplay(), gdk_x11_get_default_screen()), + TrueColor, &vinfo); + if (!foundVisual) + return nsnull; + + gXImage = XShmCreateImage(gdk_x11_get_default_xdisplay(), + vinfo.visual, + vinfo.depth, + ZPixmap, 0, &gShmInfo, + gBufferPixmapMaxSize.width, + gBufferPixmapMaxSize.height); + + if (!gXImage) + return nsnull; + + gShmInfo.shmid = shmget(IPC_PRIVATE, gXImage->bytes_per_line * gXImage->height, IPC_CREAT | 0777); + gShmInfo.shmaddr = gXImage->data = (char*) shmat(gShmInfo.shmid, 0, 0); + gShmInfo.readOnly = False; + + if (!XShmAttach(gdk_x11_get_default_xdisplay(), &gShmInfo)) { + XDestroyImage(gXImage); + gXImage = nsnull; + + return nsnull; + } + + if (gXImage->depth != 24 || getenv("MOZ_X11_SHM_FORCE_PIXMAN")) { + gShmSurfaceData = new PRUint8[gXImage->width * gXImage->height * 4]; + + gSrcPixmanImage = pixman_image_create_bits(PIXMAN_x8r8g8b8, + gBufferPixmapMaxSize.width, gBufferPixmapMaxSize.height, + (uint32_t*)gShmSurfaceData.get(), + gBufferPixmapMaxSize.width * 4); + + pixman_format_code_t dstfmt; + switch (gXImage->depth) { + case 16: dstfmt = PIXMAN_r5g6b5; break; + case 24: dstfmt = PIXMAN_x8r8g8b8; break; + default: + NS_ASSERTION(0, "Don't know how to create pixman dst for your depth while doing shm drawing!"); + return nsnull; + } + + gDstPixmanImage = pixman_image_create_bits(dstfmt, + gBufferPixmapMaxSize.width, gBufferPixmapMaxSize.height, + (uint32_t*) gXImage->data, + gXImage->bytes_per_line); + gShmTargetNeedsDoubleBuffer = PR_FALSE; + } else { + gShmSurfaceData = nsnull; + gShmTargetNeedsDoubleBuffer = PR_TRUE; + } + } + + nsRefPtr surf; + + if (gShmSurfaceData) { + surf = new gfxImageSurface(gShmSurfaceData, + gBufferPixmapMaxSize, + gBufferPixmapMaxSize.width * 4, + gfxASurface::ImageFormatRGB24); + } else { + surf = new gfxImageSurface((unsigned char*) gXImage->data, + gBufferPixmapMaxSize, + gXImage->bytes_per_line, + gfxASurface::ImageFormatRGB24); + } + + return surf.forget(); +}