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 "remoting/host/capturer.h" | |
6 | |
7 #include <X11/Xlib.h> | |
8 #include <X11/Xutil.h> | |
9 #include <X11/extensions/Xdamage.h> | |
10 #include <X11/extensions/Xfixes.h> | |
11 | |
12 #include <set> | |
13 | |
14 #include "base/basictypes.h" | |
15 #include "base/logging.h" | |
16 #include "base/memory/scoped_ptr.h" | |
17 #include "remoting/base/capture_data.h" | |
18 #include "remoting/host/capturer_helper.h" | |
19 #include "remoting/host/differ.h" | |
20 #include "remoting/host/x_server_pixel_buffer.h" | |
21 #include "remoting/proto/control.pb.h" | |
22 | |
23 namespace remoting { | |
24 | |
25 namespace { | |
26 | |
27 static const int kBytesPerPixel = 4; | |
28 | |
29 // Default to false, since many systems have broken XDamage support - see | |
30 // http://crbug.com/73423. | |
31 static bool g_should_use_x_damage = false; | |
32 | |
33 static bool ShouldUseXDamage() { | |
34 return g_should_use_x_damage; | |
35 } | |
36 | |
37 // A class representing a full-frame pixel buffer | |
38 class VideoFrameBuffer { | |
39 public: | |
40 VideoFrameBuffer() : bytes_per_row_(0), needs_update_(true) { | |
41 size_.set(0, 0); | |
42 } | |
43 | |
44 void Update(Display* display, Window root_window) { | |
45 if (needs_update_) { | |
46 needs_update_ = false; | |
47 XWindowAttributes root_attr; | |
48 XGetWindowAttributes(display, root_window, &root_attr); | |
49 if (root_attr.width != size_.width() || | |
50 root_attr.height != size_.height()) { | |
51 size_.set(root_attr.width, root_attr.height); | |
52 bytes_per_row_ = size_.width() * kBytesPerPixel; | |
53 size_t buffer_size = size_.width() * size_.height() * kBytesPerPixel; | |
54 ptr_.reset(new uint8[buffer_size]); | |
55 } | |
56 } | |
57 } | |
58 | |
59 SkISize size() const { return size_; } | |
60 int bytes_per_row() const { return bytes_per_row_; } | |
61 uint8* ptr() const { return ptr_.get(); } | |
62 | |
63 void set_needs_update() { needs_update_ = true; } | |
64 | |
65 private: | |
66 SkISize size_; | |
67 int bytes_per_row_; | |
68 scoped_array<uint8> ptr_; | |
69 bool needs_update_; | |
70 | |
71 DISALLOW_COPY_AND_ASSIGN(VideoFrameBuffer); | |
72 }; | |
73 | |
74 // A class to perform capturing for Linux. | |
75 class CapturerLinux : public Capturer { | |
76 public: | |
77 CapturerLinux(); | |
78 virtual ~CapturerLinux(); | |
79 | |
80 bool Init(); // TODO(ajwong): Do we really want this to be synchronous? | |
81 | |
82 // Capturer interface. | |
83 virtual void Start(const CursorShapeChangedCallback& callback) OVERRIDE; | |
84 virtual void Stop() OVERRIDE; | |
85 virtual void ScreenConfigurationChanged() OVERRIDE; | |
86 virtual media::VideoFrame::Format pixel_format() const OVERRIDE; | |
87 virtual void ClearInvalidRegion() OVERRIDE; | |
88 virtual void InvalidateRegion(const SkRegion& invalid_region) OVERRIDE; | |
89 virtual void InvalidateScreen(const SkISize& size) OVERRIDE; | |
90 virtual void InvalidateFullScreen() OVERRIDE; | |
91 virtual void CaptureInvalidRegion( | |
92 const CaptureCompletedCallback& callback) OVERRIDE; | |
93 virtual const SkISize& size_most_recent() const OVERRIDE; | |
94 | |
95 private: | |
96 void InitXDamage(); | |
97 | |
98 // Read and handle all currently-pending XEvents. | |
99 // In the DAMAGE case, process the XDamage events and store the resulting | |
100 // damage rectangles in the CapturerHelper. | |
101 // In all cases, call ScreenConfigurationChanged() in response to any | |
102 // ConfigNotify events. | |
103 void ProcessPendingXEvents(); | |
104 | |
105 // Capture screen pixels, and return the data in a new CaptureData object, | |
106 // to be freed by the caller. | |
107 // In the DAMAGE case, the CapturerHelper already holds the list of invalid | |
108 // rectangles from ProcessPendingXEvents(). | |
109 // In the non-DAMAGE case, this captures the whole screen, then calculates | |
110 // some invalid rectangles that include any differences between this and the | |
111 // previous capture. | |
112 CaptureData* CaptureFrame(); | |
113 | |
114 // Capture the cursor image and call the CursorShapeChangedCallback if it | |
115 // has been set (using SetCursorShapeChangedCallback). | |
116 void CaptureCursor(); | |
117 | |
118 // Synchronize the current buffer with |last_buffer_|, by copying pixels from | |
119 // the area of |last_invalid_rects|. | |
120 // Note this only works on the assumption that kNumBuffers == 2, as | |
121 // |last_invalid_rects| holds the differences from the previous buffer and | |
122 // the one prior to that (which will then be the current buffer). | |
123 void SynchronizeFrame(); | |
124 | |
125 void DeinitXlib(); | |
126 | |
127 // Capture a rectangle from |x_server_pixel_buffer_|, and copy the data into | |
128 // |capture_data|. | |
129 void CaptureRect(const SkIRect& rect, CaptureData* capture_data); | |
130 | |
131 // We expose two forms of blitting to handle variations in the pixel format. | |
132 // In FastBlit, the operation is effectively a memcpy. | |
133 void FastBlit(uint8* image, const SkIRect& rect, CaptureData* capture_data); | |
134 void SlowBlit(uint8* image, const SkIRect& rect, CaptureData* capture_data); | |
135 | |
136 // X11 graphics context. | |
137 Display* display_; | |
138 GC gc_; | |
139 Window root_window_; | |
140 | |
141 // XFixes. | |
142 bool has_xfixes_; | |
143 int xfixes_event_base_; | |
144 int xfixes_error_base_; | |
145 | |
146 // XDamage information. | |
147 bool use_damage_; | |
148 Damage damage_handle_; | |
149 int damage_event_base_; | |
150 int damage_error_base_; | |
151 XserverRegion damage_region_; | |
152 | |
153 // Access to the X Server's pixel buffer. | |
154 XServerPixelBuffer x_server_pixel_buffer_; | |
155 | |
156 // A thread-safe list of invalid rectangles, and the size of the most | |
157 // recently captured screen. | |
158 CapturerHelper helper_; | |
159 | |
160 // Callback notified whenever the cursor shape is changed. | |
161 CursorShapeChangedCallback cursor_shape_changed_callback_; | |
162 | |
163 // Capture state. | |
164 static const int kNumBuffers = 2; | |
165 VideoFrameBuffer buffers_[kNumBuffers]; | |
166 int current_buffer_; | |
167 | |
168 // Format of pixels returned in buffer. | |
169 media::VideoFrame::Format pixel_format_; | |
170 | |
171 // Invalid region from the previous capture. This is used to synchronize the | |
172 // current with the last buffer used. | |
173 SkRegion last_invalid_region_; | |
174 | |
175 // Last capture buffer used. | |
176 uint8* last_buffer_; | |
177 | |
178 // |Differ| for use when polling for changes. | |
179 scoped_ptr<Differ> differ_; | |
180 | |
181 DISALLOW_COPY_AND_ASSIGN(CapturerLinux); | |
182 }; | |
183 | |
184 CapturerLinux::CapturerLinux() | |
185 : display_(NULL), | |
186 gc_(NULL), | |
187 root_window_(BadValue), | |
188 has_xfixes_(false), | |
189 xfixes_event_base_(-1), | |
190 xfixes_error_base_(-1), | |
191 use_damage_(false), | |
192 damage_handle_(0), | |
193 damage_event_base_(-1), | |
194 damage_error_base_(-1), | |
195 damage_region_(0), | |
196 current_buffer_(0), | |
197 pixel_format_(media::VideoFrame::RGB32), | |
198 last_buffer_(NULL) { | |
199 helper_.SetLogGridSize(4); | |
200 } | |
201 | |
202 CapturerLinux::~CapturerLinux() { | |
203 DeinitXlib(); | |
204 } | |
205 | |
206 bool CapturerLinux::Init() { | |
207 // TODO(ajwong): We should specify the display string we are attaching to | |
208 // in the constructor. | |
209 display_ = XOpenDisplay(NULL); | |
210 if (!display_) { | |
211 LOG(ERROR) << "Unable to open display"; | |
212 return false; | |
213 } | |
214 | |
215 x_server_pixel_buffer_.Init(display_); | |
216 | |
217 root_window_ = RootWindow(display_, DefaultScreen(display_)); | |
218 if (root_window_ == BadValue) { | |
219 LOG(ERROR) << "Unable to get the root window"; | |
220 DeinitXlib(); | |
221 return false; | |
222 } | |
223 | |
224 gc_ = XCreateGC(display_, root_window_, 0, NULL); | |
225 if (gc_ == NULL) { | |
226 LOG(ERROR) << "Unable to get graphics context"; | |
227 DeinitXlib(); | |
228 return false; | |
229 } | |
230 | |
231 // Check for XFixes extension. This is required for cursor shape | |
232 // notifications, and for our use of XDamage. | |
233 if (XFixesQueryExtension(display_, &xfixes_event_base_, | |
234 &xfixes_error_base_)) { | |
235 has_xfixes_ = true; | |
236 } else { | |
237 LOG(INFO) << "X server does not support XFixes."; | |
238 } | |
239 | |
240 if (ShouldUseXDamage()) { | |
241 InitXDamage(); | |
242 } | |
243 | |
244 // Register for changes to the dimensions of the root window. | |
245 XSelectInput(display_, root_window_, StructureNotifyMask); | |
246 | |
247 if (has_xfixes_) { | |
248 // Register for changes to the cursor shape. | |
249 XFixesSelectCursorInput(display_, root_window_, | |
250 XFixesDisplayCursorNotifyMask); | |
251 } | |
252 | |
253 return true; | |
254 } | |
255 | |
256 void CapturerLinux::InitXDamage() { | |
257 // Our use of XDamage requires XFixes. | |
258 if (!has_xfixes_) { | |
259 return; | |
260 } | |
261 | |
262 // Check for XDamage extension. | |
263 if (!XDamageQueryExtension(display_, &damage_event_base_, | |
264 &damage_error_base_)) { | |
265 LOG(INFO) << "X server does not support XDamage."; | |
266 return; | |
267 } | |
268 | |
269 // TODO(lambroslambrou): Disable DAMAGE in situations where it is known | |
270 // to fail, such as when Desktop Effects are enabled, with graphics | |
271 // drivers (nVidia, ATI) that fail to report DAMAGE notifications | |
272 // properly. | |
273 | |
274 // Request notifications every time the screen becomes damaged. | |
275 damage_handle_ = XDamageCreate(display_, root_window_, | |
276 XDamageReportNonEmpty); | |
277 if (!damage_handle_) { | |
278 LOG(ERROR) << "Unable to initialize XDamage."; | |
279 return; | |
280 } | |
281 | |
282 // Create an XFixes server-side region to collate damage into. | |
283 damage_region_ = XFixesCreateRegion(display_, 0, 0); | |
284 if (!damage_region_) { | |
285 XDamageDestroy(display_, damage_handle_); | |
286 LOG(ERROR) << "Unable to create XFixes region."; | |
287 return; | |
288 } | |
289 | |
290 use_damage_ = true; | |
291 LOG(INFO) << "Using XDamage extension."; | |
292 } | |
293 | |
294 void CapturerLinux::Start( | |
295 const CursorShapeChangedCallback& callback) { | |
296 cursor_shape_changed_callback_ = callback; | |
297 } | |
298 | |
299 void CapturerLinux::Stop() { | |
300 } | |
301 | |
302 void CapturerLinux::ScreenConfigurationChanged() { | |
303 last_buffer_ = NULL; | |
304 for (int i = 0; i < kNumBuffers; ++i) { | |
305 buffers_[i].set_needs_update(); | |
306 } | |
307 helper_.ClearInvalidRegion(); | |
308 x_server_pixel_buffer_.Init(display_); | |
309 } | |
310 | |
311 media::VideoFrame::Format CapturerLinux::pixel_format() const { | |
312 return pixel_format_; | |
313 } | |
314 | |
315 void CapturerLinux::ClearInvalidRegion() { | |
316 helper_.ClearInvalidRegion(); | |
317 } | |
318 | |
319 void CapturerLinux::InvalidateRegion(const SkRegion& invalid_region) { | |
320 helper_.InvalidateRegion(invalid_region); | |
321 } | |
322 | |
323 void CapturerLinux::InvalidateScreen(const SkISize& size) { | |
324 helper_.InvalidateScreen(size); | |
325 } | |
326 | |
327 void CapturerLinux::InvalidateFullScreen() { | |
328 helper_.InvalidateFullScreen(); | |
329 last_buffer_ = NULL; | |
330 } | |
331 | |
332 void CapturerLinux::CaptureInvalidRegion( | |
333 const CaptureCompletedCallback& callback) { | |
334 // Process XEvents for XDamage and cursor shape tracking. | |
335 ProcessPendingXEvents(); | |
336 | |
337 // Resize the current buffer if there was a recent change of | |
338 // screen-resolution. | |
339 VideoFrameBuffer ¤t = buffers_[current_buffer_]; | |
340 current.Update(display_, root_window_); | |
341 // Also refresh the Differ helper used by CaptureFrame(), if needed. | |
342 if (!use_damage_ && !last_buffer_) { | |
343 differ_.reset(new Differ(current.size().width(), current.size().height(), | |
344 kBytesPerPixel, current.bytes_per_row())); | |
345 } | |
346 | |
347 scoped_refptr<CaptureData> capture_data(CaptureFrame()); | |
348 | |
349 current_buffer_ = (current_buffer_ + 1) % kNumBuffers; | |
350 | |
351 callback.Run(capture_data); | |
352 } | |
353 | |
354 void CapturerLinux::ProcessPendingXEvents() { | |
355 // Find the number of events that are outstanding "now." We don't just loop | |
356 // on XPending because we want to guarantee this terminates. | |
357 int events_to_process = XPending(display_); | |
358 XEvent e; | |
359 | |
360 for (int i = 0; i < events_to_process; i++) { | |
361 XNextEvent(display_, &e); | |
362 if (use_damage_ && (e.type == damage_event_base_ + XDamageNotify)) { | |
363 XDamageNotifyEvent* event = reinterpret_cast<XDamageNotifyEvent*>(&e); | |
364 DCHECK(event->level == XDamageReportNonEmpty); | |
365 } else if (e.type == ConfigureNotify) { | |
366 ScreenConfigurationChanged(); | |
367 } else if (has_xfixes_ && | |
368 e.type == xfixes_event_base_ + XFixesCursorNotify) { | |
369 XFixesCursorNotifyEvent* cne; | |
370 cne = reinterpret_cast<XFixesCursorNotifyEvent*>(&e); | |
371 if (cne->subtype == XFixesDisplayCursorNotify) { | |
372 CaptureCursor(); | |
373 } | |
374 } else { | |
375 LOG(WARNING) << "Got unknown event type: " << e.type; | |
376 } | |
377 } | |
378 } | |
379 | |
380 void CapturerLinux::CaptureCursor() { | |
381 DCHECK(has_xfixes_); | |
382 if (cursor_shape_changed_callback_.is_null()) | |
383 return; | |
384 | |
385 XFixesCursorImage* img = XFixesGetCursorImage(display_); | |
386 if (!img) { | |
387 return; | |
388 } | |
389 | |
390 int width = img->width; | |
391 int height = img->height; | |
392 int total_bytes = width * height * kBytesPerPixel; | |
393 | |
394 scoped_ptr<protocol::CursorShapeInfo> cursor_proto( | |
395 new protocol::CursorShapeInfo()); | |
396 cursor_proto->set_width(width); | |
397 cursor_proto->set_height(height); | |
398 cursor_proto->set_hotspot_x(img->xhot); | |
399 cursor_proto->set_hotspot_y(img->yhot); | |
400 | |
401 cursor_proto->mutable_data()->resize(total_bytes); | |
402 uint8* proto_data = const_cast<uint8*>(reinterpret_cast<const uint8*>( | |
403 cursor_proto->mutable_data()->data())); | |
404 | |
405 // Xlib stores 32-bit data in longs, even if longs are 64-bits long. | |
406 unsigned long* src = img->pixels; | |
407 uint32* dst = reinterpret_cast<uint32*>(proto_data); | |
408 uint32* dst_end = dst + (width * height); | |
409 while (dst < dst_end) { | |
410 *dst++ = static_cast<uint32>(*src++); | |
411 } | |
412 XFree(img); | |
413 | |
414 cursor_shape_changed_callback_.Run(cursor_proto.Pass()); | |
415 } | |
416 | |
417 CaptureData* CapturerLinux::CaptureFrame() { | |
418 VideoFrameBuffer& buffer = buffers_[current_buffer_]; | |
419 DataPlanes planes; | |
420 planes.data[0] = buffer.ptr(); | |
421 planes.strides[0] = buffer.bytes_per_row(); | |
422 | |
423 CaptureData* capture_data = new CaptureData(planes, buffer.size(), | |
424 media::VideoFrame::RGB32); | |
425 | |
426 // Pass the screen size to the helper, so it can clip the invalid region if it | |
427 // expands that region to a grid. | |
428 helper_.set_size_most_recent(capture_data->size()); | |
429 | |
430 // In the DAMAGE case, ensure the frame is up-to-date with the previous frame | |
431 // if any. If there isn't a previous frame, that means a screen-resolution | |
432 // change occurred, and |invalid_rects| will be updated to include the whole | |
433 // screen. | |
434 if (use_damage_ && last_buffer_) | |
435 SynchronizeFrame(); | |
436 | |
437 SkRegion invalid_region; | |
438 | |
439 x_server_pixel_buffer_.Synchronize(); | |
440 if (use_damage_ && last_buffer_) { | |
441 // Atomically fetch and clear the damage region. | |
442 XDamageSubtract(display_, damage_handle_, None, damage_region_); | |
443 int nRects = 0; | |
444 XRectangle bounds; | |
445 XRectangle* rects = XFixesFetchRegionAndBounds(display_, damage_region_, | |
446 &nRects, &bounds); | |
447 for (int i=0; i<nRects; ++i) { | |
448 invalid_region.op(SkIRect::MakeXYWH(rects[i].x, rects[i].y, | |
449 rects[i].width, rects[i].height), | |
450 SkRegion::kUnion_Op); | |
451 } | |
452 XFree(rects); | |
453 helper_.InvalidateRegion(invalid_region); | |
454 | |
455 // Capture the damaged portions of the desktop. | |
456 helper_.SwapInvalidRegion(&invalid_region); | |
457 for (SkRegion::Iterator it(invalid_region); !it.done(); it.next()) { | |
458 CaptureRect(it.rect(), capture_data); | |
459 } | |
460 } else { | |
461 // Doing full-screen polling, or this is the first capture after a | |
462 // screen-resolution change. In either case, need a full-screen capture. | |
463 SkIRect screen_rect = SkIRect::MakeWH(buffer.size().width(), | |
464 buffer.size().height()); | |
465 CaptureRect(screen_rect, capture_data); | |
466 | |
467 if (last_buffer_) { | |
468 // Full-screen polling, so calculate the invalid rects here, based on the | |
469 // changed pixels between current and previous buffers. | |
470 DCHECK(differ_ != NULL); | |
471 differ_->CalcDirtyRegion(last_buffer_, buffer.ptr(), &invalid_region); | |
472 } else { | |
473 // No previous buffer, so always invalidate the whole screen, whether | |
474 // or not DAMAGE is being used. DAMAGE doesn't necessarily send a | |
475 // full-screen notification after a screen-resolution change, so | |
476 // this is done here. | |
477 invalid_region.op(screen_rect, SkRegion::kUnion_Op); | |
478 } | |
479 } | |
480 | |
481 capture_data->mutable_dirty_region() = invalid_region; | |
482 last_invalid_region_ = invalid_region; | |
483 last_buffer_ = buffer.ptr(); | |
484 return capture_data; | |
485 } | |
486 | |
487 void CapturerLinux::SynchronizeFrame() { | |
488 // Synchronize the current buffer with the previous one since we do not | |
489 // capture the entire desktop. Note that encoder may be reading from the | |
490 // previous buffer at this time so thread access complaints are false | |
491 // positives. | |
492 | |
493 // TODO(hclam): We can reduce the amount of copying here by subtracting | |
494 // |capturer_helper_|s region from |last_invalid_region_|. | |
495 // http://crbug.com/92354 | |
496 DCHECK(last_buffer_); | |
497 VideoFrameBuffer& buffer = buffers_[current_buffer_]; | |
498 for (SkRegion::Iterator it(last_invalid_region_); !it.done(); it.next()) { | |
499 const SkIRect& r = it.rect(); | |
500 int offset = r.fTop * buffer.bytes_per_row() + r.fLeft * kBytesPerPixel; | |
501 for (int i = 0; i < r.height(); ++i) { | |
502 memcpy(buffer.ptr() + offset, last_buffer_ + offset, | |
503 r.width() * kBytesPerPixel); | |
504 offset += buffer.size().width() * kBytesPerPixel; | |
505 } | |
506 } | |
507 } | |
508 | |
509 void CapturerLinux::DeinitXlib() { | |
510 if (gc_) { | |
511 XFreeGC(display_, gc_); | |
512 gc_ = NULL; | |
513 } | |
514 | |
515 x_server_pixel_buffer_.Release(); | |
516 | |
517 if (display_) { | |
518 if (damage_handle_) | |
519 XDamageDestroy(display_, damage_handle_); | |
520 if (damage_region_) | |
521 XFixesDestroyRegion(display_, damage_region_); | |
522 XCloseDisplay(display_); | |
523 display_ = NULL; | |
524 damage_handle_ = 0; | |
525 damage_region_ = 0; | |
526 } | |
527 } | |
528 | |
529 void CapturerLinux::CaptureRect(const SkIRect& rect, | |
530 CaptureData* capture_data) { | |
531 uint8* image = x_server_pixel_buffer_.CaptureRect(rect); | |
532 int depth = x_server_pixel_buffer_.GetDepth(); | |
533 int bpp = x_server_pixel_buffer_.GetBitsPerPixel(); | |
534 bool is_rgb = x_server_pixel_buffer_.IsRgb(); | |
535 if ((depth == 24 || depth == 32) && bpp == 32 && is_rgb) { | |
536 DVLOG(3) << "Fast blitting"; | |
537 FastBlit(image, rect, capture_data); | |
538 } else { | |
539 DVLOG(3) << "Slow blitting"; | |
540 SlowBlit(image, rect, capture_data); | |
541 } | |
542 } | |
543 | |
544 void CapturerLinux::FastBlit(uint8* image, const SkIRect& rect, | |
545 CaptureData* capture_data) { | |
546 uint8* src_pos = image; | |
547 int src_stride = x_server_pixel_buffer_.GetStride(); | |
548 int dst_x = rect.fLeft, dst_y = rect.fTop; | |
549 | |
550 DataPlanes planes = capture_data->data_planes(); | |
551 uint8* dst_buffer = planes.data[0]; | |
552 | |
553 const int dst_stride = planes.strides[0]; | |
554 | |
555 uint8* dst_pos = dst_buffer + dst_stride * dst_y; | |
556 dst_pos += dst_x * kBytesPerPixel; | |
557 | |
558 int height = rect.height(), row_bytes = rect.width() * kBytesPerPixel; | |
559 for (int y = 0; y < height; ++y) { | |
560 memcpy(dst_pos, src_pos, row_bytes); | |
561 src_pos += src_stride; | |
562 dst_pos += dst_stride; | |
563 } | |
564 } | |
565 | |
566 void CapturerLinux::SlowBlit(uint8* image, const SkIRect& rect, | |
567 CaptureData* capture_data) { | |
568 DataPlanes planes = capture_data->data_planes(); | |
569 uint8* dst_buffer = planes.data[0]; | |
570 const int dst_stride = planes.strides[0]; | |
571 int src_stride = x_server_pixel_buffer_.GetStride(); | |
572 int dst_x = rect.fLeft, dst_y = rect.fTop; | |
573 int width = rect.width(), height = rect.height(); | |
574 | |
575 unsigned int red_mask = x_server_pixel_buffer_.GetRedMask(); | |
576 unsigned int blue_mask = x_server_pixel_buffer_.GetBlueMask(); | |
577 unsigned int green_mask = x_server_pixel_buffer_.GetGreenMask(); | |
578 unsigned int red_shift = x_server_pixel_buffer_.GetRedShift(); | |
579 unsigned int blue_shift = x_server_pixel_buffer_.GetBlueShift(); | |
580 unsigned int green_shift = x_server_pixel_buffer_.GetGreenShift(); | |
581 | |
582 unsigned int max_red = red_mask >> red_shift; | |
583 unsigned int max_blue = blue_mask >> blue_shift; | |
584 unsigned int max_green = green_mask >> green_shift; | |
585 | |
586 unsigned int bits_per_pixel = x_server_pixel_buffer_.GetBitsPerPixel(); | |
587 | |
588 uint8* dst_pos = dst_buffer + dst_stride * dst_y; | |
589 uint8* src_pos = image; | |
590 dst_pos += dst_x * kBytesPerPixel; | |
591 // TODO(hclam): Optimize, perhaps using MMX code or by converting to | |
592 // YUV directly | |
593 for (int y = 0; y < height; y++) { | |
594 uint32_t* dst_pos_32 = reinterpret_cast<uint32_t*>(dst_pos); | |
595 uint32_t* src_pos_32 = reinterpret_cast<uint32_t*>(src_pos); | |
596 uint16_t* src_pos_16 = reinterpret_cast<uint16_t*>(src_pos); | |
597 for (int x = 0; x < width; x++) { | |
598 // Dereference through an appropriately-aligned pointer. | |
599 uint32_t pixel; | |
600 if (bits_per_pixel == 32) | |
601 pixel = src_pos_32[x]; | |
602 else if (bits_per_pixel == 16) | |
603 pixel = src_pos_16[x]; | |
604 else | |
605 pixel = src_pos[x]; | |
606 uint32_t r = (((pixel & red_mask) >> red_shift) * 255) / max_red; | |
607 uint32_t b = (((pixel & blue_mask) >> blue_shift) * 255) / max_blue; | |
608 uint32_t g = (((pixel & green_mask) >> green_shift) * 255) / max_green; | |
609 // Write as 32-bit RGB. | |
610 dst_pos_32[x] = r << 16 | g << 8 | b; | |
611 } | |
612 dst_pos += dst_stride; | |
613 src_pos += src_stride; | |
614 } | |
615 } | |
616 | |
617 const SkISize& CapturerLinux::size_most_recent() const { | |
618 return helper_.size_most_recent(); | |
619 } | |
620 | |
621 } // namespace | |
622 | |
623 // static | |
624 Capturer* Capturer::Create() { | |
625 CapturerLinux* capturer = new CapturerLinux(); | |
626 if (!capturer->Init()) { | |
627 delete capturer; | |
628 capturer = NULL; | |
629 } | |
630 return capturer; | |
631 } | |
632 | |
633 // static | |
634 void Capturer::EnableXDamage(bool enable) { | |
635 g_should_use_x_damage = enable; | |
636 } | |
637 | |
638 } // namespace remoting | |
OLD | NEW |