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 |