| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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_gdi.h" | |
| 6 #include "remoting/host/differ.h" | |
| 7 | |
| 8 #include "ui/gfx/rect.h" | |
| 9 | |
| 10 namespace remoting { | |
| 11 | |
| 12 // 3780 pixels per meter is equivalent to 96 DPI, typical on desktop monitors. | |
| 13 static const int kPixelsPerMeter = 3780; | |
| 14 // 32 bit RGBA is 4 bytes per pixel. | |
| 15 static const int kBytesPerPixel = 4; | |
| 16 | |
| 17 CapturerGdi::CapturerGdi() | |
| 18 : desktop_dc_(NULL), | |
| 19 memory_dc_(NULL), | |
| 20 dc_size_(0, 0), | |
| 21 current_buffer_(0), | |
| 22 pixel_format_(media::VideoFrame::RGB32), | |
| 23 capture_fullscreen_(true) { | |
| 24 memset(target_bitmap_, 0, sizeof(target_bitmap_)); | |
| 25 memset(buffers_, 0, sizeof(buffers_)); | |
| 26 ScreenConfigurationChanged(); | |
| 27 } | |
| 28 | |
| 29 CapturerGdi::~CapturerGdi() { | |
| 30 ReleaseBuffers(); | |
| 31 } | |
| 32 | |
| 33 media::VideoFrame::Format CapturerGdi::pixel_format() const { | |
| 34 return pixel_format_; | |
| 35 } | |
| 36 | |
| 37 void CapturerGdi::ClearInvalidRects() { | |
| 38 helper.ClearInvalidRects(); | |
| 39 } | |
| 40 | |
| 41 void CapturerGdi::InvalidateRects(const InvalidRects& inval_rects) { | |
| 42 helper.InvalidateRects(inval_rects); | |
| 43 } | |
| 44 | |
| 45 void CapturerGdi::InvalidateScreen(const gfx::Size& size) { | |
| 46 helper.InvalidateScreen(size); | |
| 47 } | |
| 48 | |
| 49 void CapturerGdi::InvalidateFullScreen() { | |
| 50 helper.InvalidateFullScreen(); | |
| 51 } | |
| 52 | |
| 53 void CapturerGdi::CaptureInvalidRects(CaptureCompletedCallback* callback) { | |
| 54 CalculateInvalidRects(); | |
| 55 InvalidRects inval_rects; | |
| 56 helper.SwapInvalidRects(inval_rects); | |
| 57 CaptureRects(inval_rects, callback); | |
| 58 } | |
| 59 | |
| 60 const gfx::Size& CapturerGdi::size_most_recent() const { | |
| 61 return helper.size_most_recent(); | |
| 62 } | |
| 63 | |
| 64 void CapturerGdi::ReleaseBuffers() { | |
| 65 for (int i = kNumBuffers - 1; i >= 0; i--) { | |
| 66 if (target_bitmap_[i]) { | |
| 67 DeleteObject(target_bitmap_[i]); | |
| 68 target_bitmap_[i] = NULL; | |
| 69 } | |
| 70 if (buffers_[i].data) { | |
| 71 DeleteObject(buffers_[i].data); | |
| 72 buffers_[i].data = NULL; | |
| 73 } | |
| 74 } | |
| 75 | |
| 76 desktop_dc_ = NULL; | |
| 77 if (memory_dc_) { | |
| 78 DeleteDC(memory_dc_); | |
| 79 memory_dc_ = NULL; | |
| 80 } | |
| 81 } | |
| 82 | |
| 83 void CapturerGdi::ScreenConfigurationChanged() { | |
| 84 // We poll for screen configuration changes, so ignore notifications. | |
| 85 } | |
| 86 | |
| 87 void CapturerGdi::UpdateBufferCapture(const gfx::Size& size) { | |
| 88 // Make sure the DCs have the correct dimensions. | |
| 89 if (size != dc_size_) { | |
| 90 // TODO(simonmorris): screen dimensions changing isn't equivalent to needing | |
| 91 // a new DC, but it's good enough for now. | |
| 92 desktop_dc_ = GetDC(GetDesktopWindow()); | |
| 93 if (memory_dc_) | |
| 94 DeleteDC(memory_dc_); | |
| 95 memory_dc_ = CreateCompatibleDC(desktop_dc_); | |
| 96 dc_size_ = size; | |
| 97 } | |
| 98 | |
| 99 // Make sure the current bitmap has the correct dimensions. | |
| 100 if (size != buffers_[current_buffer_].size) { | |
| 101 ReallocateBuffer(current_buffer_, size); | |
| 102 capture_fullscreen_ = true; | |
| 103 } | |
| 104 } | |
| 105 | |
| 106 void CapturerGdi::ReallocateBuffer(int buffer_index, const gfx::Size& size) { | |
| 107 // Delete any previously constructed bitmap. | |
| 108 if (target_bitmap_[buffer_index]) { | |
| 109 DeleteObject(target_bitmap_[buffer_index]); | |
| 110 target_bitmap_[buffer_index] = NULL; | |
| 111 } | |
| 112 if (buffers_[buffer_index].data) { | |
| 113 DeleteObject(buffers_[buffer_index].data); | |
| 114 buffers_[buffer_index].data = NULL; | |
| 115 } | |
| 116 | |
| 117 // Create a bitmap to keep the desktop image. | |
| 118 int rounded_width = (size.width() + 3) & (~3); | |
| 119 | |
| 120 // Dimensions of screen. | |
| 121 pixel_format_ = media::VideoFrame::RGB32; | |
| 122 int bytes_per_row = rounded_width * kBytesPerPixel; | |
| 123 | |
| 124 // Create a device independent bitmap (DIB) that is the same size. | |
| 125 BITMAPINFO bmi; | |
| 126 memset(&bmi, 0, sizeof(bmi)); | |
| 127 bmi.bmiHeader.biHeight = -size.height(); | |
| 128 bmi.bmiHeader.biWidth = size.width(); | |
| 129 bmi.bmiHeader.biPlanes = 1; | |
| 130 bmi.bmiHeader.biBitCount = kBytesPerPixel * 8; | |
| 131 bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); | |
| 132 bmi.bmiHeader.biSizeImage = bytes_per_row * size.height(); | |
| 133 bmi.bmiHeader.biXPelsPerMeter = kPixelsPerMeter; | |
| 134 bmi.bmiHeader.biYPelsPerMeter = kPixelsPerMeter; | |
| 135 | |
| 136 // Create memory for the buffers. | |
| 137 target_bitmap_[buffer_index] = | |
| 138 CreateDIBSection(desktop_dc_, &bmi, DIB_RGB_COLORS, | |
| 139 static_cast<void**>(&buffers_[buffer_index].data), | |
| 140 NULL, 0); | |
| 141 buffers_[buffer_index].size = gfx::Size(bmi.bmiHeader.biWidth, | |
| 142 std::abs(bmi.bmiHeader.biHeight)); | |
| 143 buffers_[buffer_index].bytes_per_pixel = bmi.bmiHeader.biBitCount / 8; | |
| 144 buffers_[buffer_index].bytes_per_row = | |
| 145 bmi.bmiHeader.biSizeImage / std::abs(bmi.bmiHeader.biHeight); | |
| 146 } | |
| 147 | |
| 148 void CapturerGdi::CalculateInvalidRects() { | |
| 149 CaptureImage(); | |
| 150 | |
| 151 const VideoFrameBuffer& current = buffers_[current_buffer_]; | |
| 152 if (helper.IsCaptureFullScreen(current.size)) | |
| 153 capture_fullscreen_ = true; | |
| 154 | |
| 155 if (capture_fullscreen_) { | |
| 156 InvalidateScreen(current.size); | |
| 157 capture_fullscreen_ = false; | |
| 158 return; | |
| 159 } | |
| 160 | |
| 161 // Find the previous and current screens. | |
| 162 int prev_buffer_id = current_buffer_ - 1; | |
| 163 if (prev_buffer_id < 0) { | |
| 164 prev_buffer_id = kNumBuffers - 1; | |
| 165 } | |
| 166 const VideoFrameBuffer& prev = buffers_[prev_buffer_id]; | |
| 167 | |
| 168 // Maybe the previous and current screens can't be differenced. | |
| 169 if ((current.size != prev.size) || | |
| 170 (current.bytes_per_pixel != prev.bytes_per_pixel) || | |
| 171 (current.bytes_per_row != prev.bytes_per_row)) { | |
| 172 InvalidateScreen(current.size); | |
| 173 return; | |
| 174 } | |
| 175 | |
| 176 // Make sure the differencer is set up correctly for these previous and | |
| 177 // current screens. | |
| 178 if (!differ_.get() || | |
| 179 (differ_->width() != current.size.width()) || | |
| 180 (differ_->height() != current.size.height()) || | |
| 181 (differ_->bytes_per_pixel() != current.bytes_per_pixel) || | |
| 182 (differ_->bytes_per_row() != current.bytes_per_row)) { | |
| 183 differ_.reset(new Differ(current.size.width(), current.size.height(), | |
| 184 current.bytes_per_pixel, current.bytes_per_row)); | |
| 185 } | |
| 186 | |
| 187 InvalidRects rects; | |
| 188 differ_->CalcDirtyRects(prev.data, current.data, &rects); | |
| 189 | |
| 190 InvalidateRects(rects); | |
| 191 } | |
| 192 | |
| 193 void CapturerGdi::CaptureRects(const InvalidRects& rects, | |
| 194 CaptureCompletedCallback* callback) { | |
| 195 scoped_ptr<CaptureCompletedCallback> callback_deleter(callback); | |
| 196 | |
| 197 const VideoFrameBuffer& buffer = buffers_[current_buffer_]; | |
| 198 current_buffer_ = (current_buffer_ + 1) % kNumBuffers; | |
| 199 | |
| 200 DataPlanes planes; | |
| 201 planes.data[0] = static_cast<uint8*>(buffer.data); | |
| 202 planes.strides[0] = buffer.bytes_per_row; | |
| 203 | |
| 204 scoped_refptr<CaptureData> data(new CaptureData(planes, | |
| 205 buffer.size, | |
| 206 pixel_format_)); | |
| 207 data->mutable_dirty_rects() = rects; | |
| 208 | |
| 209 helper.set_size_most_recent(data->size()); | |
| 210 | |
| 211 callback->Run(data); | |
| 212 } | |
| 213 | |
| 214 void CapturerGdi::CaptureImage() { | |
| 215 // Make sure the structures we use to capture the image have the correct size. | |
| 216 UpdateBufferCapture(GetScreenSize()); | |
| 217 | |
| 218 // Select the target bitmap into the memory dc. | |
| 219 SelectObject(memory_dc_, target_bitmap_[current_buffer_]); | |
| 220 | |
| 221 // And then copy the rect from desktop to memory. | |
| 222 BitBlt(memory_dc_, 0, 0, buffers_[current_buffer_].size.width(), | |
| 223 buffers_[current_buffer_].size.height(), desktop_dc_, 0, 0, | |
| 224 SRCCOPY | CAPTUREBLT); | |
| 225 } | |
| 226 | |
| 227 gfx::Size CapturerGdi::GetScreenSize() { | |
| 228 return gfx::Size(GetSystemMetrics(SM_CXSCREEN), | |
| 229 GetSystemMetrics(SM_CYSCREEN)); | |
| 230 } | |
| 231 | |
| 232 // static | |
| 233 Capturer* Capturer::Create() { | |
| 234 return new CapturerGdi(); | |
| 235 } | |
| 236 | |
| 237 } // namespace remoting | |
| OLD | NEW |