OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 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 "chrome/browser/renderer_host/backing_store_x.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 | |
13 #if defined(OS_OPENBSD) || defined(OS_FREEBSD) | |
14 #include <sys/endian.h> | |
15 #endif | |
16 | |
17 #include <algorithm> | |
18 #include <utility> | |
19 #include <limits> | |
20 | |
21 #include "app/surface/transport_dib.h" | |
22 #include "base/compiler_specific.h" | |
23 #include "base/logging.h" | |
24 #include "base/metrics/histogram.h" | |
25 #include "base/time.h" | |
26 #include "chrome/browser/renderer_host/render_process_host.h" | |
27 #include "skia/ext/platform_canvas.h" | |
28 #include "third_party/skia/include/core/SkBitmap.h" | |
29 #include "ui/base/x/x11_util.h" | |
30 #include "ui/base/x/x11_util_internal.h" | |
31 #include "ui/gfx/rect.h" | |
32 | |
33 // Assume that somewhere along the line, someone will do width * height * 4 | |
34 // with signed numbers. If the maximum value is 2**31, then 2**31 / 4 = | |
35 // 2**29 and floor(sqrt(2**29)) = 23170. | |
36 | |
37 // Max height and width for layers | |
38 static const int kMaxVideoLayerSize = 23170; | |
39 | |
40 | |
41 // X Backing Stores: | |
42 // | |
43 // Unlike Windows, where the backing store is kept in heap memory, we keep our | |
44 // backing store in the X server, as a pixmap. Thus expose events just require | |
45 // instructing the X server to copy from the backing store to the window. | |
46 // | |
47 // The backing store is in the same format as the visual which our main window | |
48 // is using. Bitmaps from the renderer are uploaded to the X server, either via | |
49 // shared memory or over the wire, and XRENDER is used to convert them to the | |
50 // correct format for the backing store. | |
51 | |
52 // Destroys the image and the associated shared memory structures. This is a | |
53 // helper function for code using shared memory. | |
54 static void DestroySharedImage(Display* display, | |
55 XImage* image, | |
56 XShmSegmentInfo* shminfo) { | |
57 XShmDetach(display, shminfo); | |
58 XDestroyImage(image); | |
59 shmdt(shminfo->shmaddr); | |
60 } | |
61 | |
62 BackingStoreX::BackingStoreX(RenderWidgetHost* widget, | |
63 const gfx::Size& size, | |
64 void* visual, | |
65 int depth) | |
66 : BackingStore(widget, size), | |
67 display_(ui::GetXDisplay()), | |
68 shared_memory_support_(ui::QuerySharedMemorySupport(display_)), | |
69 use_render_(ui::QueryRenderSupport(display_)), | |
70 visual_(visual), | |
71 visual_depth_(depth), | |
72 root_window_(ui::GetX11RootWindow()) { | |
73 #if defined(OS_OPENBSD) || defined(OS_FREEBSD) | |
74 COMPILE_ASSERT(_BYTE_ORDER == _LITTLE_ENDIAN, assumes_little_endian); | |
75 #else | |
76 COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, assumes_little_endian); | |
77 #endif | |
78 | |
79 pixmap_ = XCreatePixmap(display_, root_window_, | |
80 size.width(), size.height(), depth); | |
81 | |
82 if (use_render_) { | |
83 picture_ = XRenderCreatePicture( | |
84 display_, pixmap_, | |
85 ui::GetRenderVisualFormat(display_, | |
86 static_cast<Visual*>(visual)), | |
87 0, NULL); | |
88 pixmap_bpp_ = 0; | |
89 } else { | |
90 picture_ = 0; | |
91 pixmap_bpp_ = ui::BitsPerPixelForPixmapDepth(display_, depth); | |
92 } | |
93 | |
94 pixmap_gc_ = XCreateGC(display_, pixmap_, 0, NULL); | |
95 } | |
96 | |
97 BackingStoreX::BackingStoreX(RenderWidgetHost* widget, const gfx::Size& size) | |
98 : BackingStore(widget, size), | |
99 display_(NULL), | |
100 shared_memory_support_(ui::SHARED_MEMORY_NONE), | |
101 use_render_(false), | |
102 pixmap_bpp_(0), | |
103 visual_(NULL), | |
104 visual_depth_(-1), | |
105 root_window_(0), | |
106 pixmap_(0), | |
107 picture_(0), | |
108 pixmap_gc_(NULL) { | |
109 } | |
110 | |
111 BackingStoreX::~BackingStoreX() { | |
112 // In unit tests, display_ may be NULL. | |
113 if (!display_) | |
114 return; | |
115 | |
116 XRenderFreePicture(display_, picture_); | |
117 XFreePixmap(display_, pixmap_); | |
118 XFreeGC(display_, static_cast<GC>(pixmap_gc_)); | |
119 } | |
120 | |
121 size_t BackingStoreX::MemorySize() { | |
122 if (!use_render_) | |
123 return size().GetArea() * (pixmap_bpp_ / 8); | |
124 else | |
125 return size().GetArea() * 4; | |
126 } | |
127 | |
128 void BackingStoreX::PaintRectWithoutXrender( | |
129 TransportDIB* bitmap, | |
130 const gfx::Rect& bitmap_rect, | |
131 const std::vector<gfx::Rect>& copy_rects) { | |
132 const int width = bitmap_rect.width(); | |
133 const int height = bitmap_rect.height(); | |
134 Pixmap pixmap = XCreatePixmap(display_, root_window_, width, height, | |
135 visual_depth_); | |
136 | |
137 // Draw ARGB transport DIB onto our pixmap. | |
138 ui::PutARGBImage(display_, visual_, visual_depth_, pixmap, | |
139 pixmap_gc_, static_cast<uint8*>(bitmap->memory()), | |
140 width, height); | |
141 | |
142 for (size_t i = 0; i < copy_rects.size(); i++) { | |
143 const gfx::Rect& copy_rect = copy_rects[i]; | |
144 XCopyArea(display_, | |
145 pixmap, // src | |
146 pixmap_, // dest | |
147 static_cast<GC>(pixmap_gc_), // gc | |
148 copy_rect.x() - bitmap_rect.x(), // src_x | |
149 copy_rect.y() - bitmap_rect.y(), // src_y | |
150 copy_rect.width(), // width | |
151 copy_rect.height(), // height | |
152 copy_rect.x(), // dest_x | |
153 copy_rect.y()); // dest_y | |
154 } | |
155 | |
156 XFreePixmap(display_, pixmap); | |
157 } | |
158 | |
159 void BackingStoreX::PaintToBackingStore( | |
160 RenderProcessHost* process, | |
161 TransportDIB::Id bitmap, | |
162 const gfx::Rect& bitmap_rect, | |
163 const std::vector<gfx::Rect>& copy_rects) { | |
164 if (!display_) | |
165 return; | |
166 | |
167 if (bitmap_rect.IsEmpty()) | |
168 return; | |
169 | |
170 const int width = bitmap_rect.width(); | |
171 const int height = bitmap_rect.height(); | |
172 | |
173 if (width <= 0 || width > kMaxVideoLayerSize || | |
174 height <= 0 || height > kMaxVideoLayerSize) | |
175 return; | |
176 | |
177 TransportDIB* dib = process->GetTransportDIB(bitmap); | |
178 if (!dib) | |
179 return; | |
180 | |
181 if (!use_render_) | |
182 return PaintRectWithoutXrender(dib, bitmap_rect, copy_rects); | |
183 | |
184 Picture picture; | |
185 Pixmap pixmap; | |
186 | |
187 if (shared_memory_support_ == ui::SHARED_MEMORY_PIXMAP) { | |
188 XShmSegmentInfo shminfo = {0}; | |
189 shminfo.shmseg = dib->MapToX(display_); | |
190 | |
191 // The NULL in the following is the |data| pointer: this is an artifact of | |
192 // Xlib trying to be helpful, rather than just exposing the X protocol. It | |
193 // assumes that we have the shared memory segment mapped into our memory, | |
194 // which we don't, and it's trying to calculate an offset by taking the | |
195 // difference between the |data| pointer and the address of the mapping in | |
196 // |shminfo|. Since both are NULL, the offset will be calculated to be 0, | |
197 // which is correct for us. | |
198 pixmap = XShmCreatePixmap(display_, root_window_, NULL, &shminfo, | |
199 width, height, 32); | |
200 } else { | |
201 // We don't have shared memory pixmaps. Fall back to creating a pixmap | |
202 // ourselves and putting an image on it. | |
203 pixmap = XCreatePixmap(display_, root_window_, width, height, 32); | |
204 GC gc = XCreateGC(display_, pixmap, 0, NULL); | |
205 | |
206 if (shared_memory_support_ == ui::SHARED_MEMORY_PUTIMAGE) { | |
207 const XID shmseg = dib->MapToX(display_); | |
208 | |
209 XShmSegmentInfo shminfo; | |
210 memset(&shminfo, 0, sizeof(shminfo)); | |
211 shminfo.shmseg = shmseg; | |
212 shminfo.shmaddr = static_cast<char*>(dib->memory()); | |
213 | |
214 XImage* image = XShmCreateImage(display_, static_cast<Visual*>(visual_), | |
215 32, ZPixmap, | |
216 shminfo.shmaddr, &shminfo, | |
217 width, height); | |
218 | |
219 // This code path is important for performance and we have found that | |
220 // different techniques work better on different platforms. See | |
221 // http://code.google.com/p/chromium/issues/detail?id=44124. | |
222 // | |
223 // Checking for ARM is an approximation, but it seems to be a good one so | |
224 // far. | |
225 #if defined(ARCH_CPU_ARM_FAMILY) | |
226 for (size_t i = 0; i < copy_rects.size(); i++) { | |
227 const gfx::Rect& copy_rect = copy_rects[i]; | |
228 XShmPutImage(display_, pixmap, gc, image, | |
229 copy_rect.x() - bitmap_rect.x(), /* source x */ | |
230 copy_rect.y() - bitmap_rect.y(), /* source y */ | |
231 copy_rect.x() - bitmap_rect.x(), /* dest x */ | |
232 copy_rect.y() - bitmap_rect.y(), /* dest y */ | |
233 copy_rect.width(), copy_rect.height(), | |
234 False /* send_event */); | |
235 } | |
236 #else | |
237 XShmPutImage(display_, pixmap, gc, image, | |
238 0, 0 /* source x, y */, 0, 0 /* dest x, y */, | |
239 width, height, False /* send_event */); | |
240 #endif | |
241 XDestroyImage(image); | |
242 } else { // case SHARED_MEMORY_NONE | |
243 // No shared memory support, we have to copy the bitmap contents | |
244 // to the X server. Xlib wraps the underlying PutImage call | |
245 // behind several layers of functions which try to convert the | |
246 // image into the format which the X server expects. The | |
247 // following values hopefully disable all conversions. | |
248 XImage image; | |
249 memset(&image, 0, sizeof(image)); | |
250 | |
251 image.width = width; | |
252 image.height = height; | |
253 image.depth = 32; | |
254 image.bits_per_pixel = 32; | |
255 image.format = ZPixmap; | |
256 image.byte_order = LSBFirst; | |
257 image.bitmap_unit = 8; | |
258 image.bitmap_bit_order = LSBFirst; | |
259 image.bytes_per_line = width * 4; | |
260 image.red_mask = 0xff; | |
261 image.green_mask = 0xff00; | |
262 image.blue_mask = 0xff0000; | |
263 image.data = static_cast<char*>(dib->memory()); | |
264 | |
265 XPutImage(display_, pixmap, gc, &image, | |
266 0, 0 /* source x, y */, 0, 0 /* dest x, y */, | |
267 width, height); | |
268 } | |
269 XFreeGC(display_, gc); | |
270 } | |
271 | |
272 picture = ui::CreatePictureFromSkiaPixmap(display_, pixmap); | |
273 | |
274 for (size_t i = 0; i < copy_rects.size(); i++) { | |
275 const gfx::Rect& copy_rect = copy_rects[i]; | |
276 XRenderComposite(display_, | |
277 PictOpSrc, // op | |
278 picture, // src | |
279 0, // mask | |
280 picture_, // dest | |
281 copy_rect.x() - bitmap_rect.x(), // src_x | |
282 copy_rect.y() - bitmap_rect.y(), // src_y | |
283 0, // mask_x | |
284 0, // mask_y | |
285 copy_rect.x(), // dest_x | |
286 copy_rect.y(), // dest_y | |
287 copy_rect.width(), // width | |
288 copy_rect.height()); // height | |
289 } | |
290 | |
291 // In the case of shared memory, we wait for the composite to complete so that | |
292 // we are sure that the X server has finished reading from the shared memory | |
293 // segment. | |
294 if (shared_memory_support_ != ui::SHARED_MEMORY_NONE) | |
295 XSync(display_, False); | |
296 | |
297 XRenderFreePicture(display_, picture); | |
298 XFreePixmap(display_, pixmap); | |
299 } | |
300 | |
301 bool BackingStoreX::CopyFromBackingStore(const gfx::Rect& rect, | |
302 skia::PlatformCanvas* output) { | |
303 base::TimeTicks begin_time = base::TimeTicks::Now(); | |
304 | |
305 if (visual_depth_ < 24) { | |
306 // CopyFromBackingStore() copies pixels out of the XImage | |
307 // in a way that assumes that each component (red, green, | |
308 // blue) is a byte. This doesn't work on visuals which | |
309 // encode a pixel color with less than a byte per color. | |
310 return false; | |
311 } | |
312 | |
313 const int width = std::min(size().width(), rect.width()); | |
314 const int height = std::min(size().height(), rect.height()); | |
315 | |
316 XImage* image; | |
317 XShmSegmentInfo shminfo; // Used only when shared memory is enabled. | |
318 if (shared_memory_support_ != ui::SHARED_MEMORY_NONE) { | |
319 // Use shared memory for faster copies when it's available. | |
320 Visual* visual = static_cast<Visual*>(visual_); | |
321 memset(&shminfo, 0, sizeof(shminfo)); | |
322 image = XShmCreateImage(display_, visual, 32, | |
323 ZPixmap, NULL, &shminfo, width, height); | |
324 if (!image) { | |
325 return false; | |
326 } | |
327 // Create the shared memory segment for the image and map it. | |
328 if (image->bytes_per_line == 0 || image->height == 0 || | |
329 static_cast<size_t>(image->height) > | |
330 (std::numeric_limits<size_t>::max() / image->bytes_per_line)) { | |
331 XDestroyImage(image); | |
332 return false; | |
333 } | |
334 shminfo.shmid = shmget(IPC_PRIVATE, image->bytes_per_line * image->height, | |
335 IPC_CREAT|0666); | |
336 if (shminfo.shmid == -1) { | |
337 XDestroyImage(image); | |
338 return false; | |
339 } | |
340 | |
341 void* mapped_memory = shmat(shminfo.shmid, NULL, SHM_RDONLY); | |
342 shmctl(shminfo.shmid, IPC_RMID, 0); | |
343 if (mapped_memory == (void*)-1) { | |
344 XDestroyImage(image); | |
345 return false; | |
346 } | |
347 shminfo.shmaddr = image->data = static_cast<char*>(mapped_memory); | |
348 | |
349 if (!XShmAttach(display_, &shminfo) || | |
350 !XShmGetImage(display_, pixmap_, image, rect.x(), rect.y(), | |
351 AllPlanes)) { | |
352 DestroySharedImage(display_, image, &shminfo); | |
353 return false; | |
354 } | |
355 } else { | |
356 // Non-shared memory case just copy the image from the server. | |
357 image = XGetImage(display_, pixmap_, | |
358 rect.x(), rect.y(), width, height, | |
359 AllPlanes, ZPixmap); | |
360 } | |
361 | |
362 // TODO(jhawkins): Need to convert the image data if the image bits per pixel | |
363 // is not 32. | |
364 // Note that this also initializes the output bitmap as opaque. | |
365 if (!output->initialize(width, height, true) || | |
366 image->bits_per_pixel != 32) { | |
367 if (shared_memory_support_ != ui::SHARED_MEMORY_NONE) | |
368 DestroySharedImage(display_, image, &shminfo); | |
369 else | |
370 XDestroyImage(image); | |
371 return false; | |
372 } | |
373 | |
374 // The X image might have a different row stride, so iterate through | |
375 // it and copy each row out, only up to the pixels we're actually | |
376 // using. This code assumes a visual mode where a pixel is | |
377 // represented using a 32-bit unsigned int, with a byte per component. | |
378 SkBitmap bitmap = output->getTopPlatformDevice().accessBitmap(true); | |
379 for (int y = 0; y < height; y++) { | |
380 const uint32* src_row = reinterpret_cast<uint32*>( | |
381 &image->data[image->bytes_per_line * y]); | |
382 uint32* dest_row = bitmap.getAddr32(0, y); | |
383 for (int x = 0; x < width; ++x, ++dest_row) { | |
384 // Force alpha to be 0xff, because otherwise it causes rendering problems. | |
385 *dest_row = src_row[x] | 0xff000000; | |
386 } | |
387 } | |
388 | |
389 if (shared_memory_support_ != ui::SHARED_MEMORY_NONE) | |
390 DestroySharedImage(display_, image, &shminfo); | |
391 else | |
392 XDestroyImage(image); | |
393 | |
394 HISTOGRAM_TIMES("BackingStore.RetrievalFromX", | |
395 base::TimeTicks::Now() - begin_time); | |
396 return true; | |
397 } | |
398 | |
399 void BackingStoreX::ScrollBackingStore(int dx, int dy, | |
400 const gfx::Rect& clip_rect, | |
401 const gfx::Size& view_size) { | |
402 if (!display_) | |
403 return; | |
404 | |
405 // We only support scrolling in one direction at a time. | |
406 DCHECK(dx == 0 || dy == 0); | |
407 | |
408 if (dy) { | |
409 // Positive values of |dy| scroll up | |
410 if (abs(dy) < clip_rect.height()) { | |
411 XCopyArea(display_, pixmap_, pixmap_, static_cast<GC>(pixmap_gc_), | |
412 clip_rect.x() /* source x */, | |
413 std::max(clip_rect.y(), clip_rect.y() - dy), | |
414 clip_rect.width(), | |
415 clip_rect.height() - abs(dy), | |
416 clip_rect.x() /* dest x */, | |
417 std::max(clip_rect.y(), clip_rect.y() + dy) /* dest y */); | |
418 } | |
419 } else if (dx) { | |
420 // Positive values of |dx| scroll right | |
421 if (abs(dx) < clip_rect.width()) { | |
422 XCopyArea(display_, pixmap_, pixmap_, static_cast<GC>(pixmap_gc_), | |
423 std::max(clip_rect.x(), clip_rect.x() - dx), | |
424 clip_rect.y() /* source y */, | |
425 clip_rect.width() - abs(dx), | |
426 clip_rect.height(), | |
427 std::max(clip_rect.x(), clip_rect.x() + dx) /* dest x */, | |
428 clip_rect.y() /* dest x */); | |
429 } | |
430 } | |
431 } | |
432 | |
433 void BackingStoreX::XShowRect(const gfx::Point &origin, | |
434 const gfx::Rect& rect, XID target) { | |
435 XCopyArea(display_, pixmap_, target, static_cast<GC>(pixmap_gc_), | |
436 rect.x(), rect.y(), rect.width(), rect.height(), | |
437 rect.x() + origin.x(), rect.y() + origin.y()); | |
438 } | |
439 | |
440 void BackingStoreX::CairoShowRect(const gfx::Rect& rect, | |
441 GdkDrawable* drawable) { | |
442 cairo_surface_t* surface = cairo_xlib_surface_create( | |
443 display_, pixmap_, static_cast<Visual*>(visual_), | |
444 size().width(), size().height()); | |
445 cairo_t* cr = gdk_cairo_create(drawable); | |
446 cairo_set_source_surface(cr, surface, 0, 0); | |
447 | |
448 cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height()); | |
449 cairo_fill(cr); | |
450 cairo_destroy(cr); | |
451 cairo_surface_destroy(surface); | |
452 } | |
453 | |
454 #if defined(TOOLKIT_GTK) | |
455 void BackingStoreX::PaintToRect(const gfx::Rect& rect, GdkDrawable* target) { | |
456 cairo_surface_t* surface = cairo_xlib_surface_create( | |
457 display_, pixmap_, static_cast<Visual*>(visual_), | |
458 size().width(), size().height()); | |
459 cairo_t* cr = gdk_cairo_create(target); | |
460 | |
461 cairo_translate(cr, rect.x(), rect.y()); | |
462 double x_scale = static_cast<double>(rect.width()) / size().width(); | |
463 double y_scale = static_cast<double>(rect.height()) / size().height(); | |
464 cairo_scale(cr, x_scale, y_scale); | |
465 | |
466 cairo_pattern_t* pattern = cairo_pattern_create_for_surface(surface); | |
467 cairo_pattern_set_filter(pattern, CAIRO_FILTER_BEST); | |
468 cairo_set_source(cr, pattern); | |
469 cairo_pattern_destroy(pattern); | |
470 | |
471 cairo_identity_matrix(cr); | |
472 | |
473 cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height()); | |
474 cairo_fill(cr); | |
475 cairo_destroy(cr); | |
476 } | |
477 #endif | |
OLD | NEW |