| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "content/browser/renderer_host/backing_store_gtk.h" | |
| 6 | |
| 7 #include <cairo-xlib.h> | |
| 8 #include <gtk/gtk.h> | |
| 9 #include <stdlib.h> | |
| 10 #include <sys/ipc.h> | |
| 11 #include <sys/shm.h> | |
| 12 #include <X11/extensions/sync.h> | |
| 13 | |
| 14 #if defined(OS_OPENBSD) || defined(OS_FREEBSD) | |
| 15 #include <sys/endian.h> | |
| 16 #endif | |
| 17 | |
| 18 #include <algorithm> | |
| 19 #include <limits> | |
| 20 #include <queue> | |
| 21 #include <utility> | |
| 22 | |
| 23 #include "base/compiler_specific.h" | |
| 24 #include "base/logging.h" | |
| 25 #include "base/memory/singleton.h" | |
| 26 #include "base/metrics/histogram.h" | |
| 27 #include "base/time/time.h" | |
| 28 #include "content/browser/renderer_host/render_process_host_impl.h" | |
| 29 #include "skia/ext/platform_canvas.h" | |
| 30 #include "third_party/skia/include/core/SkBitmap.h" | |
| 31 #include "ui/base/gtk/gtk_signal.h" | |
| 32 #include "ui/base/x/x11_util_internal.h" | |
| 33 #include "ui/gfx/rect.h" | |
| 34 #include "ui/gfx/rect_conversions.h" | |
| 35 #include "ui/gfx/x/x11_types.h" | |
| 36 #include "ui/surface/transport_dib.h" | |
| 37 | |
| 38 namespace content { | |
| 39 namespace { | |
| 40 | |
| 41 // Assume that somewhere along the line, someone will do width * height * 4 | |
| 42 // with signed numbers. If the maximum value is 2**31, then 2**31 / 4 = | |
| 43 // 2**29 and floor(sqrt(2**29)) = 23170. | |
| 44 | |
| 45 // Max height and width for layers | |
| 46 static const int kMaxVideoLayerSize = 23170; | |
| 47 | |
| 48 | |
| 49 // X Backing Stores: | |
| 50 // | |
| 51 // Unlike Windows, where the backing store is kept in heap memory, we keep our | |
| 52 // backing store in the X server, as a pixmap. Thus expose events just require | |
| 53 // instructing the X server to copy from the backing store to the window. | |
| 54 // | |
| 55 // The backing store is in the same format as the visual which our main window | |
| 56 // is using. Bitmaps from the renderer are uploaded to the X server, either via | |
| 57 // shared memory or over the wire, and XRENDER is used to convert them to the | |
| 58 // correct format for the backing store. | |
| 59 | |
| 60 // Destroys the image and the associated shared memory structures. This is a | |
| 61 // helper function for code using shared memory. | |
| 62 void DestroySharedImage(XDisplay* display, | |
| 63 XImage* image, | |
| 64 XShmSegmentInfo* shminfo) { | |
| 65 XShmDetach(display, shminfo); | |
| 66 XDestroyImage(image); | |
| 67 shmdt(shminfo->shmaddr); | |
| 68 } | |
| 69 | |
| 70 // So we don't don't want to call XSync(), which can block the UI loop for | |
| 71 // ~100ms on first paint and is generally slow. We optionally use the | |
| 72 // XSyncExtension to push a callback into the X11 event queue and get a | |
| 73 // callback instead of blocking until the event queue is cleared. | |
| 74 // | |
| 75 // TODO(erg): If gfx::GetXDisplay() ever gets fixed to handle multiple Displays, | |
| 76 // this must be modified to be per Display instead of a Singleton. | |
| 77 class XSyncHandler { | |
| 78 public: | |
| 79 static XSyncHandler* GetInstance() { | |
| 80 return Singleton<XSyncHandler>::get(); | |
| 81 } | |
| 82 | |
| 83 bool Enabled() { | |
| 84 return loaded_extension_; | |
| 85 } | |
| 86 | |
| 87 void PushPaintCounter(TransportDIB* dib, | |
| 88 XDisplay* display, | |
| 89 Picture picture, | |
| 90 Pixmap pixmap, | |
| 91 const base::Closure& completion_callback); | |
| 92 | |
| 93 private: | |
| 94 friend struct DefaultSingletonTraits<XSyncHandler>; | |
| 95 | |
| 96 // A struct that has cleanup and callback tasks that were queued into the | |
| 97 // future and are run on |g_backing_store_sync_alarm| firing. | |
| 98 struct BackingStoreEvents { | |
| 99 BackingStoreEvents(TransportDIB* dib, XDisplay* d, Picture pic, Pixmap pix, | |
| 100 const base::Closure& c) | |
| 101 : dib(dib), | |
| 102 display(d), | |
| 103 picture(pic), | |
| 104 pixmap(pix), | |
| 105 closure(c) { | |
| 106 dib->IncreaseInFlightCounter(); | |
| 107 } | |
| 108 | |
| 109 TransportDIB* dib; | |
| 110 | |
| 111 // The display we're running on. | |
| 112 XDisplay* display; | |
| 113 | |
| 114 // Data to delete. | |
| 115 Picture picture; | |
| 116 Pixmap pixmap; | |
| 117 | |
| 118 // Callback once everything else is done. | |
| 119 base::Closure closure; | |
| 120 }; | |
| 121 | |
| 122 XSyncHandler(); | |
| 123 ~XSyncHandler(); | |
| 124 | |
| 125 // An event filter notified about all XEvents. We then filter out XSync | |
| 126 // events that are on counters that we made. | |
| 127 CHROMEG_CALLBACK_1(XSyncHandler, GdkFilterReturn, OnEvent, GdkXEvent*, | |
| 128 GdkEvent*); | |
| 129 | |
| 130 // Whether we successfully loaded XSyncExtension. | |
| 131 bool loaded_extension_; | |
| 132 | |
| 133 // The event ids returned to us by XSyncQueryExtension(). | |
| 134 int xsync_event_base_; | |
| 135 int xsync_error_base_; | |
| 136 | |
| 137 XSyncCounter backing_store_sync_counter_; | |
| 138 XSyncAlarm backing_store_sync_alarm_; | |
| 139 | |
| 140 // A queue of pending paints that we clean up after as alarms fire. | |
| 141 std::queue<BackingStoreEvents*> backing_store_events_; | |
| 142 }; | |
| 143 | |
| 144 void XSyncHandler::PushPaintCounter(TransportDIB* dib, | |
| 145 XDisplay* display, | |
| 146 Picture picture, | |
| 147 Pixmap pixmap, | |
| 148 const base::Closure& completion_callback) { | |
| 149 backing_store_events_.push(new BackingStoreEvents( | |
| 150 dib, display, picture, pixmap, completion_callback)); | |
| 151 | |
| 152 // Push a change counter event into the X11 event queue that will trigger our | |
| 153 // alarm when it is processed. | |
| 154 XSyncValue value; | |
| 155 XSyncIntToValue(&value, 1); | |
| 156 XSyncChangeCounter(gfx::GetXDisplay(), | |
| 157 backing_store_sync_counter_, | |
| 158 value); | |
| 159 } | |
| 160 | |
| 161 XSyncHandler::XSyncHandler() | |
| 162 : loaded_extension_(false), | |
| 163 xsync_event_base_(0), | |
| 164 xsync_error_base_(0), | |
| 165 backing_store_sync_counter_(0), | |
| 166 backing_store_sync_alarm_(0) { | |
| 167 XDisplay* display = gfx::GetXDisplay(); | |
| 168 if (XSyncQueryExtension(display, | |
| 169 &xsync_event_base_, | |
| 170 &xsync_error_base_)) { | |
| 171 // Create our monotonically increasing counter. | |
| 172 XSyncValue value; | |
| 173 XSyncIntToValue(&value, 0); | |
| 174 backing_store_sync_counter_ = XSyncCreateCounter(display, value); | |
| 175 | |
| 176 // Cerate our alarm that watches for changes to our counter. | |
| 177 XSyncAlarmAttributes attributes; | |
| 178 attributes.trigger.counter = backing_store_sync_counter_; | |
| 179 backing_store_sync_alarm_ = XSyncCreateAlarm(display, | |
| 180 XSyncCACounter, | |
| 181 &attributes); | |
| 182 | |
| 183 // Add our filter to the message loop to handle alarm triggers. | |
| 184 gdk_window_add_filter(NULL, &OnEventThunk, this); | |
| 185 | |
| 186 loaded_extension_ = true; | |
| 187 } | |
| 188 } | |
| 189 | |
| 190 XSyncHandler::~XSyncHandler() { | |
| 191 if (loaded_extension_) | |
| 192 gdk_window_remove_filter(NULL, &OnEventThunk, this); | |
| 193 | |
| 194 XSync(gfx::GetXDisplay(), False); | |
| 195 while (!backing_store_events_.empty()) { | |
| 196 // We delete the X11 resources we're holding onto. We don't run the | |
| 197 // callbacks because we are shutting down. | |
| 198 BackingStoreEvents* data = backing_store_events_.front(); | |
| 199 backing_store_events_.pop(); | |
| 200 XRenderFreePicture(data->display, data->picture); | |
| 201 XFreePixmap(data->display, data->pixmap); | |
| 202 data->dib->DecreaseInFlightCounter(); | |
| 203 delete data; | |
| 204 } | |
| 205 } | |
| 206 | |
| 207 GdkFilterReturn XSyncHandler::OnEvent(GdkXEvent* gdkxevent, | |
| 208 GdkEvent* event) { | |
| 209 XEvent* xevent = reinterpret_cast<XEvent*>(gdkxevent); | |
| 210 if (xevent->type == xsync_event_base_ + XSyncAlarmNotify) { | |
| 211 XSyncAlarmNotifyEvent* alarm_event = | |
| 212 reinterpret_cast<XSyncAlarmNotifyEvent*>(xevent); | |
| 213 if (alarm_event->alarm == backing_store_sync_alarm_) { | |
| 214 if (alarm_event->counter_value.hi == 0 && | |
| 215 alarm_event->counter_value.lo == 0) { | |
| 216 // We receive an event about the initial state of the counter during | |
| 217 // alarm creation. We must ignore this event instead of responding to | |
| 218 // it. | |
| 219 return GDK_FILTER_REMOVE; | |
| 220 } | |
| 221 | |
| 222 DCHECK(!backing_store_events_.empty()); | |
| 223 BackingStoreEvents* data = backing_store_events_.front(); | |
| 224 backing_store_events_.pop(); | |
| 225 | |
| 226 // We are responsible for deleting all the data in the struct now that | |
| 227 // we are finished with it. | |
| 228 XRenderFreePicture(data->display, data->picture); | |
| 229 XFreePixmap(data->display, data->pixmap); | |
| 230 | |
| 231 // Dispatch the closure we were given. | |
| 232 data->closure.Run(); | |
| 233 | |
| 234 data->dib->DecreaseInFlightCounter(); | |
| 235 delete data; | |
| 236 | |
| 237 return GDK_FILTER_REMOVE; | |
| 238 } | |
| 239 } | |
| 240 | |
| 241 return GDK_FILTER_CONTINUE; | |
| 242 } | |
| 243 | |
| 244 } // namespace | |
| 245 | |
| 246 BackingStoreGtk::BackingStoreGtk(RenderWidgetHost* widget, | |
| 247 const gfx::Size& size, | |
| 248 void* visual, | |
| 249 int depth) | |
| 250 : BackingStore(widget, size), | |
| 251 display_(gfx::GetXDisplay()), | |
| 252 shared_memory_support_(ui::QuerySharedMemorySupport(display_)), | |
| 253 use_render_(ui::QueryRenderSupport(display_)), | |
| 254 visual_(visual), | |
| 255 visual_depth_(depth), | |
| 256 root_window_(ui::GetX11RootWindow()) { | |
| 257 #if defined(OS_OPENBSD) || defined(OS_FREEBSD) | |
| 258 COMPILE_ASSERT(_BYTE_ORDER == _LITTLE_ENDIAN, assumes_little_endian); | |
| 259 #else | |
| 260 COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, assumes_little_endian); | |
| 261 #endif | |
| 262 | |
| 263 pixmap_ = XCreatePixmap(display_, root_window_, | |
| 264 size.width(), size.height(), depth); | |
| 265 | |
| 266 if (use_render_) { | |
| 267 picture_ = XRenderCreatePicture( | |
| 268 display_, pixmap_, | |
| 269 ui::GetRenderVisualFormat(display_, | |
| 270 static_cast<Visual*>(visual)), | |
| 271 0, NULL); | |
| 272 pixmap_bpp_ = 0; | |
| 273 } else { | |
| 274 picture_ = 0; | |
| 275 pixmap_bpp_ = gfx::BitsPerPixelForPixmapDepth(display_, depth); | |
| 276 } | |
| 277 | |
| 278 pixmap_gc_ = XCreateGC(display_, pixmap_, 0, NULL); | |
| 279 } | |
| 280 | |
| 281 BackingStoreGtk::BackingStoreGtk(RenderWidgetHost* widget, | |
| 282 const gfx::Size& size) | |
| 283 : BackingStore(widget, size), | |
| 284 display_(NULL), | |
| 285 shared_memory_support_(ui::SHARED_MEMORY_NONE), | |
| 286 use_render_(false), | |
| 287 pixmap_bpp_(0), | |
| 288 visual_(NULL), | |
| 289 visual_depth_(-1), | |
| 290 root_window_(0), | |
| 291 pixmap_(0), | |
| 292 picture_(0), | |
| 293 pixmap_gc_(NULL) { | |
| 294 } | |
| 295 | |
| 296 BackingStoreGtk::~BackingStoreGtk() { | |
| 297 // In unit tests, display_ may be NULL. | |
| 298 if (!display_) | |
| 299 return; | |
| 300 | |
| 301 XRenderFreePicture(display_, picture_); | |
| 302 XFreePixmap(display_, pixmap_); | |
| 303 XFreeGC(display_, static_cast<GC>(pixmap_gc_)); | |
| 304 } | |
| 305 | |
| 306 size_t BackingStoreGtk::MemorySize() { | |
| 307 if (!use_render_) | |
| 308 return size().GetArea() * (pixmap_bpp_ / 8); | |
| 309 else | |
| 310 return size().GetArea() * 4; | |
| 311 } | |
| 312 | |
| 313 void BackingStoreGtk::PaintRectWithoutXrender( | |
| 314 TransportDIB* bitmap, | |
| 315 const gfx::Rect& bitmap_rect, | |
| 316 const std::vector<gfx::Rect>& copy_rects) { | |
| 317 const int width = bitmap_rect.width(); | |
| 318 const int height = bitmap_rect.height(); | |
| 319 Pixmap pixmap = XCreatePixmap(display_, root_window_, width, height, | |
| 320 visual_depth_); | |
| 321 | |
| 322 // Draw ARGB transport DIB onto our pixmap. | |
| 323 gfx::PutARGBImage(display_, visual_, visual_depth_, pixmap, | |
| 324 pixmap_gc_, static_cast<uint8*>(bitmap->memory()), | |
| 325 width, height); | |
| 326 | |
| 327 for (size_t i = 0; i < copy_rects.size(); i++) { | |
| 328 const gfx::Rect& copy_rect = copy_rects[i]; | |
| 329 XCopyArea(display_, | |
| 330 pixmap, // src | |
| 331 pixmap_, // dest | |
| 332 static_cast<GC>(pixmap_gc_), // gc | |
| 333 copy_rect.x() - bitmap_rect.x(), // src_x | |
| 334 copy_rect.y() - bitmap_rect.y(), // src_y | |
| 335 copy_rect.width(), // width | |
| 336 copy_rect.height(), // height | |
| 337 copy_rect.x(), // dest_x | |
| 338 copy_rect.y()); // dest_y | |
| 339 } | |
| 340 | |
| 341 XFreePixmap(display_, pixmap); | |
| 342 } | |
| 343 | |
| 344 void BackingStoreGtk::PaintToBackingStore( | |
| 345 RenderProcessHost* process, | |
| 346 TransportDIB::Id bitmap, | |
| 347 const gfx::Rect& bitmap_rect, | |
| 348 const std::vector<gfx::Rect>& copy_rects, | |
| 349 float scale_factor, | |
| 350 const base::Closure& completion_callback, | |
| 351 bool* scheduled_completion_callback) { | |
| 352 *scheduled_completion_callback = false; | |
| 353 | |
| 354 if (!display_) | |
| 355 return; | |
| 356 | |
| 357 if (bitmap_rect.IsEmpty()) | |
| 358 return; | |
| 359 | |
| 360 gfx::Rect pixel_bitmap_rect = gfx::ToEnclosedRect( | |
| 361 gfx::ScaleRect(bitmap_rect, scale_factor)); | |
| 362 const int width = pixel_bitmap_rect.width(); | |
| 363 const int height = pixel_bitmap_rect.height(); | |
| 364 | |
| 365 if (width <= 0 || width > kMaxVideoLayerSize || | |
| 366 height <= 0 || height > kMaxVideoLayerSize) | |
| 367 return; | |
| 368 | |
| 369 TransportDIB* dib = process->GetTransportDIB(bitmap); | |
| 370 if (!dib) | |
| 371 return; | |
| 372 | |
| 373 if (!use_render_) | |
| 374 return PaintRectWithoutXrender(dib, bitmap_rect, copy_rects); | |
| 375 | |
| 376 Picture picture; | |
| 377 Pixmap pixmap; | |
| 378 | |
| 379 if (shared_memory_support_ == ui::SHARED_MEMORY_PIXMAP) { | |
| 380 XShmSegmentInfo shminfo = {0}; | |
| 381 shminfo.shmseg = dib->MapToX(display_); | |
| 382 | |
| 383 // The NULL in the following is the |data| pointer: this is an artifact of | |
| 384 // Xlib trying to be helpful, rather than just exposing the X protocol. It | |
| 385 // assumes that we have the shared memory segment mapped into our memory, | |
| 386 // which we don't, and it's trying to calculate an offset by taking the | |
| 387 // difference between the |data| pointer and the address of the mapping in | |
| 388 // |shminfo|. Since both are NULL, the offset will be calculated to be 0, | |
| 389 // which is correct for us. | |
| 390 pixmap = XShmCreatePixmap(display_, root_window_, NULL, &shminfo, | |
| 391 width, height, 32); | |
| 392 } else { | |
| 393 // We don't have shared memory pixmaps. Fall back to creating a pixmap | |
| 394 // ourselves and putting an image on it. | |
| 395 pixmap = XCreatePixmap(display_, root_window_, width, height, 32); | |
| 396 GC gc = XCreateGC(display_, pixmap, 0, NULL); | |
| 397 | |
| 398 if (shared_memory_support_ == ui::SHARED_MEMORY_PUTIMAGE) { | |
| 399 const XID shmseg = dib->MapToX(display_); | |
| 400 | |
| 401 XShmSegmentInfo shminfo; | |
| 402 memset(&shminfo, 0, sizeof(shminfo)); | |
| 403 shminfo.shmseg = shmseg; | |
| 404 shminfo.shmaddr = static_cast<char*>(dib->memory()); | |
| 405 | |
| 406 XImage* image = XShmCreateImage(display_, static_cast<Visual*>(visual_), | |
| 407 32, ZPixmap, | |
| 408 shminfo.shmaddr, &shminfo, | |
| 409 width, height); | |
| 410 | |
| 411 // This code path is important for performance and we have found that | |
| 412 // different techniques work better on different platforms. See | |
| 413 // http://code.google.com/p/chromium/issues/detail?id=44124. | |
| 414 // | |
| 415 // Checking for ARM is an approximation, but it seems to be a good one so | |
| 416 // far. | |
| 417 #if defined(ARCH_CPU_ARM_FAMILY) | |
| 418 for (size_t i = 0; i < copy_rects.size(); i++) { | |
| 419 const gfx::Rect& copy_rect = copy_rects[i]; | |
| 420 gfx::Rect pixel_copy_rect = gfx::ToEnclosedRect( | |
| 421 gfx::ScaleRect(copy_rect, scale_factor)); | |
| 422 XShmPutImage(display_, pixmap, gc, image, | |
| 423 pixel_copy_rect.x() - pixel_bitmap_rect.x(), /* source x */ | |
| 424 pixel_copy_rect.y() - pixel_bitmap_rect.y(), /* source y */ | |
| 425 pixel_copy_rect.x() - pixel_bitmap_rect.x(), /* dest x */ | |
| 426 pixel_copy_rect.y() - pixel_bitmap_rect.y(), /* dest y */ | |
| 427 pixel_copy_rect.width(), pixel_copy_rect.height(), | |
| 428 False /* send_event */); | |
| 429 } | |
| 430 #else | |
| 431 XShmPutImage(display_, pixmap, gc, image, | |
| 432 0, 0 /* source x, y */, 0, 0 /* dest x, y */, | |
| 433 width, height, False /* send_event */); | |
| 434 #endif | |
| 435 XDestroyImage(image); | |
| 436 } else { // case SHARED_MEMORY_NONE | |
| 437 // No shared memory support, we have to copy the bitmap contents | |
| 438 // to the X server. Xlib wraps the underlying PutImage call | |
| 439 // behind several layers of functions which try to convert the | |
| 440 // image into the format which the X server expects. The | |
| 441 // following values hopefully disable all conversions. | |
| 442 XImage image; | |
| 443 memset(&image, 0, sizeof(image)); | |
| 444 | |
| 445 image.width = width; | |
| 446 image.height = height; | |
| 447 image.depth = 32; | |
| 448 image.bits_per_pixel = 32; | |
| 449 image.format = ZPixmap; | |
| 450 image.byte_order = LSBFirst; | |
| 451 image.bitmap_unit = 8; | |
| 452 image.bitmap_bit_order = LSBFirst; | |
| 453 image.bytes_per_line = width * 4; | |
| 454 image.red_mask = 0xff; | |
| 455 image.green_mask = 0xff00; | |
| 456 image.blue_mask = 0xff0000; | |
| 457 image.data = static_cast<char*>(dib->memory()); | |
| 458 | |
| 459 XPutImage(display_, pixmap, gc, &image, | |
| 460 0, 0 /* source x, y */, 0, 0 /* dest x, y */, | |
| 461 width, height); | |
| 462 } | |
| 463 XFreeGC(display_, gc); | |
| 464 } | |
| 465 | |
| 466 picture = ui::CreatePictureFromSkiaPixmap(display_, pixmap); | |
| 467 | |
| 468 if (scale_factor != 1.0) { | |
| 469 float up_scale = 1.0 / scale_factor; | |
| 470 XTransform scaling = { { | |
| 471 { XDoubleToFixed(1), XDoubleToFixed(0), XDoubleToFixed(0) }, | |
| 472 { XDoubleToFixed(0), XDoubleToFixed(1), XDoubleToFixed(0) }, | |
| 473 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(up_scale) } | |
| 474 } }; | |
| 475 XRenderSetPictureTransform(display_, picture, &scaling); | |
| 476 XRenderSetPictureFilter(display_, picture, FilterGood, NULL, 0); | |
| 477 } | |
| 478 for (size_t i = 0; i < copy_rects.size(); i++) { | |
| 479 const gfx::Rect& copy_rect = copy_rects[i]; | |
| 480 XRenderComposite(display_, | |
| 481 PictOpSrc, // op | |
| 482 picture, // src | |
| 483 0, // mask | |
| 484 picture_, // dest | |
| 485 copy_rect.x() - bitmap_rect.x(), // src_x | |
| 486 copy_rect.y() - bitmap_rect.y(), // src_y | |
| 487 0, // mask_x | |
| 488 0, // mask_y | |
| 489 copy_rect.x(), // dest_x | |
| 490 copy_rect.y(), // dest_y | |
| 491 copy_rect.width(), // width | |
| 492 copy_rect.height()); // height | |
| 493 } | |
| 494 | |
| 495 // In the case of shared memory, we wait for the composite to complete so that | |
| 496 // we are sure that the X server has finished reading from the shared memory | |
| 497 // segment. | |
| 498 if (shared_memory_support_ != ui::SHARED_MEMORY_NONE) { | |
| 499 XSyncHandler* handler = XSyncHandler::GetInstance(); | |
| 500 if (handler->Enabled()) { | |
| 501 *scheduled_completion_callback = true; | |
| 502 handler->PushPaintCounter( | |
| 503 dib, display_, picture, pixmap, completion_callback); | |
| 504 } else { | |
| 505 XSync(display_, False); | |
| 506 } | |
| 507 } | |
| 508 | |
| 509 if (*scheduled_completion_callback == false) { | |
| 510 // If we didn't schedule a callback, we need to delete our resources now. | |
| 511 XRenderFreePicture(display_, picture); | |
| 512 XFreePixmap(display_, pixmap); | |
| 513 } | |
| 514 } | |
| 515 | |
| 516 bool BackingStoreGtk::CopyFromBackingStore(const gfx::Rect& rect, | |
| 517 skia::PlatformBitmap* output) { | |
| 518 base::TimeTicks begin_time = base::TimeTicks::Now(); | |
| 519 | |
| 520 if (visual_depth_ < 24) { | |
| 521 // CopyFromBackingStore() copies pixels out of the XImage | |
| 522 // in a way that assumes that each component (red, green, | |
| 523 // blue) is a byte. This doesn't work on visuals which | |
| 524 // encode a pixel color with less than a byte per color. | |
| 525 return false; | |
| 526 } | |
| 527 | |
| 528 const int width = std::min(size().width(), rect.width()); | |
| 529 const int height = std::min(size().height(), rect.height()); | |
| 530 | |
| 531 XImage* image; | |
| 532 XShmSegmentInfo shminfo; // Used only when shared memory is enabled. | |
| 533 if (shared_memory_support_ != ui::SHARED_MEMORY_NONE) { | |
| 534 // Use shared memory for faster copies when it's available. | |
| 535 Visual* visual = static_cast<Visual*>(visual_); | |
| 536 memset(&shminfo, 0, sizeof(shminfo)); | |
| 537 image = XShmCreateImage(display_, visual, 32, | |
| 538 ZPixmap, NULL, &shminfo, width, height); | |
| 539 if (!image) { | |
| 540 return false; | |
| 541 } | |
| 542 // Create the shared memory segment for the image and map it. | |
| 543 if (image->bytes_per_line == 0 || image->height == 0 || | |
| 544 static_cast<size_t>(image->height) > | |
| 545 (std::numeric_limits<size_t>::max() / image->bytes_per_line)) { | |
| 546 XDestroyImage(image); | |
| 547 return false; | |
| 548 } | |
| 549 shminfo.shmid = shmget(IPC_PRIVATE, image->bytes_per_line * image->height, | |
| 550 IPC_CREAT|0600); | |
| 551 if (shminfo.shmid == -1) { | |
| 552 XDestroyImage(image); | |
| 553 LOG(WARNING) << "Failed to get shared memory segment. " | |
| 554 "Performance may be degraded."; | |
| 555 return false; | |
| 556 } else { | |
| 557 VLOG(1) << "Got shared memory segment " << shminfo.shmid; | |
| 558 } | |
| 559 | |
| 560 void* mapped_memory = shmat(shminfo.shmid, NULL, SHM_RDONLY); | |
| 561 shmctl(shminfo.shmid, IPC_RMID, 0); | |
| 562 if (mapped_memory == (void*)-1) { | |
| 563 XDestroyImage(image); | |
| 564 return false; | |
| 565 } | |
| 566 shminfo.shmaddr = image->data = static_cast<char*>(mapped_memory); | |
| 567 | |
| 568 if (!XShmAttach(display_, &shminfo) || | |
| 569 !XShmGetImage(display_, pixmap_, image, rect.x(), rect.y(), | |
| 570 AllPlanes)) { | |
| 571 DestroySharedImage(display_, image, &shminfo); | |
| 572 LOG(WARNING) << "X failed to get shared memory segment. " | |
| 573 "Performance may be degraded."; | |
| 574 return false; | |
| 575 } | |
| 576 | |
| 577 VLOG(1) << "Using X shared memory segment " << shminfo.shmid; | |
| 578 } else { | |
| 579 LOG(WARNING) << "Not using X shared memory."; | |
| 580 // Non-shared memory case just copy the image from the server. | |
| 581 image = XGetImage(display_, pixmap_, | |
| 582 rect.x(), rect.y(), width, height, | |
| 583 AllPlanes, ZPixmap); | |
| 584 } | |
| 585 | |
| 586 // TODO(jhawkins): Need to convert the image data if the image bits per pixel | |
| 587 // is not 32. | |
| 588 // Note that this also initializes the output bitmap as opaque. | |
| 589 if (!output->Allocate(width, height, true) || | |
| 590 image->bits_per_pixel != 32) { | |
| 591 if (shared_memory_support_ != ui::SHARED_MEMORY_NONE) | |
| 592 DestroySharedImage(display_, image, &shminfo); | |
| 593 else | |
| 594 XDestroyImage(image); | |
| 595 return false; | |
| 596 } | |
| 597 | |
| 598 // The X image might have a different row stride, so iterate through | |
| 599 // it and copy each row out, only up to the pixels we're actually | |
| 600 // using. This code assumes a visual mode where a pixel is | |
| 601 // represented using a 32-bit unsigned int, with a byte per component. | |
| 602 const SkBitmap& bitmap = output->GetBitmap(); | |
| 603 SkAutoLockPixels alp(bitmap); | |
| 604 | |
| 605 for (int y = 0; y < height; y++) { | |
| 606 const uint32* src_row = reinterpret_cast<uint32*>( | |
| 607 &image->data[image->bytes_per_line * y]); | |
| 608 uint32* dest_row = bitmap.getAddr32(0, y); | |
| 609 for (int x = 0; x < width; ++x, ++dest_row) { | |
| 610 // Force alpha to be 0xff, because otherwise it causes rendering problems. | |
| 611 *dest_row = src_row[x] | 0xff000000; | |
| 612 } | |
| 613 } | |
| 614 | |
| 615 if (shared_memory_support_ != ui::SHARED_MEMORY_NONE) | |
| 616 DestroySharedImage(display_, image, &shminfo); | |
| 617 else | |
| 618 XDestroyImage(image); | |
| 619 | |
| 620 HISTOGRAM_TIMES("BackingStore.RetrievalFromX", | |
| 621 base::TimeTicks::Now() - begin_time); | |
| 622 return true; | |
| 623 } | |
| 624 | |
| 625 void BackingStoreGtk::ScrollBackingStore(const gfx::Vector2d& delta, | |
| 626 const gfx::Rect& clip_rect, | |
| 627 const gfx::Size& view_size) { | |
| 628 if (!display_) | |
| 629 return; | |
| 630 | |
| 631 // We only support scrolling in one direction at a time. | |
| 632 DCHECK(delta.x() == 0 || delta.y() == 0); | |
| 633 | |
| 634 if (delta.y()) { | |
| 635 // Positive values of |delta|.y() scroll up | |
| 636 if (abs(delta.y()) < clip_rect.height()) { | |
| 637 XCopyArea(display_, pixmap_, pixmap_, static_cast<GC>(pixmap_gc_), | |
| 638 clip_rect.x() /* source x */, | |
| 639 std::max(clip_rect.y(), clip_rect.y() - delta.y()), | |
| 640 clip_rect.width(), | |
| 641 clip_rect.height() - abs(delta.y()), | |
| 642 clip_rect.x() /* dest x */, | |
| 643 std::max(clip_rect.y(), clip_rect.y() + delta.y()) /* dest y */ | |
| 644 ); | |
| 645 } | |
| 646 } else if (delta.x()) { | |
| 647 // Positive values of |delta|.x() scroll right | |
| 648 if (abs(delta.x()) < clip_rect.width()) { | |
| 649 XCopyArea(display_, pixmap_, pixmap_, static_cast<GC>(pixmap_gc_), | |
| 650 std::max(clip_rect.x(), clip_rect.x() - delta.x()), | |
| 651 clip_rect.y() /* source y */, | |
| 652 clip_rect.width() - abs(delta.x()), | |
| 653 clip_rect.height(), | |
| 654 std::max(clip_rect.x(), clip_rect.x() + delta.x()) /* dest x */, | |
| 655 clip_rect.y() /* dest x */); | |
| 656 } | |
| 657 } | |
| 658 } | |
| 659 | |
| 660 void BackingStoreGtk::XShowRect(const gfx::Point &origin, | |
| 661 const gfx::Rect& rect, XID target) { | |
| 662 XCopyArea(display_, pixmap_, target, static_cast<GC>(pixmap_gc_), | |
| 663 rect.x(), rect.y(), rect.width(), rect.height(), | |
| 664 rect.x() + origin.x(), rect.y() + origin.y()); | |
| 665 } | |
| 666 | |
| 667 #if defined(TOOLKIT_GTK) | |
| 668 void BackingStoreGtk::PaintToRect(const gfx::Rect& rect, GdkDrawable* target) { | |
| 669 cairo_surface_t* surface = cairo_xlib_surface_create( | |
| 670 display_, pixmap_, static_cast<Visual*>(visual_), | |
| 671 size().width(), size().height()); | |
| 672 cairo_t* cr = gdk_cairo_create(target); | |
| 673 | |
| 674 cairo_translate(cr, rect.x(), rect.y()); | |
| 675 double x_scale = static_cast<double>(rect.width()) / size().width(); | |
| 676 double y_scale = static_cast<double>(rect.height()) / size().height(); | |
| 677 cairo_scale(cr, x_scale, y_scale); | |
| 678 | |
| 679 cairo_pattern_t* pattern = cairo_pattern_create_for_surface(surface); | |
| 680 cairo_pattern_set_filter(pattern, CAIRO_FILTER_BEST); | |
| 681 cairo_set_source(cr, pattern); | |
| 682 cairo_pattern_destroy(pattern); | |
| 683 | |
| 684 cairo_identity_matrix(cr); | |
| 685 | |
| 686 cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height()); | |
| 687 cairo_fill(cr); | |
| 688 cairo_destroy(cr); | |
| 689 } | |
| 690 #endif | |
| 691 | |
| 692 } // namespace content | |
| OLD | NEW |