Chromium Code Reviews| Index: content/browser/media/capture/cursor_renderer.cc |
| diff --git a/content/browser/media/capture/cursor_renderer.cc b/content/browser/media/capture/cursor_renderer.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..8444d8be779e80f331f4c7e0986c7b72cbeea69c |
| --- /dev/null |
| +++ b/content/browser/media/capture/cursor_renderer.cc |
| @@ -0,0 +1,173 @@ |
| +// Copyright (c) 2015 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "content/browser/media/capture/cursor_renderer.h" |
| + |
| +#include <algorithm> |
| + |
| +#include "base/logging.h" |
| +#include "ui/aura/client/screen_position_client.h" |
| +#include "ui/aura/env.h" |
| +#include "ui/aura/window.h" |
| +#include "ui/aura/window_tree_host.h" |
| +#include "ui/base/cursor/cursors_aura.h" |
| +#include "ui/compositor/dip_util.h" |
| +#include "ui/compositor/layer.h" |
| + |
| +namespace content { |
| + |
| +namespace { |
| + |
| +int clip_byte(int x) { |
| + return std::max(0, std::min(x, 255)); |
| +} |
| + |
| +int alpha_blend(int alpha, int src, int dst) { |
| + return (src * alpha + dst * (255 - alpha)) / 255; |
| +} |
| + |
| +} // namespace |
| + |
| +CursorRenderer::CursorRenderer(aura::Window* window, bool enable_mouse_events) |
| + : window_(window), enable_mouse_events_(enable_mouse_events) { |
| + if (enable_mouse_events_) |
| + window_->AddPreTargetHandler(this); |
| + Clear(); |
| +} |
| + |
| +CursorRenderer::~CursorRenderer() { |
| + if (enable_mouse_events_) |
| + window_->RemovePreTargetHandler(this); |
| +} |
| + |
| +void CursorRenderer::Clear() { |
| + last_cursor_ = ui::Cursor(); |
| + window_size_when_cursor_last_updated_ = gfx::Size(); |
| + scaled_cursor_bitmap_.reset(); |
| +} |
| + |
| +bool CursorRenderer::Update(const gfx::Rect& region_in_frame) { |
| + const gfx::Rect window_bounds = window_->GetBoundsInScreen(); |
| + gfx::Point cursor_position = aura::Env::GetInstance()->last_mouse_location(); |
| + if (!window_bounds.Contains(cursor_position)) { |
| + // Return early if there is no need to draw the cursor. |
| + Clear(); |
| + return false; |
| + } |
| + |
|
miu
2015/10/20 02:12:44
Please include changes in: https://codereview.chro
Irfan
2015/10/21 23:01:07
Done.
|
| + gfx::NativeCursor cursor = window_->GetHost()->last_cursor(); |
| + gfx::Point cursor_hot_point; |
| + if (last_cursor_ != cursor || |
| + window_size_when_cursor_last_updated_ != window_bounds.size()) { |
| + SkBitmap cursor_bitmap; |
| + if (ui::GetCursorBitmap(cursor, &cursor_bitmap, &cursor_hot_point)) { |
| + const int scaled_width = cursor_bitmap.width() * region_in_frame.width() / |
| + window_bounds.width(); |
| + const int scaled_height = cursor_bitmap.height() * |
| + region_in_frame.height() / |
| + window_bounds.height(); |
| + if (scaled_width <= 0 || scaled_height <= 0) { |
| + Clear(); |
| + return false; |
| + } |
| + scaled_cursor_bitmap_ = skia::ImageOperations::Resize( |
| + cursor_bitmap, skia::ImageOperations::RESIZE_BEST, scaled_width, |
| + scaled_height); |
| + last_cursor_ = cursor; |
| + window_size_when_cursor_last_updated_ = window_bounds.size(); |
| + } else { |
| + // Clear cursor state if ui::GetCursorBitmap failed so that we do not |
| + // render cursor on the captured frame. |
| + Clear(); |
| + } |
| + } |
| + |
| + cursor_position.Offset(-window_bounds.x() - cursor_hot_point.x(), |
| + -window_bounds.y() - cursor_hot_point.y()); |
| + cursor_position_in_frame_ = gfx::Point( |
| + region_in_frame.x() + |
| + cursor_position.x() * region_in_frame.width() / window_bounds.width(), |
| + region_in_frame.y() + |
| + cursor_position.y() * region_in_frame.height() / |
| + window_bounds.height()); |
| + |
| + if (cursor_displayed_) { |
| + // Stop displaying cursor if there has been no mouse movement |
| + base::TimeDelta now = base::TimeDelta::FromInternalValue( |
|
miu
2015/10/20 02:12:44
FromInternalValue() and ToInternalValue() are only
Irfan
2015/10/21 23:01:07
Done.
|
| + base::TimeTicks::Now().ToInternalValue()); |
| + if ((now - last_mouse_movement_timestamp_) > |
| + base::TimeDelta::FromSeconds(MAX_IDLE_TIME)) |
| + cursor_displayed_ = false; |
| + } |
| + return cursor_displayed_; |
| +} |
| + |
| +// Helper function to composite a cursor bitmap on a YUV420 video frame. |
| +void CursorRenderer::RenderOnVideoFrame( |
| + const scoped_refptr<media::VideoFrame>& target) const { |
| + if (scaled_cursor_bitmap_.isNull()) |
| + return; |
| + |
| + DCHECK(target); |
| + |
| + gfx::Rect rect = gfx::IntersectRects( |
| + gfx::Rect(scaled_cursor_bitmap_.width(), scaled_cursor_bitmap_.height()) + |
| + gfx::Vector2d(cursor_position_in_frame_.x(), |
| + cursor_position_in_frame_.y()), |
| + target->visible_rect()); |
| + |
| + scaled_cursor_bitmap_.lockPixels(); |
| + for (int y = rect.y(); y < rect.bottom(); ++y) { |
| + int cursor_y = y - cursor_position_in_frame_.y(); |
| + uint8* yplane = target->data(media::VideoFrame::kYPlane) + |
| + y * target->row_bytes(media::VideoFrame::kYPlane); |
| + uint8* uplane = target->data(media::VideoFrame::kUPlane) + |
| + (y / 2) * target->row_bytes(media::VideoFrame::kUPlane); |
| + uint8* vplane = target->data(media::VideoFrame::kVPlane) + |
| + (y / 2) * target->row_bytes(media::VideoFrame::kVPlane); |
| + for (int x = rect.x(); x < rect.right(); ++x) { |
| + int cursor_x = x - cursor_position_in_frame_.x(); |
| + SkColor color = scaled_cursor_bitmap_.getColor(cursor_x, cursor_y); |
| + int alpha = SkColorGetA(color); |
| + int color_r = SkColorGetR(color); |
| + int color_g = SkColorGetG(color); |
| + int color_b = SkColorGetB(color); |
| + int color_y = clip_byte( |
| + ((color_r * 66 + color_g * 129 + color_b * 25 + 128) >> 8) + 16); |
| + yplane[x] = alpha_blend(alpha, color_y, yplane[x]); |
| + |
| + // Only sample U and V at even coordinates. |
| + if ((x % 2 == 0) && (y % 2 == 0)) { |
| + int color_u = clip_byte( |
| + ((color_r * -38 + color_g * -74 + color_b * 112 + 128) >> 8) + 128); |
| + int color_v = clip_byte( |
| + ((color_r * 112 + color_g * -94 + color_b * -18 + 128) >> 8) + 128); |
| + uplane[x / 2] = alpha_blend(alpha, color_u, uplane[x / 2]); |
| + vplane[x / 2] = alpha_blend(alpha, color_v, vplane[x / 2]); |
| + } |
| + } |
| + } |
| + scaled_cursor_bitmap_.unlockPixels(); |
| +} |
| + |
| +void CursorRenderer::OnMouseEvent(ui::MouseEvent* event) { |
| + switch (event->type()) { |
| + case ui::ET_MOUSE_MOVED: |
| + if (!cursor_displayed_) { |
| + if ((event->x() - last_mouse_position_x_) > MIN_MOVEMENT || |
| + (event->y() - last_mouse_position_y_) > MIN_MOVEMENT) |
| + cursor_displayed_ = true; |
| + } |
| + if (cursor_displayed_) { |
| + last_mouse_movement_timestamp_ = event->time_stamp(); |
| + last_mouse_position_x_ = event->x(); |
| + last_mouse_position_y_ = event->y(); |
| + } |
| + break; |
| + default: |
| + break; |
| + } |
| +} |
| + |
| +} // namespace content |