OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 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/client/desktop_viewport.h" |
| 6 |
| 7 #include <algorithm> |
| 8 |
| 9 #include "base/logging.h" |
| 10 |
| 11 namespace remoting { |
| 12 |
| 13 namespace { |
| 14 |
| 15 float MAX_ZOOM_LEVEL = 100.f; |
| 16 |
| 17 } // namespace |
| 18 |
| 19 DesktopViewport::DesktopViewport() : desktop_to_surface_transform_() {} |
| 20 |
| 21 DesktopViewport::~DesktopViewport() {} |
| 22 |
| 23 void DesktopViewport::SetDesktopSize(int desktop_width, int desktop_height) { |
| 24 bool need_resize_to_fit = !desktop_size_ready_ && surface_size_ready_; |
| 25 desktop_size_.x = desktop_width; |
| 26 desktop_size_.y = desktop_height; |
| 27 desktop_size_ready_ = true; |
| 28 if (need_resize_to_fit) { |
| 29 ResizeToFit(); |
| 30 } else if (IsViewportReady()) { |
| 31 UpdateViewport(); |
| 32 } |
| 33 } |
| 34 |
| 35 void DesktopViewport::SetSurfaceSize(int surface_width, int surface_height) { |
| 36 bool need_resize_to_fit = desktop_size_ready_ && !surface_size_ready_; |
| 37 surface_size_.x = surface_width; |
| 38 surface_size_.y = surface_height; |
| 39 surface_size_ready_ = true; |
| 40 if (need_resize_to_fit) { |
| 41 ResizeToFit(); |
| 42 } else if (IsViewportReady()) { |
| 43 UpdateViewport(); |
| 44 } |
| 45 } |
| 46 |
| 47 void DesktopViewport::MoveDesktop(float dx, float dy) { |
| 48 desktop_to_surface_transform_.PostTranslate({dx, dy}); |
| 49 UpdateViewport(); |
| 50 } |
| 51 |
| 52 void DesktopViewport::ScaleDesktop(float px, float py, float scale) { |
| 53 desktop_to_surface_transform_.PostScale({px, py}, scale); |
| 54 UpdateViewport(); |
| 55 } |
| 56 |
| 57 void DesktopViewport::SetViewportCenter(float x, float y) { |
| 58 ViewMatrix::Point old_center = GetViewportCenter(); |
| 59 MoveViewportCenterWithoutUpdate(x - old_center.x, y - old_center.y); |
| 60 UpdateViewport(); |
| 61 } |
| 62 |
| 63 void DesktopViewport::RegisterOnTransformationChangedCallback( |
| 64 const TransformationCallback& callback, |
| 65 bool run_immediately) { |
| 66 on_transformation_changed_ = callback; |
| 67 if (IsViewportReady() && run_immediately) { |
| 68 callback.Run(desktop_to_surface_transform_); |
| 69 } |
| 70 } |
| 71 |
| 72 void DesktopViewport::ResizeToFit() { |
| 73 DCHECK(IsViewportReady()); |
| 74 |
| 75 // <---Desktop Width----> |
| 76 // +==========+---------+ |
| 77 // | | | |
| 78 // | Viewport | Desktop | |
| 79 // | | | |
| 80 // +==========+---------+ |
| 81 // |
| 82 // +==========+ ^ |
| 83 // | | | |
| 84 // | Viewport | | |
| 85 // | | | |
| 86 // +==========+ | Desktop |
| 87 // | | | Height |
| 88 // | Desktop | | |
| 89 // | | | |
| 90 // +----------+ v |
| 91 // resize the desktop such that it fits the viewport in one dimension. |
| 92 |
| 93 float scale = std::max(surface_size_.x / desktop_size_.x, |
| 94 surface_size_.y / desktop_size_.y); |
| 95 desktop_to_surface_transform_.SetScale(scale); |
| 96 UpdateViewport(); |
| 97 } |
| 98 |
| 99 bool DesktopViewport::IsViewportReady() const { |
| 100 return desktop_size_ready_ && surface_size_ready_; |
| 101 } |
| 102 |
| 103 void DesktopViewport::UpdateViewport() { |
| 104 if (!IsViewportReady()) { |
| 105 // User may attempt to zoom and pan before the desktop image is received. |
| 106 // This should be fine since the viewport will be reset once the image |
| 107 // dimension is set. |
| 108 VLOG(1) << "Viewport is not ready yet."; |
| 109 return; |
| 110 } |
| 111 |
| 112 // Adjust zoom level. |
| 113 float zoom_level = desktop_to_surface_transform_.GetScale(); |
| 114 if (zoom_level > MAX_ZOOM_LEVEL) { |
| 115 // TODO(yuweih): This doesn't account for the effect of the pivot point, |
| 116 // which will shift the desktop closer to the origin. |
| 117 desktop_to_surface_transform_.SetScale(MAX_ZOOM_LEVEL); |
| 118 } |
| 119 |
| 120 ViewMatrix::Vector2D desktop_size_on_surface_ = |
| 121 desktop_to_surface_transform_.MapVector(desktop_size_); |
| 122 if (desktop_size_on_surface_.x < surface_size_.x && |
| 123 desktop_size_on_surface_.y < surface_size_.y) { |
| 124 // +==============+ |
| 125 // | VP | +==========+ |
| 126 // | | | VP | |
| 127 // | +----------+ | +----------+ |
| 128 // | | DP | | ==> | DP | |
| 129 // | +----------+ | +----------+ |
| 130 // | | | | |
| 131 // | | +==========+ |
| 132 // +==============+ |
| 133 // Displayed desktop is too small in both directions, so apply the minimum |
| 134 // zoom level needed to fit either the width or height. |
| 135 float scale = std::min(surface_size_.x / desktop_size_.x, |
| 136 surface_size_.y / desktop_size_.y); |
| 137 desktop_to_surface_transform_.SetScale(scale); |
| 138 } |
| 139 |
| 140 // Adjust position. |
| 141 // Scenarios: |
| 142 // 1. If the viewport can fully fit inside the desktop (smaller or equal width |
| 143 // and height) but it overlaps with the border, it will be moved in minimum |
| 144 // distance to be fitted inside the desktop. |
| 145 // |
| 146 // +========+ |
| 147 // | VP | |
| 148 // | +----|--------+ +========+-----+ |
| 149 // | | | | | | | |
| 150 // +========+ | | VP | DP | |
| 151 // | DP | ==> | | | |
| 152 // | | +========+ | |
| 153 // | | | | |
| 154 // +-------------+ +--------------+ |
| 155 // |
| 156 // 2. If the viewport is larger than the desktop, the viewport will always |
| 157 // share the same center as the desktop. |
| 158 // |
| 159 // +==========+------+==+ +======+------+======+ |
| 160 // | VP | DP | | ==> | VP | DP | | |
| 161 // +==========+------+==+ +======+------+======+ |
| 162 // |
| 163 // This is done on the desktop's reference frame to simplify things a bit. |
| 164 ViewMatrix::Point old_center = GetViewportCenter(); |
| 165 ViewMatrix::Point new_center = |
| 166 ConstrainPointToBounds(GetViewportCenterBounds(), old_center); |
| 167 MoveViewportCenterWithoutUpdate(new_center.x - old_center.x, |
| 168 new_center.y - old_center.y); |
| 169 |
| 170 if (on_transformation_changed_) { |
| 171 on_transformation_changed_.Run(desktop_to_surface_transform_); |
| 172 } |
| 173 } |
| 174 |
| 175 DesktopViewport::Bounds DesktopViewport::GetViewportCenterBounds() const { |
| 176 Bounds bounds; |
| 177 |
| 178 // Viewport size on the desktop space. |
| 179 ViewMatrix::Vector2D viewport_size = |
| 180 desktop_to_surface_transform_.Invert().MapVector(surface_size_); |
| 181 |
| 182 // Scenario 1: If VP can fully fit inside the desktop, then VP's center can be |
| 183 // anywhere inside the desktop as long as VP doesn't overlap with the border. |
| 184 bounds.left = viewport_size.x / 2.f; |
| 185 bounds.right = desktop_size_.x - viewport_size.x / 2.f; |
| 186 bounds.top = viewport_size.y / 2.f; |
| 187 bounds.bottom = desktop_size_.y - viewport_size.y / 2.f; |
| 188 |
| 189 // Scenario 2: If VP can't fully fit inside the desktop in dimension D, then |
| 190 // its bounds in dimension D is tightly restricted to the center of the |
| 191 // desktop. |
| 192 if (bounds.left > bounds.right) { |
| 193 float desktop_width_center = desktop_size_.x / 2.f; |
| 194 bounds.left = desktop_width_center; |
| 195 bounds.right = desktop_width_center; |
| 196 } |
| 197 |
| 198 if (bounds.top > bounds.bottom) { |
| 199 float desktop_height_center = desktop_size_.y / 2.f; |
| 200 bounds.top = desktop_height_center; |
| 201 bounds.bottom = desktop_height_center; |
| 202 } |
| 203 |
| 204 return bounds; |
| 205 } |
| 206 |
| 207 ViewMatrix::Point DesktopViewport::GetViewportCenter() const { |
| 208 return desktop_to_surface_transform_.Invert().MapPoint( |
| 209 {surface_size_.x / 2.f, surface_size_.y / 2.f}); |
| 210 } |
| 211 |
| 212 void DesktopViewport::MoveViewportCenterWithoutUpdate(float dx, float dy) { |
| 213 // <dx, dy> is defined on desktop's reference frame. Translation must be |
| 214 // flipped and scaled. |
| 215 desktop_to_surface_transform_.PostTranslate( |
| 216 desktop_to_surface_transform_.MapVector({-dx, -dy})); |
| 217 } |
| 218 |
| 219 // static |
| 220 ViewMatrix::Point DesktopViewport::ConstrainPointToBounds( |
| 221 const Bounds& bounds, |
| 222 const ViewMatrix::Point& point) { |
| 223 ViewMatrix::Point new_point = point; |
| 224 if (new_point.x < bounds.left) { |
| 225 new_point.x = bounds.left; |
| 226 } else if (new_point.x > bounds.right) { |
| 227 new_point.x = bounds.right; |
| 228 } |
| 229 |
| 230 if (new_point.y < bounds.top) { |
| 231 new_point.y = bounds.top; |
| 232 } else if (new_point.y > bounds.bottom) { |
| 233 new_point.y = bounds.bottom; |
| 234 } |
| 235 return new_point; |
| 236 } |
| 237 |
| 238 } // namespace remoting |
OLD | NEW |