OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "remoting/host/capturer.h" | 5 #include "remoting/host/capturer.h" |
6 | 6 |
7 #include <windows.h> | 7 #include <windows.h> |
8 | 8 |
9 #include "base/file_path.h" | 9 #include "base/file_path.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/memory/scoped_ptr.h" | 11 #include "base/memory/scoped_ptr.h" |
12 #include "base/scoped_native_library.h" | 12 #include "base/scoped_native_library.h" |
13 #include "base/utf_string_conversions.h" | 13 #include "base/utf_string_conversions.h" |
14 #include "base/win/scoped_gdi_object.h" | 14 #include "base/win/scoped_gdi_object.h" |
| 15 #include "base/win/scoped_hdc.h" |
15 #include "remoting/base/capture_data.h" | 16 #include "remoting/base/capture_data.h" |
16 #include "remoting/host/capturer_helper.h" | 17 #include "remoting/host/capturer_helper.h" |
17 #include "remoting/host/desktop_win.h" | 18 #include "remoting/host/desktop_win.h" |
18 #include "remoting/host/differ.h" | 19 #include "remoting/host/differ.h" |
19 #include "remoting/host/scoped_thread_desktop_win.h" | 20 #include "remoting/host/scoped_thread_desktop_win.h" |
20 #include "remoting/proto/control.pb.h" | 21 #include "remoting/proto/control.pb.h" |
21 | 22 |
22 namespace remoting { | 23 namespace remoting { |
23 | 24 |
24 namespace { | 25 namespace { |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
68 VideoFrameBuffer() { | 69 VideoFrameBuffer() { |
69 data = 0; | 70 data = 0; |
70 size = SkISize::Make(0, 0); | 71 size = SkISize::Make(0, 0); |
71 bytes_per_pixel = 0; | 72 bytes_per_pixel = 0; |
72 bytes_per_row = 0; | 73 bytes_per_row = 0; |
73 } | 74 } |
74 void* data; | 75 void* data; |
75 SkISize size; | 76 SkISize size; |
76 int bytes_per_pixel; | 77 int bytes_per_pixel; |
77 int bytes_per_row; | 78 int bytes_per_row; |
| 79 int resource_generation; |
78 }; | 80 }; |
79 | 81 |
80 // Make sure that the current buffer has the same size as the screen. | 82 // Make sure that the device contexts and the current bufffer match the screen |
81 void UpdateBufferCapture(const SkISize& size); | 83 // configuration. |
| 84 void PrepareCaptureResources(); |
82 | 85 |
83 // Allocate memory for a buffer of a given size, freeing any memory previously | 86 // Allocates the specified capture buffer using the current device contexts |
84 // allocated for that buffer. | 87 // and desktop dimensions, releasing any pre-existing buffer. |
85 void ReallocateBuffer(int buffer_index, const SkISize& size); | 88 void AllocateBuffer(int buffer_index); |
86 | 89 |
87 void CalculateInvalidRegion(); | 90 void CalculateInvalidRegion(); |
88 void CaptureRegion(const SkRegion& region, | 91 void CaptureRegion(const SkRegion& region, |
89 const CaptureCompletedCallback& callback); | 92 const CaptureCompletedCallback& callback); |
90 | 93 |
91 void ReleaseBuffers(); | |
92 // Generates an image in the current buffer. | 94 // Generates an image in the current buffer. |
93 void CaptureImage(); | 95 void CaptureImage(); |
94 | 96 |
95 // Expand the cursor shape to add a white outline for visibility against | 97 // Expand the cursor shape to add a white outline for visibility against |
96 // dark backgrounds. | 98 // dark backgrounds. |
97 void AddCursorOutline(int width, int height, uint32* dst); | 99 void AddCursorOutline(int width, int height, uint32* dst); |
98 | 100 |
99 // Capture the current cursor shape. | 101 // Capture the current cursor shape. |
100 void CaptureCursor(); | 102 void CaptureCursor(); |
101 | 103 |
102 // Gets the screen size. | |
103 SkISize GetScreenSize(); | |
104 | |
105 // A thread-safe list of invalid rectangles, and the size of the most | 104 // A thread-safe list of invalid rectangles, and the size of the most |
106 // recently captured screen. | 105 // recently captured screen. |
107 CapturerHelper helper_; | 106 CapturerHelper helper_; |
108 | 107 |
109 // Callback notified whenever the cursor shape is changed. | 108 // Callback notified whenever the cursor shape is changed. |
110 CursorShapeChangedCallback cursor_shape_changed_callback_; | 109 CursorShapeChangedCallback cursor_shape_changed_callback_; |
111 | 110 |
112 // Snapshot of the last cursor bitmap we sent to the client. This is used | 111 // Snapshot of the last cursor bitmap we sent to the client. This is used |
113 // to diff against the current cursor so we only send a cursor-change | 112 // to diff against the current cursor so we only send a cursor-change |
114 // message when the shape has changed. | 113 // message when the shape has changed. |
115 scoped_array<uint8> last_cursor_; | 114 scoped_array<uint8> last_cursor_; |
116 SkISize last_cursor_size_; | 115 SkISize last_cursor_size_; |
117 | 116 |
118 // There are two buffers for the screen images, as required by Capturer. | 117 // There are two buffers for the screen images, as required by Capturer. |
119 static const int kNumBuffers = 2; | 118 static const int kNumBuffers = 2; |
120 VideoFrameBuffer buffers_[kNumBuffers]; | 119 VideoFrameBuffer buffers_[kNumBuffers]; |
121 | 120 |
122 ScopedThreadDesktopWin desktop_; | 121 ScopedThreadDesktopWin desktop_; |
123 | 122 |
124 // Gdi specific information about screen. | 123 // GDI resources used for screen capture. |
125 HWND desktop_window_; | 124 scoped_ptr<base::win::ScopedGetDC> desktop_dc_; |
126 HDC desktop_dc_; | 125 base::win::ScopedCreateDC memory_dc_; |
127 HDC memory_dc_; | 126 base::win::ScopedBitmap target_bitmap_[kNumBuffers]; |
128 HBITMAP target_bitmap_[kNumBuffers]; | 127 int resource_generation_; |
129 | 128 |
130 // The screen size attached to the device contexts through which the screen | 129 // Rectangle describing the bounds of the desktop device context. |
131 // is captured. | 130 SkIRect desktop_dc_rect_; |
132 SkISize dc_size_; | |
133 | 131 |
134 // The current buffer with valid data for reading. | 132 // The current buffer with valid data for reading. |
135 int current_buffer_; | 133 int current_buffer_; |
136 | 134 |
137 // Format of pixels returned in buffer. | 135 // Format of pixels returned in buffer. |
138 media::VideoFrame::Format pixel_format_; | 136 media::VideoFrame::Format pixel_format_; |
139 | 137 |
140 // Class to calculate the difference between two screen bitmaps. | 138 // Class to calculate the difference between two screen bitmaps. |
141 scoped_ptr<Differ> differ_; | 139 scoped_ptr<Differ> differ_; |
142 | 140 |
143 base::ScopedNativeLibrary dwmapi_library_; | 141 base::ScopedNativeLibrary dwmapi_library_; |
144 DwmEnableCompositionFunc composition_func_; | 142 DwmEnableCompositionFunc composition_func_; |
145 | 143 |
146 DISALLOW_COPY_AND_ASSIGN(CapturerGdi); | 144 DISALLOW_COPY_AND_ASSIGN(CapturerGdi); |
147 }; | 145 }; |
148 | 146 |
149 // 3780 pixels per meter is equivalent to 96 DPI, typical on desktop monitors. | 147 // 3780 pixels per meter is equivalent to 96 DPI, typical on desktop monitors. |
150 static const int kPixelsPerMeter = 3780; | 148 static const int kPixelsPerMeter = 3780; |
151 // 32 bit RGBA is 4 bytes per pixel. | 149 // 32 bit RGBA is 4 bytes per pixel. |
152 static const int kBytesPerPixel = 4; | 150 static const int kBytesPerPixel = 4; |
153 | 151 |
154 CapturerGdi::CapturerGdi() | 152 CapturerGdi::CapturerGdi() |
155 : last_cursor_size_(SkISize::Make(0, 0)), | 153 : last_cursor_size_(SkISize::Make(0, 0)), |
156 desktop_window_(NULL), | 154 desktop_dc_rect_(SkIRect::MakeEmpty()), |
157 desktop_dc_(NULL), | 155 resource_generation_(0), |
158 memory_dc_(NULL), | |
159 dc_size_(SkISize::Make(0, 0)), | |
160 current_buffer_(0), | 156 current_buffer_(0), |
161 pixel_format_(media::VideoFrame::RGB32), | 157 pixel_format_(media::VideoFrame::RGB32), |
162 composition_func_(NULL) { | 158 composition_func_(NULL) { |
163 memset(target_bitmap_, 0, sizeof(target_bitmap_)); | |
164 memset(buffers_, 0, sizeof(buffers_)); | |
165 ScreenConfigurationChanged(); | 159 ScreenConfigurationChanged(); |
166 } | 160 } |
167 | 161 |
168 CapturerGdi::~CapturerGdi() { | 162 CapturerGdi::~CapturerGdi() { |
169 ReleaseBuffers(); | |
170 } | 163 } |
171 | 164 |
172 media::VideoFrame::Format CapturerGdi::pixel_format() const { | 165 media::VideoFrame::Format CapturerGdi::pixel_format() const { |
173 return pixel_format_; | 166 return pixel_format_; |
174 } | 167 } |
175 | 168 |
176 void CapturerGdi::ClearInvalidRegion() { | 169 void CapturerGdi::ClearInvalidRegion() { |
177 helper_.ClearInvalidRegion(); | 170 helper_.ClearInvalidRegion(); |
178 } | 171 } |
179 | 172 |
(...skipping 21 matching lines...) Expand all Loading... |
201 CaptureRegion(invalid_region, callback); | 194 CaptureRegion(invalid_region, callback); |
202 | 195 |
203 // Check for cursor shape update. | 196 // Check for cursor shape update. |
204 CaptureCursor(); | 197 CaptureCursor(); |
205 } | 198 } |
206 | 199 |
207 const SkISize& CapturerGdi::size_most_recent() const { | 200 const SkISize& CapturerGdi::size_most_recent() const { |
208 return helper_.size_most_recent(); | 201 return helper_.size_most_recent(); |
209 } | 202 } |
210 | 203 |
211 void CapturerGdi::ReleaseBuffers() { | |
212 for (int i = kNumBuffers - 1; i >= 0; i--) { | |
213 if (target_bitmap_[i]) { | |
214 DeleteObject(target_bitmap_[i]); | |
215 target_bitmap_[i] = NULL; | |
216 } | |
217 if (buffers_[i].data) { | |
218 DeleteObject(buffers_[i].data); | |
219 buffers_[i].data = NULL; | |
220 } | |
221 } | |
222 | |
223 if (desktop_dc_) { | |
224 ReleaseDC(desktop_window_, desktop_dc_); | |
225 desktop_window_ = NULL; | |
226 desktop_dc_ = NULL; | |
227 } | |
228 | |
229 if (memory_dc_) { | |
230 DeleteDC(memory_dc_); | |
231 memory_dc_ = NULL; | |
232 } | |
233 } | |
234 | |
235 void CapturerGdi::Start( | 204 void CapturerGdi::Start( |
236 const CursorShapeChangedCallback& callback) { | 205 const CursorShapeChangedCallback& callback) { |
237 cursor_shape_changed_callback_ = callback; | 206 cursor_shape_changed_callback_ = callback; |
238 | 207 |
239 // Load dwmapi.dll dynamically since it is not available on XP. | 208 // Load dwmapi.dll dynamically since it is not available on XP. |
240 if (!dwmapi_library_.is_valid()) { | 209 if (!dwmapi_library_.is_valid()) { |
241 FilePath path(base::GetNativeLibraryName(UTF8ToUTF16(kDwmapiLibraryName))); | 210 FilePath path(base::GetNativeLibraryName(UTF8ToUTF16(kDwmapiLibraryName))); |
242 dwmapi_library_.Reset(base::LoadNativeLibrary(path, NULL)); | 211 dwmapi_library_.Reset(base::LoadNativeLibrary(path, NULL)); |
243 } | 212 } |
244 | 213 |
(...skipping 14 matching lines...) Expand all Loading... |
259 // Restore Aero. | 228 // Restore Aero. |
260 if (composition_func_ != NULL) { | 229 if (composition_func_ != NULL) { |
261 (*composition_func_)(DWM_EC_ENABLECOMPOSITION); | 230 (*composition_func_)(DWM_EC_ENABLECOMPOSITION); |
262 } | 231 } |
263 } | 232 } |
264 | 233 |
265 void CapturerGdi::ScreenConfigurationChanged() { | 234 void CapturerGdi::ScreenConfigurationChanged() { |
266 // We poll for screen configuration changes, so ignore notifications. | 235 // We poll for screen configuration changes, so ignore notifications. |
267 } | 236 } |
268 | 237 |
269 void CapturerGdi::UpdateBufferCapture(const SkISize& size) { | 238 void CapturerGdi::PrepareCaptureResources() { |
270 // Switch to the desktop receiving user input if different from the current | 239 // Switch to the desktop receiving user input if different from the current |
271 // one. | 240 // one. |
272 scoped_ptr<DesktopWin> input_desktop = DesktopWin::GetInputDesktop(); | 241 scoped_ptr<DesktopWin> input_desktop = DesktopWin::GetInputDesktop(); |
273 if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) { | 242 if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) { |
274 // Release GDI resources otherwise SetThreadDesktop will fail. | 243 // Release GDI resources otherwise SetThreadDesktop will fail. |
275 if (desktop_dc_) { | 244 desktop_dc_.reset(); |
276 ReleaseDC(desktop_window_, desktop_dc_); | 245 memory_dc_.Set(NULL); |
277 desktop_window_ = NULL; | |
278 desktop_dc_ = NULL; | |
279 } | |
280 | |
281 if (memory_dc_) { | |
282 DeleteDC(memory_dc_); | |
283 memory_dc_ = NULL; | |
284 } | |
285 | |
286 ReleaseBuffers(); | |
287 | 246 |
288 // If SetThreadDesktop() fails, the thread is still assigned a desktop. | 247 // If SetThreadDesktop() fails, the thread is still assigned a desktop. |
289 // So we can continue capture screen bits, just from a diffented desktop. | 248 // So we can continue capture screen bits, just from the wrong desktop. |
290 desktop_.SetThreadDesktop(input_desktop.Pass()); | 249 desktop_.SetThreadDesktop(input_desktop.Pass()); |
291 } | 250 } |
292 | 251 |
293 // Make sure the DCs have the correct dimensions. | 252 // If the display bounds have changed then recreate GDI resources. |
294 if (size != dc_size_) { | 253 // TODO(wez): Also check for pixel format changes. |
295 // TODO(simonmorris): screen dimensions changing isn't equivalent to needing | 254 SkIRect screen_rect(SkIRect::MakeXYWH( |
296 // a new DC, but it's good enough for now. | 255 GetSystemMetrics(SM_XVIRTUALSCREEN), |
297 if (desktop_dc_) { | 256 GetSystemMetrics(SM_YVIRTUALSCREEN), |
298 ReleaseDC(desktop_window_, desktop_dc_); | 257 GetSystemMetrics(SM_CXVIRTUALSCREEN), |
299 desktop_window_ = NULL; | 258 GetSystemMetrics(SM_CYVIRTUALSCREEN))); |
300 desktop_dc_ = NULL; | 259 if (screen_rect != desktop_dc_rect_) { |
301 } | 260 desktop_dc_.reset(); |
302 | 261 memory_dc_.Set(NULL); |
303 if (memory_dc_) { | 262 desktop_dc_rect_.setEmpty(); |
304 DeleteDC(memory_dc_); | |
305 memory_dc_ = NULL; | |
306 } | |
307 } | 263 } |
308 | 264 |
309 if (desktop_dc_ == NULL) { | 265 // Create GDI device contexts to capture from the desktop into memory, and |
310 DCHECK(desktop_window_ == NULL); | 266 // allocate buffers to capture into. |
311 DCHECK(memory_dc_ == NULL); | 267 if (desktop_dc_.get() == NULL) { |
| 268 DCHECK(memory_dc_.Get() == NULL); |
312 | 269 |
313 desktop_window_ = GetDesktopWindow(); | 270 desktop_dc_.reset(new base::win::ScopedGetDC(NULL)); |
314 desktop_dc_ = GetDC(desktop_window_); | 271 memory_dc_.Set(CreateCompatibleDC(*desktop_dc_)); |
315 memory_dc_ = CreateCompatibleDC(desktop_dc_); | 272 desktop_dc_rect_ = screen_rect; |
316 dc_size_ = size; | 273 |
| 274 ++resource_generation_; |
317 } | 275 } |
318 | 276 |
319 // Make sure the current bitmap has the correct dimensions. | 277 // If the current buffer is from an older generation then allocate a new one. |
320 if (buffers_[current_buffer_].data == NULL || | 278 // Note that we can't reallocate other buffers at this point, since the caller |
321 size != buffers_[current_buffer_].size) { | 279 // may still be reading from them. |
322 ReallocateBuffer(current_buffer_, size); | 280 if (resource_generation_ != buffers_[current_buffer_].resource_generation) { |
| 281 AllocateBuffer(current_buffer_); |
323 InvalidateFullScreen(); | 282 InvalidateFullScreen(); |
324 } | 283 } |
325 } | 284 } |
326 | 285 |
327 void CapturerGdi::ReallocateBuffer(int buffer_index, const SkISize& size) { | 286 void CapturerGdi::AllocateBuffer(int buffer_index) { |
328 // Delete any previously constructed bitmap. | 287 DCHECK(desktop_dc_.get() != NULL); |
329 if (target_bitmap_[buffer_index]) { | 288 DCHECK(memory_dc_.Get() != NULL); |
330 DeleteObject(target_bitmap_[buffer_index]); | |
331 target_bitmap_[buffer_index] = NULL; | |
332 } | |
333 if (buffers_[buffer_index].data) { | |
334 DeleteObject(buffers_[buffer_index].data); | |
335 buffers_[buffer_index].data = NULL; | |
336 } | |
337 | 289 |
338 // Create a bitmap to keep the desktop image. | 290 // Windows requires DIB sections' rows to start DWORD-aligned, which is |
339 int rounded_width = (size.width() + 3) & (~3); | 291 // implicit when working with RGB32 pixels. |
| 292 DCHECK_EQ(pixel_format_, media::VideoFrame::RGB32); |
340 | 293 |
341 // Dimensions of screen. | 294 // Describe a device independent bitmap (DIB) that is the size of the desktop. |
342 pixel_format_ = media::VideoFrame::RGB32; | |
343 int bytes_per_row = rounded_width * kBytesPerPixel; | |
344 | |
345 // Create a device independent bitmap (DIB) that is the same size. | |
346 BITMAPINFO bmi; | 295 BITMAPINFO bmi; |
347 memset(&bmi, 0, sizeof(bmi)); | 296 memset(&bmi, 0, sizeof(bmi)); |
348 bmi.bmiHeader.biHeight = -size.height(); | 297 bmi.bmiHeader.biHeight = -desktop_dc_rect_.height(); |
349 bmi.bmiHeader.biWidth = rounded_width; | 298 bmi.bmiHeader.biWidth = desktop_dc_rect_.width(); |
350 bmi.bmiHeader.biPlanes = 1; | 299 bmi.bmiHeader.biPlanes = 1; |
351 bmi.bmiHeader.biBitCount = kBytesPerPixel * 8; | 300 bmi.bmiHeader.biBitCount = kBytesPerPixel * 8; |
352 bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); | 301 bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); |
353 bmi.bmiHeader.biSizeImage = bytes_per_row * size.height(); | 302 int bytes_per_row = desktop_dc_rect_.width() * kBytesPerPixel; |
| 303 bmi.bmiHeader.biSizeImage = bytes_per_row * desktop_dc_rect_.height(); |
354 bmi.bmiHeader.biXPelsPerMeter = kPixelsPerMeter; | 304 bmi.bmiHeader.biXPelsPerMeter = kPixelsPerMeter; |
355 bmi.bmiHeader.biYPelsPerMeter = kPixelsPerMeter; | 305 bmi.bmiHeader.biYPelsPerMeter = kPixelsPerMeter; |
356 | 306 |
357 // Create memory for the buffers. | 307 // Create the DIB, and store a pointer to its pixel buffer. |
358 target_bitmap_[buffer_index] = | 308 target_bitmap_[buffer_index] = |
359 CreateDIBSection(desktop_dc_, &bmi, DIB_RGB_COLORS, | 309 CreateDIBSection(*desktop_dc_, &bmi, DIB_RGB_COLORS, |
360 static_cast<void**>(&buffers_[buffer_index].data), | 310 static_cast<void**>(&buffers_[buffer_index].data), |
361 NULL, 0); | 311 NULL, 0); |
362 buffers_[buffer_index].size = SkISize::Make(bmi.bmiHeader.biWidth, | 312 buffers_[buffer_index].size = SkISize::Make(bmi.bmiHeader.biWidth, |
363 std::abs(bmi.bmiHeader.biHeight)); | 313 std::abs(bmi.bmiHeader.biHeight)); |
364 buffers_[buffer_index].bytes_per_pixel = bmi.bmiHeader.biBitCount / 8; | 314 buffers_[buffer_index].bytes_per_pixel = bmi.bmiHeader.biBitCount / 8; |
365 buffers_[buffer_index].bytes_per_row = | 315 buffers_[buffer_index].bytes_per_row = |
366 bmi.bmiHeader.biSizeImage / std::abs(bmi.bmiHeader.biHeight); | 316 bmi.bmiHeader.biSizeImage / std::abs(bmi.bmiHeader.biHeight); |
367 } | 317 } |
368 | 318 |
369 void CapturerGdi::CalculateInvalidRegion() { | 319 void CapturerGdi::CalculateInvalidRegion() { |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
416 buffer.size, | 366 buffer.size, |
417 pixel_format_)); | 367 pixel_format_)); |
418 data->mutable_dirty_region() = region; | 368 data->mutable_dirty_region() = region; |
419 | 369 |
420 helper_.set_size_most_recent(data->size()); | 370 helper_.set_size_most_recent(data->size()); |
421 | 371 |
422 callback.Run(data); | 372 callback.Run(data); |
423 } | 373 } |
424 | 374 |
425 void CapturerGdi::CaptureImage() { | 375 void CapturerGdi::CaptureImage() { |
426 // Make sure the structures we use to capture the image have the correct size. | 376 // Make sure the GDI capture resources are up-to-date. |
427 UpdateBufferCapture(GetScreenSize()); | 377 PrepareCaptureResources(); |
428 | 378 |
429 // Select the target bitmap into the memory dc. | 379 // Select the target bitmap into the memory dc. |
430 SelectObject(memory_dc_, target_bitmap_[current_buffer_]); | 380 SelectObject(memory_dc_, target_bitmap_[current_buffer_]); |
431 | 381 |
432 // And then copy the rect from desktop to memory. | 382 // And then copy the rect from desktop to memory. |
433 BitBlt(memory_dc_, 0, 0, buffers_[current_buffer_].size.width(), | 383 BitBlt(memory_dc_, 0, 0, buffers_[current_buffer_].size.width(), |
434 buffers_[current_buffer_].size.height(), desktop_dc_, 0, 0, | 384 buffers_[current_buffer_].size.height(), *desktop_dc_, |
| 385 desktop_dc_rect_.x(), desktop_dc_rect_.y(), |
435 SRCCOPY | CAPTUREBLT); | 386 SRCCOPY | CAPTUREBLT); |
436 } | 387 } |
437 | 388 |
438 void CapturerGdi::AddCursorOutline(int width, int height, uint32* dst) { | 389 void CapturerGdi::AddCursorOutline(int width, int height, uint32* dst) { |
439 for (int y = 0; y < height; y++) { | 390 for (int y = 0; y < height; y++) { |
440 for (int x = 0; x < width; x++) { | 391 for (int x = 0; x < width; x++) { |
441 // If this is a transparent pixel (bgr == 0 and alpha = 0), check the | 392 // If this is a transparent pixel (bgr == 0 and alpha = 0), check the |
442 // neighbor pixels to see if this should be changed to an outline pixel. | 393 // neighbor pixels to see if this should be changed to an outline pixel. |
443 if (*dst == kPixelBgraTransparent) { | 394 if (*dst == kPixelBgraTransparent) { |
444 // Change to white pixel if any neighbors (top, bottom, left, right) | 395 // Change to white pixel if any neighbors (top, bottom, left, right) |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
601 cursor_proto->set_hotspot_y(hotspot_y); | 552 cursor_proto->set_hotspot_y(hotspot_y); |
602 | 553 |
603 // Record the last cursor image that we sent to the client. | 554 // Record the last cursor image that we sent to the client. |
604 last_cursor_.reset(new uint8[data_size]); | 555 last_cursor_.reset(new uint8[data_size]); |
605 memcpy(last_cursor_.get(), cursor_dst_data, data_size); | 556 memcpy(last_cursor_.get(), cursor_dst_data, data_size); |
606 last_cursor_size_ = SkISize::Make(width, height); | 557 last_cursor_size_ = SkISize::Make(width, height); |
607 | 558 |
608 cursor_shape_changed_callback_.Run(cursor_proto.Pass()); | 559 cursor_shape_changed_callback_.Run(cursor_proto.Pass()); |
609 } | 560 } |
610 | 561 |
611 SkISize CapturerGdi::GetScreenSize() { | |
612 return SkISize::Make(GetSystemMetrics(SM_CXSCREEN), | |
613 GetSystemMetrics(SM_CYSCREEN)); | |
614 } | |
615 | |
616 } // namespace | 562 } // namespace |
617 | 563 |
618 // static | 564 // static |
619 Capturer* Capturer::Create() { | 565 Capturer* Capturer::Create() { |
620 return new CapturerGdi(); | 566 return new CapturerGdi(); |
621 } | 567 } |
622 | 568 |
623 } // namespace remoting | 569 } // namespace remoting |
OLD | NEW |