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 |