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