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

Side by Side Diff: remoting/host/video_frame_capturer_win.cc

Issue 11470028: Move screen capturers to remoting/capturer. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years 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) 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/video_frame_capturer.h"
6
7 #include <windows.h>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/file_path.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/scoped_native_library.h"
15 #include "base/time.h"
16 #include "base/utf_string_conversions.h"
17 #include "base/win/scoped_gdi_object.h"
18 #include "base/win/scoped_hdc.h"
19 #include "remoting/base/capture_data.h"
20 #include "remoting/base/shared_buffer_factory.h"
21 #include "remoting/host/differ.h"
22 #include "remoting/host/video_frame.h"
23 #include "remoting/host/video_frame_capturer_helper.h"
24 #include "remoting/host/video_frame_queue.h"
25 #include "remoting/host/win/desktop.h"
26 #include "remoting/host/win/scoped_thread_desktop.h"
27 #include "remoting/proto/control.pb.h"
28 #include "third_party/skia/include/core/SkColorPriv.h"
29
30 namespace remoting {
31
32 namespace {
33
34 // Constants from dwmapi.h.
35 const UINT DWM_EC_DISABLECOMPOSITION = 0;
36 const UINT DWM_EC_ENABLECOMPOSITION = 1;
37
38 typedef HRESULT (WINAPI * DwmEnableCompositionFunc)(UINT);
39
40 const char kDwmapiLibraryName[] = "dwmapi";
41
42 // Pixel colors used when generating cursor outlines.
43 const uint32 kPixelBgraBlack = 0xff000000;
44 const uint32 kPixelBgraWhite = 0xffffffff;
45 const uint32 kPixelBgraTransparent = 0x00000000;
46
47 // A class representing a full-frame pixel buffer.
48 class VideoFrameWin : public VideoFrame {
49 public:
50 VideoFrameWin(HDC desktop_dc, const SkISize& size,
51 SharedBufferFactory* shared_buffer_factory);
52 virtual ~VideoFrameWin();
53
54 // Returns handle of the device independent bitmap representing this frame
55 // buffer to GDI.
56 HBITMAP GetBitmap();
57
58 private:
59 // Allocates a device independent bitmap representing this frame buffer to
60 // GDI.
61 void AllocateBitmap(HDC desktop_dc, const SkISize& size);
62
63 // Handle of the device independent bitmap representing this frame buffer to
64 // GDI.
65 base::win::ScopedBitmap bitmap_;
66
67 // Used to allocate shared memory buffers if set.
68 SharedBufferFactory* shared_buffer_factory_;
69
70 DISALLOW_COPY_AND_ASSIGN(VideoFrameWin);
71 };
72
73 // VideoFrameCapturerWin captures 32bit RGB using GDI.
74 //
75 // VideoFrameCapturerWin is double-buffered as required by VideoFrameCapturer.
76 // See remoting/host/video_frame_capturer.h.
77 class VideoFrameCapturerWin : public VideoFrameCapturer {
78 public:
79 VideoFrameCapturerWin();
80 explicit VideoFrameCapturerWin(SharedBufferFactory* shared_buffer_factory);
81 virtual ~VideoFrameCapturerWin();
82
83 // Overridden from VideoFrameCapturer:
84 virtual void Start(Delegate* delegate) OVERRIDE;
85 virtual void Stop() OVERRIDE;
86 virtual media::VideoFrame::Format pixel_format() const OVERRIDE;
87 virtual void InvalidateRegion(const SkRegion& invalid_region) OVERRIDE;
88 virtual void CaptureFrame() OVERRIDE;
89 virtual const SkISize& size_most_recent() const OVERRIDE;
90
91 private:
92 // Make sure that the device contexts match the screen configuration.
93 void PrepareCaptureResources();
94
95 // Creates a CaptureData instance wrapping the current framebuffer and
96 // notifies |delegate_|.
97 void CaptureRegion(const SkRegion& region,
98 const base::Time& capture_start_time);
99
100 // Captures the current screen contents into the current buffer.
101 void CaptureImage();
102
103 // Expand the cursor shape to add a white outline for visibility against
104 // dark backgrounds.
105 void AddCursorOutline(int width, int height, uint32* dst);
106
107 // Capture the current cursor shape.
108 void CaptureCursor();
109
110 // Used to allocate shared memory buffers if set.
111 SharedBufferFactory* shared_buffer_factory_;
112
113 Delegate* delegate_;
114
115 // A thread-safe list of invalid rectangles, and the size of the most
116 // recently captured screen.
117 VideoFrameCapturerHelper helper_;
118
119 // Snapshot of the last cursor bitmap we sent to the client. This is used
120 // to diff against the current cursor so we only send a cursor-change
121 // message when the shape has changed.
122 scoped_array<uint8> last_cursor_;
123 SkISize last_cursor_size_;
124
125 ScopedThreadDesktop desktop_;
126
127 // GDI resources used for screen capture.
128 scoped_ptr<base::win::ScopedGetDC> desktop_dc_;
129 base::win::ScopedCreateDC memory_dc_;
130
131 // Queue of the frames buffers.
132 VideoFrameQueue queue_;
133
134 // Rectangle describing the bounds of the desktop device context.
135 SkIRect desktop_dc_rect_;
136
137 // Format of pixels returned in buffer.
138 media::VideoFrame::Format pixel_format_;
139
140 // Class to calculate the difference between two screen bitmaps.
141 scoped_ptr<Differ> differ_;
142
143 base::ScopedNativeLibrary dwmapi_library_;
144 DwmEnableCompositionFunc composition_func_;
145
146 DISALLOW_COPY_AND_ASSIGN(VideoFrameCapturerWin);
147 };
148
149 // 3780 pixels per meter is equivalent to 96 DPI, typical on desktop monitors.
150 static const int kPixelsPerMeter = 3780;
151 // 32 bit RGBA is 4 bytes per pixel.
152 static const int kBytesPerPixel = 4;
153
154 VideoFrameWin::VideoFrameWin(
155 HDC desktop_dc,
156 const SkISize& size,
157 SharedBufferFactory* shared_buffer_factory)
158 : shared_buffer_factory_(shared_buffer_factory) {
159 // Allocate a shared memory buffer.
160 uint32 buffer_size = size.width() * size.height() * kBytesPerPixel;
161 if (shared_buffer_factory_) {
162 scoped_refptr<SharedBuffer> shared_buffer =
163 shared_buffer_factory_->CreateSharedBuffer(buffer_size);
164 CHECK(shared_buffer->ptr() != NULL);
165 set_shared_buffer(shared_buffer);
166 }
167
168 AllocateBitmap(desktop_dc, size);
169 }
170
171 VideoFrameWin::~VideoFrameWin() {
172 if (shared_buffer())
173 shared_buffer_factory_->ReleaseSharedBuffer(shared_buffer());
174 }
175
176 HBITMAP VideoFrameWin::GetBitmap() {
177 return bitmap_;
178 }
179
180 void VideoFrameWin::AllocateBitmap(HDC desktop_dc, const SkISize& size) {
181 int bytes_per_row = size.width() * kBytesPerPixel;
182
183 // Describe a device independent bitmap (DIB) that is the size of the desktop.
184 BITMAPINFO bmi;
185 memset(&bmi, 0, sizeof(bmi));
186 bmi.bmiHeader.biHeight = -size.height();
187 bmi.bmiHeader.biWidth = size.width();
188 bmi.bmiHeader.biPlanes = 1;
189 bmi.bmiHeader.biBitCount = kBytesPerPixel * 8;
190 bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
191 bmi.bmiHeader.biSizeImage = bytes_per_row * size.height();
192 bmi.bmiHeader.biXPelsPerMeter = kPixelsPerMeter;
193 bmi.bmiHeader.biYPelsPerMeter = kPixelsPerMeter;
194
195 // Create the DIB, and store a pointer to its pixel buffer.
196 HANDLE section_handle = NULL;
197 if (shared_buffer())
198 section_handle = shared_buffer()->handle();
199 void* data = NULL;
200 bitmap_ = CreateDIBSection(desktop_dc, &bmi, DIB_RGB_COLORS, &data,
201 section_handle, 0);
202
203 // TODO(wez): Cope gracefully with failure (crbug.com/157170).
204 CHECK(bitmap_ != NULL);
205 CHECK(data != NULL);
206
207 set_pixels(reinterpret_cast<uint8*>(data));
208 set_dimensions(SkISize::Make(bmi.bmiHeader.biWidth,
209 std::abs(bmi.bmiHeader.biHeight)));
210 set_bytes_per_row(
211 bmi.bmiHeader.biSizeImage / std::abs(bmi.bmiHeader.biHeight));
212 }
213
214 VideoFrameCapturerWin::VideoFrameCapturerWin()
215 : shared_buffer_factory_(NULL),
216 delegate_(NULL),
217 last_cursor_size_(SkISize::Make(0, 0)),
218 desktop_dc_rect_(SkIRect::MakeEmpty()),
219 pixel_format_(media::VideoFrame::RGB32),
220 composition_func_(NULL) {
221 }
222
223 VideoFrameCapturerWin::VideoFrameCapturerWin(
224 SharedBufferFactory* shared_buffer_factory)
225 : shared_buffer_factory_(shared_buffer_factory),
226 delegate_(NULL),
227 last_cursor_size_(SkISize::Make(0, 0)),
228 desktop_dc_rect_(SkIRect::MakeEmpty()),
229 pixel_format_(media::VideoFrame::RGB32),
230 composition_func_(NULL) {
231 }
232
233 VideoFrameCapturerWin::~VideoFrameCapturerWin() {
234 }
235
236 media::VideoFrame::Format VideoFrameCapturerWin::pixel_format() const {
237 return pixel_format_;
238 }
239
240 void VideoFrameCapturerWin::InvalidateRegion(const SkRegion& invalid_region) {
241 helper_.InvalidateRegion(invalid_region);
242 }
243
244 void VideoFrameCapturerWin::CaptureFrame() {
245 base::Time capture_start_time = base::Time::Now();
246
247 // Force the system to power-up display hardware, if it has been suspended.
248 SetThreadExecutionState(ES_DISPLAY_REQUIRED);
249
250 // Make sure the GDI capture resources are up-to-date.
251 PrepareCaptureResources();
252
253 // Copy screen bits to the current buffer.
254 CaptureImage();
255
256 const VideoFrame* current_buffer = queue_.current_frame();
257 const VideoFrame* last_buffer = queue_.previous_frame();
258 if (last_buffer) {
259 // Make sure the differencer is set up correctly for these previous and
260 // current screens.
261 if (!differ_.get() ||
262 (differ_->width() != current_buffer->dimensions().width()) ||
263 (differ_->height() != current_buffer->dimensions().height()) ||
264 (differ_->bytes_per_row() != current_buffer->bytes_per_row())) {
265 differ_.reset(new Differ(current_buffer->dimensions().width(),
266 current_buffer->dimensions().height(),
267 kBytesPerPixel,
268 current_buffer->bytes_per_row()));
269 }
270
271 // Calculate difference between the two last captured frames.
272 SkRegion region;
273 differ_->CalcDirtyRegion(last_buffer->pixels(), current_buffer->pixels(),
274 &region);
275 InvalidateRegion(region);
276 } else {
277 // No previous frame is available. Invalidate the whole screen.
278 helper_.InvalidateScreen(current_buffer->dimensions());
279 }
280
281 // Wrap the captured frame into CaptureData structure and invoke
282 // the completion callback.
283 SkRegion invalid_region;
284 helper_.SwapInvalidRegion(&invalid_region);
285 CaptureRegion(invalid_region, capture_start_time);
286
287 // Check for cursor shape update.
288 CaptureCursor();
289 }
290
291 const SkISize& VideoFrameCapturerWin::size_most_recent() const {
292 return helper_.size_most_recent();
293 }
294
295 void VideoFrameCapturerWin::Start(Delegate* delegate) {
296 DCHECK(delegate_ == NULL);
297
298 delegate_ = delegate;
299
300 // Load dwmapi.dll dynamically since it is not available on XP.
301 if (!dwmapi_library_.is_valid()) {
302 FilePath path(base::GetNativeLibraryName(UTF8ToUTF16(kDwmapiLibraryName)));
303 dwmapi_library_.Reset(base::LoadNativeLibrary(path, NULL));
304 }
305
306 if (dwmapi_library_.is_valid() && composition_func_ == NULL) {
307 composition_func_ = static_cast<DwmEnableCompositionFunc>(
308 dwmapi_library_.GetFunctionPointer("DwmEnableComposition"));
309 }
310
311 // Vote to disable Aero composited desktop effects while capturing. Windows
312 // will restore Aero automatically if the process exits. This has no effect
313 // under Windows 8 or higher. See crbug.com/124018.
314 if (composition_func_ != NULL) {
315 (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
316 }
317 }
318
319 void VideoFrameCapturerWin::Stop() {
320 // Restore Aero.
321 if (composition_func_ != NULL) {
322 (*composition_func_)(DWM_EC_ENABLECOMPOSITION);
323 }
324
325 delegate_ = NULL;
326 }
327
328 void VideoFrameCapturerWin::PrepareCaptureResources() {
329 // Switch to the desktop receiving user input if different from the current
330 // one.
331 scoped_ptr<Desktop> input_desktop = Desktop::GetInputDesktop();
332 if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) {
333 // Release GDI resources otherwise SetThreadDesktop will fail.
334 desktop_dc_.reset();
335 memory_dc_.Set(NULL);
336
337 // If SetThreadDesktop() fails, the thread is still assigned a desktop.
338 // So we can continue capture screen bits, just from the wrong desktop.
339 desktop_.SetThreadDesktop(input_desktop.Pass());
340 }
341
342 // If the display bounds have changed then recreate GDI resources.
343 // TODO(wez): Also check for pixel format changes.
344 SkIRect screen_rect(SkIRect::MakeXYWH(
345 GetSystemMetrics(SM_XVIRTUALSCREEN),
346 GetSystemMetrics(SM_YVIRTUALSCREEN),
347 GetSystemMetrics(SM_CXVIRTUALSCREEN),
348 GetSystemMetrics(SM_CYVIRTUALSCREEN)));
349 if (screen_rect != desktop_dc_rect_) {
350 desktop_dc_.reset();
351 memory_dc_.Set(NULL);
352 desktop_dc_rect_.setEmpty();
353 }
354
355 if (desktop_dc_.get() == NULL) {
356 DCHECK(memory_dc_.Get() == NULL);
357
358 // Create GDI device contexts to capture from the desktop into memory.
359 desktop_dc_.reset(new base::win::ScopedGetDC(NULL));
360 memory_dc_.Set(CreateCompatibleDC(*desktop_dc_));
361 desktop_dc_rect_ = screen_rect;
362
363 // Make sure the frame buffers will be reallocated.
364 queue_.SetAllFramesNeedUpdate();
365
366 helper_.ClearInvalidRegion();
367 }
368 }
369
370 void VideoFrameCapturerWin::CaptureRegion(
371 const SkRegion& region,
372 const base::Time& capture_start_time) {
373 const VideoFrame* current_buffer = queue_.current_frame();
374
375 DataPlanes planes;
376 planes.data[0] = current_buffer->pixels();
377 planes.strides[0] = current_buffer->bytes_per_row();
378
379 scoped_refptr<CaptureData> data(new CaptureData(planes,
380 current_buffer->dimensions(),
381 pixel_format_));
382 data->mutable_dirty_region() = region;
383 data->set_shared_buffer(current_buffer->shared_buffer());
384
385 helper_.set_size_most_recent(data->size());
386
387 queue_.DoneWithCurrentFrame();
388
389 data->set_capture_time_ms(
390 (base::Time::Now() - capture_start_time).InMillisecondsRoundedUp());
391 delegate_->OnCaptureCompleted(data);
392 }
393
394 void VideoFrameCapturerWin::CaptureImage() {
395 // If the current buffer is from an older generation then allocate a new one.
396 // Note that we can't reallocate other buffers at this point, since the caller
397 // may still be reading from them.
398 if (queue_.current_frame_needs_update()) {
399 DCHECK(desktop_dc_.get() != NULL);
400 DCHECK(memory_dc_.Get() != NULL);
401 // Windows requires DIB sections' rows to start DWORD-aligned, which is
402 // implicit when working with RGB32 pixels.
403 DCHECK_EQ(pixel_format_, media::VideoFrame::RGB32);
404
405 SkISize size = SkISize::Make(desktop_dc_rect_.width(),
406 desktop_dc_rect_.height());
407 scoped_ptr<VideoFrameWin> buffer(
408 new VideoFrameWin(*desktop_dc_, size, shared_buffer_factory_));
409 queue_.ReplaceCurrentFrame(buffer.PassAs<VideoFrame>());
410 }
411
412 // Select the target bitmap into the memory dc and copy the rect from desktop
413 // to memory.
414 VideoFrameWin* current = static_cast<VideoFrameWin*>(queue_.current_frame());
415 HGDIOBJ previous_object = SelectObject(memory_dc_, current->GetBitmap());
416 if (previous_object != NULL) {
417 BitBlt(memory_dc_,
418 0, 0, desktop_dc_rect_.width(), desktop_dc_rect_.height(),
419 *desktop_dc_,
420 desktop_dc_rect_.x(), desktop_dc_rect_.y(),
421 SRCCOPY | CAPTUREBLT);
422
423 // Select back the previously selected object to that the device contect
424 // could be destroyed independently of the bitmap if needed.
425 SelectObject(memory_dc_, previous_object);
426 }
427 }
428
429 void VideoFrameCapturerWin::AddCursorOutline(int width,
430 int height,
431 uint32* dst) {
432 for (int y = 0; y < height; y++) {
433 for (int x = 0; x < width; x++) {
434 // If this is a transparent pixel (bgr == 0 and alpha = 0), check the
435 // neighbor pixels to see if this should be changed to an outline pixel.
436 if (*dst == kPixelBgraTransparent) {
437 // Change to white pixel if any neighbors (top, bottom, left, right)
438 // are black.
439 if ((y > 0 && dst[-width] == kPixelBgraBlack) ||
440 (y < height - 1 && dst[width] == kPixelBgraBlack) ||
441 (x > 0 && dst[-1] == kPixelBgraBlack) ||
442 (x < width - 1 && dst[1] == kPixelBgraBlack)) {
443 *dst = kPixelBgraWhite;
444 }
445 }
446 dst++;
447 }
448 }
449 }
450
451 void VideoFrameCapturerWin::CaptureCursor() {
452 CURSORINFO cursor_info;
453 cursor_info.cbSize = sizeof(CURSORINFO);
454 if (!GetCursorInfo(&cursor_info)) {
455 VLOG(3) << "Unable to get cursor info. Error = " << GetLastError();
456 return;
457 }
458
459 // Note that this does not need to be freed.
460 HCURSOR hcursor = cursor_info.hCursor;
461 ICONINFO iinfo;
462 if (!GetIconInfo(hcursor, &iinfo)) {
463 VLOG(3) << "Unable to get cursor icon info. Error = " << GetLastError();
464 return;
465 }
466 int hotspot_x = iinfo.xHotspot;
467 int hotspot_y = iinfo.yHotspot;
468
469 // Get the cursor bitmap.
470 base::win::ScopedBitmap hbitmap;
471 BITMAP bitmap;
472 bool color_bitmap;
473 if (iinfo.hbmColor) {
474 // Color cursor bitmap.
475 color_bitmap = true;
476 hbitmap.Set((HBITMAP)CopyImage(iinfo.hbmColor, IMAGE_BITMAP, 0, 0,
477 LR_CREATEDIBSECTION));
478 if (!hbitmap.Get()) {
479 VLOG(3) << "Unable to copy color cursor image. Error = "
480 << GetLastError();
481 return;
482 }
483
484 // Free the color and mask bitmaps since we only need our copy.
485 DeleteObject(iinfo.hbmColor);
486 DeleteObject(iinfo.hbmMask);
487 } else {
488 // Black and white (xor) cursor.
489 color_bitmap = false;
490 hbitmap.Set(iinfo.hbmMask);
491 }
492
493 if (!GetObject(hbitmap.Get(), sizeof(BITMAP), &bitmap)) {
494 VLOG(3) << "Unable to get cursor bitmap. Error = " << GetLastError();
495 return;
496 }
497
498 int width = bitmap.bmWidth;
499 int height = bitmap.bmHeight;
500 // For non-color cursors, the mask contains both an AND and an XOR mask and
501 // the height includes both. Thus, the width is correct, but we need to
502 // divide by 2 to get the correct mask height.
503 if (!color_bitmap) {
504 height /= 2;
505 }
506 int data_size = height * width * kBytesPerPixel;
507
508 scoped_ptr<protocol::CursorShapeInfo> cursor_proto(
509 new protocol::CursorShapeInfo());
510 cursor_proto->mutable_data()->resize(data_size);
511 uint8* cursor_dst_data = const_cast<uint8*>(reinterpret_cast<const uint8*>(
512 cursor_proto->mutable_data()->data()));
513
514 // Copy/convert cursor bitmap into format needed by chromotocol.
515 int row_bytes = bitmap.bmWidthBytes;
516 if (color_bitmap) {
517 if (bitmap.bmPlanes != 1 || bitmap.bmBitsPixel != 32) {
518 VLOG(3) << "Unsupported color cursor format. Error = " << GetLastError();
519 return;
520 }
521
522 // Copy across colour cursor imagery.
523 // CursorShapeInfo stores imagery top-down, and premultiplied
524 // by the alpha channel, whereas windows stores them bottom-up
525 // and not premultiplied.
526 uint8* cursor_src_data = reinterpret_cast<uint8*>(bitmap.bmBits);
527 uint8* src = cursor_src_data + ((height - 1) * row_bytes);
528 uint8* dst = cursor_dst_data;
529 for (int row = 0; row < height; ++row) {
530 for (int column = 0; column < width; ++column) {
531 dst[0] = SkAlphaMul(src[0], src[3]);
532 dst[1] = SkAlphaMul(src[1], src[3]);
533 dst[2] = SkAlphaMul(src[2], src[3]);
534 dst[3] = src[3];
535 dst += kBytesPerPixel;
536 src += kBytesPerPixel;
537 }
538 src -= row_bytes + (width * kBytesPerPixel);
539 }
540 } else {
541 if (bitmap.bmPlanes != 1 || bitmap.bmBitsPixel != 1) {
542 VLOG(3) << "Unsupported cursor mask format. Error = " << GetLastError();
543 return;
544 }
545
546 // x2 because there are 2 masks in the bitmap: AND and XOR.
547 int mask_bytes = height * row_bytes * 2;
548 scoped_array<uint8> mask(new uint8[mask_bytes]);
549 if (!GetBitmapBits(hbitmap.Get(), mask_bytes, mask.get())) {
550 VLOG(3) << "Unable to get cursor mask bits. Error = " << GetLastError();
551 return;
552 }
553 uint8* and_mask = mask.get();
554 uint8* xor_mask = mask.get() + height * row_bytes;
555 uint8* dst = cursor_dst_data;
556 bool add_outline = false;
557 for (int y = 0; y < height; y++) {
558 for (int x = 0; x < width; x++) {
559 int byte = y * row_bytes + x / 8;
560 int bit = 7 - x % 8;
561 int and = and_mask[byte] & (1 << bit);
562 int xor = xor_mask[byte] & (1 << bit);
563
564 // The two cursor masks combine as follows:
565 // AND XOR Windows Result Our result RGB Alpha
566 // 0 0 Black Black 00 ff
567 // 0 1 White White ff ff
568 // 1 0 Screen Transparent 00 00
569 // 1 1 Reverse-screen Black 00 ff
570 // Since we don't support XOR cursors, we replace the "Reverse Screen"
571 // with black. In this case, we also add an outline around the cursor
572 // so that it is visible against a dark background.
573 int rgb = (!and && xor) ? 0xff : 0x00;
574 int alpha = (and && !xor) ? 0x00 : 0xff;
575 *dst++ = rgb;
576 *dst++ = rgb;
577 *dst++ = rgb;
578 *dst++ = alpha;
579 if (and && xor) {
580 add_outline = true;
581 }
582 }
583 }
584 if (add_outline) {
585 AddCursorOutline(width, height,
586 reinterpret_cast<uint32*>(cursor_dst_data));
587 }
588 }
589
590 // Compare the current cursor with the last one we sent to the client. If
591 // they're the same, then don't bother sending the cursor again.
592 if (last_cursor_size_.equals(width, height) &&
593 memcmp(last_cursor_.get(), cursor_dst_data, data_size) == 0) {
594 return;
595 }
596
597 VLOG(3) << "Sending updated cursor: " << width << "x" << height;
598
599 cursor_proto->set_width(width);
600 cursor_proto->set_height(height);
601 cursor_proto->set_hotspot_x(hotspot_x);
602 cursor_proto->set_hotspot_y(hotspot_y);
603
604 // Record the last cursor image that we sent to the client.
605 last_cursor_.reset(new uint8[data_size]);
606 memcpy(last_cursor_.get(), cursor_dst_data, data_size);
607 last_cursor_size_ = SkISize::Make(width, height);
608
609 delegate_->OnCursorShapeChanged(cursor_proto.Pass());
610 }
611
612 } // namespace
613
614 // static
615 scoped_ptr<VideoFrameCapturer> VideoFrameCapturer::Create() {
616 return scoped_ptr<VideoFrameCapturer>(new VideoFrameCapturerWin());
617 }
618
619 // static
620 scoped_ptr<VideoFrameCapturer> VideoFrameCapturer::CreateWithFactory(
621 SharedBufferFactory* shared_buffer_factory) {
622 scoped_ptr<VideoFrameCapturerWin> capturer(
623 new VideoFrameCapturerWin(shared_buffer_factory));
624 return capturer.PassAs<VideoFrameCapturer>();
625 }
626
627 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698