diff options
author | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-02-26 23:18:22 +0000 |
---|---|---|
committer | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-02-26 23:18:22 +0000 |
commit | 8ab8ff072cbfb539f594425233a3805367733e42 (patch) | |
tree | 4e55550442aca6b6a3729ca0b121864ca9710735 /chrome | |
parent | 76d2816ad27088432ce9d946facfaa091151dcf6 (diff) | |
download | chromium_src-8ab8ff072cbfb539f594425233a3805367733e42.zip chromium_src-8ab8ff072cbfb539f594425233a3805367733e42.tar.gz chromium_src-8ab8ff072cbfb539f594425233a3805367733e42.tar.bz2 |
Linux: support displays without Xrender support.
VNC servers don't support Xrender. For this use case, we implement a
slow fallback which byte-fiddles the Skia bitmaps as needed to support
32 and 24 bit visuals.
Review URL: http://codereview.chromium.org/27227
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@10523 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/renderer_host/backing_store.h | 15 | ||||
-rw-r--r-- | chrome/browser/renderer_host/backing_store_x.cc | 100 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_widget_host_view_gtk.cc | 3 | ||||
-rw-r--r-- | chrome/common/x11_util.cc | 38 | ||||
-rw-r--r-- | chrome/common/x11_util.h | 4 |
5 files changed, 151 insertions, 9 deletions
diff --git a/chrome/browser/renderer_host/backing_store.h b/chrome/browser/renderer_host/backing_store.h index a74fe5d..14d1e38 100644 --- a/chrome/browser/renderer_host/backing_store.h +++ b/chrome/browser/renderer_host/backing_store.h @@ -37,9 +37,11 @@ class BackingStore { // depth: the depth of the X window which will be drawn into // visual: An Xlib Visual describing the format of the target window // root_window: The X id of the root window + // use_render: if true, the X server supports Xrender // use_shared_memory: if true, the X server is local BackingStore(const gfx::Size& size, Display* x_connection, int depth, - void* visual, XID root_window, bool use_shared_memory); + void* visual, XID root_window, bool use_render, + bool use_shared_memory); // This is for unittesting only. An object constructed using this constructor // will silently ignore all paints explicit BackingStore(const gfx::Size& size); @@ -98,11 +100,22 @@ class BackingStore { #elif defined(OS_MACOSX) skia::PlatformCanvas canvas_; #elif defined(OS_LINUX) + // Paints the bitmap from the renderer onto the backing store without + // using Xrender to composite the pixmaps. + void PaintRectWithoutXrender(TransportDIB* bitmap, + const gfx::Rect& bitmap_rect); + // This is the connection to the X server where this backing store will be // displayed. Display *const display_; // If this is true, then |connection_| is good for MIT-SHM (X shared memory). const bool use_shared_memory_; + // If this is true, then we can use Xrender to composite our pixmaps. + const bool use_render_; + // If |use_render_| is false, this is the number of bits-per-pixel for |depth| + int pixmap_bpp_; + // This is the depth of the target window. + const int visual_depth_; // The parent window (probably a GtkDrawingArea) for this backing store. const XID root_window_; // This is a handle to the server side pixmap which is our backing store. diff --git a/chrome/browser/renderer_host/backing_store_x.cc b/chrome/browser/renderer_host/backing_store_x.cc index e37fe77..75406fa 100644 --- a/chrome/browser/renderer_host/backing_store_x.cc +++ b/chrome/browser/renderer_host/backing_store_x.cc @@ -6,6 +6,7 @@ #include <utility> +#include "base/compiler_specific.h" #include "base/logging.h" #include "chrome/common/transport_dib.h" #include "chrome/common/x11_util.h" @@ -27,19 +28,31 @@ BackingStore::BackingStore(const gfx::Size& size, int depth, void* visual, Drawable root_window, + bool use_render, bool use_shared_memory) : size_(size), display_(display), use_shared_memory_(use_shared_memory), + use_render_(use_render), + visual_depth_(depth), root_window_(root_window) { const int width = size.width(); const int height = size.height(); + COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, assumes_little_endian); + pixmap_ = XCreatePixmap(display_, root_window, width, height, depth); - picture_ = XRenderCreatePicture( - display_, pixmap_, - x11_util::GetRenderVisualFormat(display_, static_cast<Visual*>(visual)), - 0, NULL); + + if (use_render_) { + picture_ = XRenderCreatePicture( + display_, pixmap_, + x11_util::GetRenderVisualFormat(display_, static_cast<Visual*>(visual)), + 0, NULL); + } else { + picture_ = 0; + pixmap_bpp_ = x11_util::BitsPerPixelForPixmapDepth(display, depth); + } + pixmap_gc_ = XCreateGC(display_, pixmap_, 0, NULL); } @@ -47,6 +60,8 @@ BackingStore::BackingStore(const gfx::Size& size) : size_(size), display_(NULL), use_shared_memory_(false), + use_render_(false), + visual_depth_(-1), root_window_(0) { } @@ -60,12 +75,89 @@ BackingStore::~BackingStore() { XFreeGC(display_, static_cast<GC>(pixmap_gc_)); } +void BackingStore::PaintRectWithoutXrender(TransportDIB* bitmap, + const gfx::Rect &bitmap_rect) { + const int width = bitmap_rect.width(); + const int height = bitmap_rect.height(); + Pixmap pixmap = XCreatePixmap(display_, root_window_, width, height, + visual_depth_); + + XImage image; + memset(&image, 0, sizeof(image)); + + image.width = width; + image.height = height; + image.format = ZPixmap; + image.byte_order = LSBFirst; + image.bitmap_unit = 8; + image.bitmap_bit_order = LSBFirst; + image.red_mask = 0xff; + image.green_mask = 0xff00; + image.blue_mask = 0xff0000; + + if (pixmap_bpp_ == 32) { + // If the X server depth is already 32-bits, then our job is easy. + image.depth = visual_depth_; + image.bits_per_pixel = 32; + image.bytes_per_line = width * 4; + image.data = static_cast<char*>(bitmap->memory()); + + XPutImage(display_, pixmap, static_cast<GC>(pixmap_gc_), &image, + 0, 0 /* source x, y */, 0, 0 /* dest x, y */, + width, height); + } else if (pixmap_bpp_ == 24) { + // In this case we just need to strip the alpha channel out of each + // pixel. This is the case which covers VNC servers since they don't + // support Xrender but typically have 24-bit visuals. + // + // It's possible to use some fancy SSE tricks here, but since this is the + // slow path anyway, we do it slowly. + + uint8_t* bitmap24 = static_cast<uint8_t*>(malloc(3 * width * height)); + const uint32_t* bitmap_in = static_cast<const uint32_t*>(bitmap->memory()); + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + const uint32_t pixel = *(bitmap_in++); + bitmap24[0] = (pixel >> 16) & 0xff; + bitmap24[1] = (pixel >> 8) & 0xff; + bitmap24[2] = pixel & 0xff; + bitmap24 += 3; + } + } + + image.depth = visual_depth_; + image.bits_per_pixel = 24; + image.bytes_per_line = width * 3; + image.data = reinterpret_cast<char*>(bitmap24); + + XPutImage(display_, pixmap, static_cast<GC>(pixmap_gc_), &image, + 0, 0 /* source x, y */, 0, 0 /* dest x, y */, + width, height); + + free(bitmap24); + } else { + CHECK(false) << "Sorry, we don't support your visual depth without " + "Xrender support (depth:" << visual_depth_ + << " bpp:" << pixmap_bpp_ << ")"; + } + + XCopyArea(display_, pixmap /* source */, pixmap_ /* target */, + static_cast<GC>(pixmap_gc_), + 0, 0 /* source x, y */, bitmap_rect.width(), bitmap_rect.height(), + bitmap_rect.x(), bitmap_rect.y() /* dest x, y */); + + XFreePixmap(display_, pixmap); +} + void BackingStore::PaintRect(base::ProcessHandle process, TransportDIB* bitmap, const gfx::Rect& bitmap_rect) { if (!display_) return; + if (!use_render_) + return PaintRectWithoutXrender(bitmap, bitmap_rect); + const int width = bitmap_rect.width(); const int height = bitmap_rect.height(); Picture picture; diff --git a/chrome/browser/renderer_host/render_widget_host_view_gtk.cc b/chrome/browser/renderer_host/render_widget_host_view_gtk.cc index 4b5f19e..23f3067 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_gtk.cc +++ b/chrome/browser/renderer_host/render_widget_host_view_gtk.cc @@ -288,11 +288,12 @@ BackingStore* RenderWidgetHostViewGtk::AllocBackingStore( Display* display = x11_util::GetXDisplay(); void* visual = x11_util::GetVisualFromGtkWidget(view_); XID root_window = x11_util::GetX11RootWindow(); + bool use_render = x11_util::QueryRenderSupport(display); bool use_shared_memory = x11_util::QuerySharedMemorySupport(display); int depth = gtk_widget_get_visual(view_)->depth; return new BackingStore(size, display, depth, visual, root_window, - use_shared_memory); + use_render, use_shared_memory); } void RenderWidgetHostViewGtk::Paint(const gfx::Rect& damage_rect) { diff --git a/chrome/common/x11_util.cc b/chrome/common/x11_util.cc index 264ab9c..d10c320 100644 --- a/chrome/common/x11_util.cc +++ b/chrome/common/x11_util.cc @@ -82,6 +82,22 @@ bool QuerySharedMemorySupport(Display* dpy) { return shared_memory_support; } +bool QueryRenderSupport(Display* dpy) { + static bool render_supported = false; + static bool render_supported_cached = false; + + if (render_supported_cached) + return render_supported; + + // We don't care about the version of Xrender since all the features which + // we use are included in every version. + int dummy; + render_supported = XRenderQueryExtension(dpy, &dummy, &dummy); + render_supported_cached = true; + + return render_supported; +} + XID GetX11RootWindow() { return GDK_WINDOW_XID(gdk_get_default_root_window()); } @@ -94,14 +110,30 @@ void* GetVisualFromGtkWidget(GtkWidget* widget) { return GDK_VISUAL_XVISUAL(gtk_widget_get_visual(widget)); } +int BitsPerPixelForPixmapDepth(Display* dpy, int depth) { + int count; + XPixmapFormatValues* formats = XListPixmapFormats(dpy, &count); + if (!formats) + return -1; + + int bits_per_pixel = -1; + for (int i = 0; i < count; ++i) { + if (formats[i].depth == depth) { + bits_per_pixel = formats[i].bits_per_pixel; + break; + } + } + + XFree(formats); + return bits_per_pixel; +} + XRenderPictFormat* GetRenderVisualFormat(Display* dpy, Visual* visual) { static XRenderPictFormat* pictformat = NULL; if (pictformat) return pictformat; - int dummy; - if (!XRenderQueryExtension(dpy, &dummy, &dummy)) - CHECK(false) << "XRENDER not supported on display"; + DCHECK(QueryRenderSupport(dpy)); pictformat = XRenderFindVisualFormat(dpy, visual); CHECK(pictformat) << "XRENDER does not support default visual"; diff --git a/chrome/common/x11_util.h b/chrome/common/x11_util.h index 6e5072b..4800a69 100644 --- a/chrome/common/x11_util.h +++ b/chrome/common/x11_util.h @@ -27,6 +27,8 @@ namespace x11_util { Display* GetXDisplay(); // Return true iff the connection supports X shared memory bool QuerySharedMemorySupport(Display* dpy); + // Return true iff the display supports Xrender + bool QueryRenderSupport(Display* dpy); // These functions do not cache their results @@ -37,6 +39,8 @@ namespace x11_util { // Get a Visual from the given widget. Since we don't include the Xlib // headers, this is returned as a void*. void* GetVisualFromGtkWidget(GtkWidget*); + // Return the number of bits-per-pixel for a pixmap of the given depth + int BitsPerPixelForPixmapDepth(Display*, int depth); // Return a handle to a server side pixmap. |shared_memory_key| is a SysV // IPC key. The shared memory region must contain 32-bit pixels. |