Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2016 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 "content/browser/media/capture/cursor_renderer.h" | |
| 6 | |
| 7 #include "base/logging.h" | |
| 8 #include "base/memory/ptr_util.h" | |
| 9 #include "skia/ext/image_operations.h" | |
| 10 | |
| 11 #if defined(USE_AURA) | |
|
miu
2016/12/27 23:21:03
It seems these platform-specific includes should b
braveyao
2017/01/04 01:57:48
Done.
| |
| 12 #include "content/browser/media/capture/cursor_renderer_aura.h" | |
| 13 #elif defined(OS_MACOSX) | |
| 14 #include "content/browser/media/capture/cursor_renderer_mac.h" | |
| 15 #endif | |
| 16 | |
| 17 namespace content { | |
| 18 | |
| 19 namespace { | |
| 20 | |
| 21 inline int clip_byte(int x) { | |
| 22 return std::max(0, std::min(x, 255)); | |
| 23 } | |
| 24 | |
| 25 inline int alpha_blend(int alpha, int src, int dst) { | |
| 26 return (src * alpha + dst * (255 - alpha)) / 255; | |
| 27 } | |
| 28 | |
| 29 } // namespace | |
| 30 | |
| 31 CursorRenderer::CursorRenderer(gfx::NativeView view, | |
| 32 CursorDisplaySetting cursor_display_setting) | |
| 33 : captured_view_(view), | |
| 34 cursor_display_setting_(cursor_display_setting), | |
| 35 tick_clock_(&default_tick_clock_), | |
| 36 weak_factory_(this) { | |
| 37 Clear(); | |
| 38 } | |
| 39 | |
| 40 CursorRenderer::~CursorRenderer() {} | |
| 41 | |
| 42 base::WeakPtr<CursorRenderer> CursorRenderer::GetWeakPtr() { | |
| 43 return weak_factory_.GetWeakPtr(); | |
| 44 } | |
| 45 | |
| 46 void CursorRenderer::Clear() { | |
| 47 last_cursor_ = gfx::NativeCursor(); | |
| 48 last_cursor_hot_point_ = gfx::Point(); | |
| 49 window_size_when_cursor_last_updated_ = gfx::Size(); | |
| 50 scaled_cursor_bitmap_.reset(); | |
| 51 last_mouse_position_x_ = 0; | |
| 52 last_mouse_position_y_ = 0; | |
| 53 last_mouse_movement_timestamp_ = base::TimeTicks::Now(); | |
| 54 if (cursor_display_setting_ == kCursorEnabledOnMouseMovement) { | |
| 55 cursor_displayed_ = false; | |
| 56 } else { | |
| 57 cursor_displayed_ = true; | |
| 58 } | |
| 59 } | |
| 60 | |
| 61 bool CursorRenderer::SnapshotCursorState(const gfx::Rect& region_in_frame) { | |
| 62 if (!captured_view_) { | |
| 63 DVLOG(2) << "Skipping update with no window being tracked"; | |
|
miu
2016/12/27 23:21:02
nit: For consistency, let's call it a "view" inste
braveyao
2017/01/04 01:57:48
Done.
| |
| 64 return false; | |
| 65 } | |
| 66 | |
| 67 // If we are sharing the root window, or namely the whole screen, we will | |
| 68 // render the mouse cursor. For ordinary window, we only render the mouse | |
| 69 // cursor when the window is active. | |
| 70 if (!IsCapturedViewActive()) { | |
| 71 // Return early if the target window is not active. | |
| 72 DVLOG(2) << "Skipping update on an inactive window"; | |
| 73 Clear(); | |
| 74 return false; | |
| 75 } | |
| 76 | |
| 77 gfx::NativeCursor cursor = GetLastKnownCursor(); | |
|
miu
2016/12/27 23:21:03
Please move this down to just before it will first
braveyao
2017/01/04 01:57:49
Done.
| |
| 78 gfx::Point cursor_position = GetCursorPositionInView(); | |
|
miu
2016/12/27 23:21:03
Please move this down to just before it will first
braveyao
2017/01/04 01:57:48
Done.
| |
| 79 gfx::Size view_size = GetCapturedViewSize(); | |
| 80 | |
| 81 if (view_size.IsEmpty()) { | |
| 82 DVLOG(2) << "Skipping update on an empty view"; | |
| 83 Clear(); | |
| 84 return false; | |
| 85 } | |
| 86 | |
| 87 if (cursor_position.x() < 0 || cursor_position.y() < 0 || | |
|
miu
2016/12/27 23:21:02
nit: A trick to make this simpler:
if (!gfx::Re
braveyao
2017/01/04 01:57:49
Done.
| |
| 88 cursor_position.x() > view_size.width() || | |
| 89 cursor_position.y() > view_size.height()) { | |
| 90 DVLOG(2) << "Skipping update with cursor outside the window"; | |
| 91 Clear(); | |
| 92 return false; | |
| 93 } | |
| 94 | |
| 95 if (cursor_display_setting_ == kCursorEnabledOnMouseMovement) { | |
| 96 if (cursor_displayed_) { | |
| 97 // Stop displaying cursor if there has been no mouse movement | |
| 98 base::TimeTicks now = tick_clock_->NowTicks(); | |
| 99 if ((now - last_mouse_movement_timestamp_) > | |
| 100 base::TimeDelta::FromSeconds(MAX_IDLE_TIME_SECONDS)) { | |
| 101 cursor_displayed_ = false; | |
| 102 DVLOG(2) << "Turning off cursor display after idle time"; | |
| 103 } | |
| 104 } | |
| 105 if (!cursor_displayed_) | |
| 106 return false; | |
| 107 } | |
| 108 | |
| 109 const float x_scale = | |
| 110 static_cast<float>(region_in_frame.width()) / view_size.width(); | |
| 111 const float y_scale = | |
| 112 static_cast<float>(region_in_frame.height()) / view_size.height(); | |
| 113 | |
| 114 if (last_cursor_ != cursor || | |
| 115 window_size_when_cursor_last_updated_ != view_size) { | |
|
miu
2016/12/27 23:21:03
The scaled size also depends on |region_in_frame.s
braveyao
2017/01/04 01:57:48
Done.
| |
| 116 SkBitmap cursor_bitmap = GetLastKnownCursorImage(); | |
| 117 const int scaled_width = cursor_bitmap.width() * x_scale; | |
| 118 const int scaled_height = cursor_bitmap.height() * y_scale; | |
| 119 if (scaled_width <= 0 || scaled_height <= 0) { | |
| 120 DVLOG(2) << "scaled_width <= 0"; | |
| 121 Clear(); | |
| 122 return false; | |
| 123 } | |
| 124 scaled_cursor_bitmap_ = skia::ImageOperations::Resize( | |
| 125 cursor_bitmap, skia::ImageOperations::RESIZE_BEST, scaled_width, | |
| 126 scaled_height); | |
| 127 | |
| 128 last_cursor_hot_point_ = GetLastKnownCursorHotPoint(); | |
| 129 last_cursor_ = cursor; | |
| 130 window_size_when_cursor_last_updated_ = view_size; | |
| 131 } | |
| 132 | |
| 133 cursor_position.Offset(-last_cursor_hot_point_.x(), | |
| 134 -last_cursor_hot_point_.y()); | |
| 135 cursor_position_in_frame_ = | |
| 136 gfx::Point(region_in_frame.x() + cursor_position.x() * x_scale, | |
| 137 region_in_frame.y() + cursor_position.y() * y_scale); | |
| 138 return true; | |
| 139 } | |
| 140 | |
| 141 // Helper function to composite a cursor bitmap on a YUV420 video frame. | |
| 142 void CursorRenderer::RenderOnVideoFrame( | |
| 143 const scoped_refptr<media::VideoFrame>& target) const { | |
| 144 if (scaled_cursor_bitmap_.isNull()) | |
| 145 return; | |
| 146 | |
| 147 DCHECK(target); | |
| 148 | |
| 149 gfx::Rect rect = gfx::IntersectRects( | |
| 150 gfx::Rect(scaled_cursor_bitmap_.width(), scaled_cursor_bitmap_.height()) + | |
| 151 gfx::Vector2d(cursor_position_in_frame_.x(), | |
| 152 cursor_position_in_frame_.y()), | |
| 153 target->visible_rect()); | |
| 154 | |
| 155 scaled_cursor_bitmap_.lockPixels(); | |
| 156 for (int y = rect.y(); y < rect.bottom(); ++y) { | |
| 157 int cursor_y = y - cursor_position_in_frame_.y(); | |
| 158 uint8_t* yplane = target->data(media::VideoFrame::kYPlane) + | |
| 159 y * target->row_bytes(media::VideoFrame::kYPlane); | |
| 160 uint8_t* uplane = target->data(media::VideoFrame::kUPlane) + | |
| 161 (y / 2) * target->row_bytes(media::VideoFrame::kUPlane); | |
| 162 uint8_t* vplane = target->data(media::VideoFrame::kVPlane) + | |
| 163 (y / 2) * target->row_bytes(media::VideoFrame::kVPlane); | |
| 164 for (int x = rect.x(); x < rect.right(); ++x) { | |
| 165 int cursor_x = x - cursor_position_in_frame_.x(); | |
| 166 SkColor color = scaled_cursor_bitmap_.getColor(cursor_x, cursor_y); | |
| 167 int alpha = SkColorGetA(color); | |
| 168 int color_r = SkColorGetR(color); | |
| 169 int color_g = SkColorGetG(color); | |
| 170 int color_b = SkColorGetB(color); | |
| 171 int color_y = clip_byte( | |
| 172 ((color_r * 66 + color_g * 129 + color_b * 25 + 128) >> 8) + 16); | |
| 173 yplane[x] = alpha_blend(alpha, color_y, yplane[x]); | |
| 174 | |
| 175 // Only sample U and V at even coordinates. | |
| 176 if ((x % 2 == 0) && (y % 2 == 0)) { | |
| 177 int color_u = clip_byte( | |
| 178 ((color_r * -38 + color_g * -74 + color_b * 112 + 128) >> 8) + 128); | |
| 179 int color_v = clip_byte( | |
| 180 ((color_r * 112 + color_g * -94 + color_b * -18 + 128) >> 8) + 128); | |
| 181 uplane[x / 2] = alpha_blend(alpha, color_u, uplane[x / 2]); | |
| 182 vplane[x / 2] = alpha_blend(alpha, color_v, vplane[x / 2]); | |
| 183 } | |
| 184 } | |
| 185 } | |
| 186 scaled_cursor_bitmap_.unlockPixels(); | |
| 187 } | |
| 188 | |
| 189 void CursorRenderer::OnMouseMoved(const gfx::Point& location, | |
| 190 base::TimeTicks timestamp) { | |
| 191 if (!cursor_displayed_) { | |
| 192 if (std::abs(location.x() - last_mouse_position_x_) > MIN_MOVEMENT_PIXELS || | |
| 193 std::abs(location.y() - last_mouse_position_y_) > MIN_MOVEMENT_PIXELS) | |
| 194 cursor_displayed_ = true; | |
| 195 } | |
| 196 | |
| 197 if (cursor_displayed_) { | |
| 198 last_mouse_movement_timestamp_ = timestamp; | |
| 199 last_mouse_position_x_ = location.x(); | |
| 200 last_mouse_position_y_ = location.y(); | |
| 201 } | |
| 202 } | |
| 203 | |
| 204 void CursorRenderer::OnMouseClicked(const gfx::Point& location, | |
| 205 base::TimeTicks timestamp) { | |
| 206 cursor_displayed_ = true; | |
| 207 last_mouse_movement_timestamp_ = timestamp; | |
| 208 last_mouse_position_x_ = location.x(); | |
| 209 last_mouse_position_y_ = location.y(); | |
| 210 } | |
| 211 | |
| 212 } // namespace content | |
| OLD | NEW |