Chromium Code Reviews| Index: remoting/client/desktop_viewport.cc |
| diff --git a/remoting/client/desktop_viewport.cc b/remoting/client/desktop_viewport.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..d8f6980b8202a6b2a274f5bebcd3cd3dcd2595cc |
| --- /dev/null |
| +++ b/remoting/client/desktop_viewport.cc |
| @@ -0,0 +1,212 @@ |
| +// Copyright 2017 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 "remoting/client/desktop_viewport.h" |
| + |
| +#include <algorithm> |
| + |
| +#include "base/logging.h" |
| + |
| +namespace remoting { |
| + |
| +namespace { |
| + |
| +float MAX_ZOOM_LEVEL = 100.f; |
| + |
| +} // namespace |
| + |
| +DesktopViewport::DesktopViewport() : desktop_to_surface_(0.f, {0.f, 0.f}) {} |
|
joedow
2017/04/28 21:29:52
Add a default c'tor to SimpleMatrix so you don't n
Yuwei
2017/04/28 23:53:40
Done.
|
| + |
| +DesktopViewport::~DesktopViewport() {} |
| + |
| +void DesktopViewport::SetDesktopSize(int desktop_width, int desktop_height) { |
| + bool need_resize_to_fit = !desktop_size_ready_ && surface_size_ready_; |
| + desktop_size_.x = desktop_width; |
| + desktop_size_.y = desktop_height; |
| + desktop_size_ready_ = true; |
| + if (need_resize_to_fit) { |
| + ResizeToFit(); |
| + } else if (IsViewportReady()) { |
| + UpdateViewport(); |
| + } |
| +} |
| + |
| +void DesktopViewport::SetSurfaceSize(int surface_width, int surface_height) { |
| + bool need_resize_to_fit = desktop_size_ready_ && !surface_size_ready_; |
| + surface_size_.x = surface_width; |
| + surface_size_.y = surface_height; |
| + surface_size_ready_ = true; |
| + if (need_resize_to_fit) { |
| + ResizeToFit(); |
| + } else if (IsViewportReady()) { |
| + UpdateViewport(); |
| + } |
| +} |
| + |
| +void DesktopViewport::ResizeToFit() { |
| + if (!IsViewportReady()) { |
| + LOG(ERROR) << "Viewport is not ready."; |
|
joedow
2017/04/28 21:29:52
Should this be a DCHECK? It seems like this is un
Yuwei
2017/04/28 23:53:40
Done.
|
| + return; |
| + } |
| + |
| + // <---Desktop Width----> |
|
joedow
2017/04/28 21:29:52
I like the ASCII art, I think it would be good to
Yuwei
2017/04/28 23:53:40
Done.
|
| + // +==========+---------+ |
| + // | | | |
| + // | Viewport | Desktop | |
| + // | | | |
| + // +==========+---------+ |
| + // |
| + // +==========+ ^ |
| + // | | | |
| + // | Viewport | | |
| + // | | | |
| + // +==========+ | Desktop |
| + // | | | Height |
| + // | Desktop | | |
| + // | | | |
| + // +----------+ v |
| + |
| + float scale = std::max(surface_size_.x / desktop_size_.x, |
| + surface_size_.y / desktop_size_.y); |
| + desktop_to_surface_.SetScale(scale); |
| + UpdateViewport(); |
| +} |
| + |
| +void DesktopViewport::MoveDesktop(float dx, float dy) { |
| + desktop_to_surface_.PostTranslate({dx, dy}); |
| + UpdateViewport(); |
| +} |
| + |
| +void DesktopViewport::ScaleDesktop(float px, float py, float scale) { |
| + desktop_to_surface_.PostScale({px, py}, scale); |
| + UpdateViewport(); |
| +} |
| + |
| +void DesktopViewport::SetViewportCenter(float x, float y) { |
| + Point old_center = GetViewportCenter(); |
| + MoveViewportCenterWithoutUpdate(x - old_center.x, y - old_center.y); |
| + UpdateViewport(); |
| +} |
| + |
| +void DesktopViewport::RegisterOnTransformationChangedCallback( |
| + const TransformationCallback& callback, |
| + bool run_immediately) { |
| + on_transformation_changed_ = callback; |
| + if (IsViewportReady() && run_immediately) { |
| + callback.Run(desktop_to_surface_.ToMatrixArray()); |
| + } |
| +} |
| + |
| +bool DesktopViewport::IsViewportReady() const { |
| + return desktop_size_ready_ && surface_size_ready_; |
| +} |
| + |
| +void DesktopViewport::UpdateViewport() { |
| + // Adjust zoom level. |
| + float zoom_level = desktop_to_surface_.GetScale(); |
| + if (zoom_level > MAX_ZOOM_LEVEL) { |
| + // TODO(yuweih): This doesn't account for the effect of the pivot point, |
| + // which will shift the desktop closer to the origin. |
| + desktop_to_surface_.SetScale(MAX_ZOOM_LEVEL); |
| + } |
| + |
| + Vector2D desktop_size_on_surface_ = |
| + desktop_to_surface_.MapVector(desktop_size_); |
| + if (desktop_size_on_surface_.x < surface_size_.x && |
| + desktop_size_on_surface_.y < surface_size_.y) { |
| + // +==============+ |
| + // | VP | +==========+ |
| + // | | | VP | |
| + // | +----------+ | +----------+ |
| + // | | DP | | ==> | DP | |
| + // | +----------+ | +----------+ |
| + // | | | | |
| + // | | +==========+ |
| + // +==============+ |
| + // Displayed desktop is too small in both directions, so apply the minimum |
| + // zoom level needed to fit either the width or height. |
| + float scale = std::min(surface_size_.x / desktop_size_.x, |
| + surface_size_.y / desktop_size_.y); |
| + desktop_to_surface_.SetScale(scale); |
| + } |
| + |
| + // Adjust position. |
| + // Scenarios: |
| + // 1. If the viewport can fully fit inside the desktop (smaller or equal width |
| + // and height), but it doesn't overlap with any non-desktop area, it will |
| + // be moved in minimum distance to be fitted inside the desktop. |
| + // |
| + // +========+ |
| + // | VP | |
| + // | +----|--------+ +========+-----+ |
| + // | | | | | | | |
| + // +========+ | | VP | DP | |
| + // | DP | ==> | | | |
| + // | | +========+ | |
| + // | | | | |
| + // +-------------+ +--------------+ |
| + // |
| + // 2. If the viewport is larger than the desktop, the viewport will always |
| + // share the same center as the desktop. |
| + // |
| + // +==========+------+==+ +======+------+======+ |
| + // | VP | DP | | ==> | VP | DP | | |
| + // +==========+------+==+ +======+------+======+ |
| + // |
| + // This is done on the desktop's reference frame to simplify things a bit. |
| + Point old_center = GetViewportCenter(); |
| + Point new_center = |
| + ConstrainPointToBounds(GetViewportCenterBounds(), old_center); |
| + MoveViewportCenterWithoutUpdate(new_center.x - old_center.x, |
| + new_center.y - old_center.y); |
| + |
| + if (!on_transformation_changed_.is_null()) { |
|
joedow
2017/04/28 21:29:52
nit: You don't need to call is_null():
if (on_tra
Yuwei
2017/04/28 23:53:40
Done.
|
| + on_transformation_changed_.Run(desktop_to_surface_.ToMatrixArray()); |
| + } |
| +} |
| + |
| +Bounds DesktopViewport::GetViewportCenterBounds() const { |
| + Bounds bounds; |
| + |
| + // Viewport size on the desktop space. |
| + Vector2D viewport_size = |
| + desktop_to_surface_.Invert().MapVector(surface_size_); |
| + |
| + // Scenario 1: If VP can fully fit inside the desktop, then VP's center can be |
| + // anywhere inside the desktop as long as VP doesn't overlap with the border. |
| + bounds.left = viewport_size.x * 0.5f; |
|
joedow
2017/04/28 21:29:52
divide by 2? I think that is a more natural way t
Yuwei
2017/04/28 23:53:40
Done.
|
| + bounds.right = desktop_size_.x - viewport_size.x * 0.5f; |
| + bounds.top = viewport_size.y * 0.5f; |
| + bounds.bottom = desktop_size_.y - viewport_size.y * 0.5f; |
| + |
| + // Scenario 2: If VP can't fully fit inside the desktop in dimension D, then |
| + // its bounds in dimension D is tightly restricted to the center of the |
| + // desktop. |
| + if (bounds.left > bounds.right) { |
| + float desktop_width_center = desktop_size_.x * 0.5f; |
| + bounds.left = desktop_width_center; |
| + bounds.right = desktop_width_center; |
| + } |
| + |
| + if (bounds.top > bounds.bottom) { |
| + float desktop_height_center = desktop_size_.y * 0.5f; |
| + bounds.top = desktop_height_center; |
| + bounds.bottom = desktop_height_center; |
| + } |
| + |
| + return bounds; |
| +} |
| + |
| +Point DesktopViewport::GetViewportCenter() const { |
| + return desktop_to_surface_.Invert().MapPoint( |
| + {surface_size_.x * 0.5f, surface_size_.y * 0.5f}); |
| +} |
| + |
| +void DesktopViewport::MoveViewportCenterWithoutUpdate(float dx, float dy) { |
| + // <dx, dy> is defined on desktop's reference frame. Translation must be |
| + // flipped and scaled. |
| + desktop_to_surface_.PostTranslate(desktop_to_surface_.MapVector({-dx, -dy})); |
| +} |
| + |
| +} // namespace remoting |