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 693