Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2015)

Side by Side Diff: chrome/browser/renderer_host/backing_store_x.cc

Issue 6532073: Move core pieces of browser\renderer_host to src\content. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698