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 "chrome/browser/vr/elements/content_element.h"
6 
7 #include "chrome/browser/vr/content_input_delegate.h"
8 #include "chrome/browser/vr/model/text_input_info.h"
9 #include "chrome/browser/vr/text_input_delegate.h"
10 #include "chrome/browser/vr/ui_element_renderer.h"
11 #include "chrome/browser/vr/ui_scene_constants.h"
12 #include "chrome/browser/vr/vr_geometry_util.h"
13 #include "ui/gfx/geometry/rect_f.h"
14 
15 namespace vr {
16 
17 namespace {
18 
19 // If the screen space bounds or the aspect ratio of the content quad change
20 // beyond these thresholds we propagate the new content bounds so that the
21 // content's resolution can be adjusted.
22 static constexpr float kContentBoundsPropagationThreshold = 0.2f;
23 // Changes of the aspect ratio lead to a
24 // distorted content much more quickly. Thus, have a smaller threshold here.
25 static constexpr float kContentAspectRatioPropagationThreshold = 0.01f;
26 
GetNormalFromTransform(const gfx::Transform & transform)27 gfx::Vector3dF GetNormalFromTransform(const gfx::Transform& transform) {
28   gfx::Vector3dF x_axis(1, 0, 0);
29   gfx::Vector3dF y_axis(0, 1, 0);
30   transform.TransformVector(&x_axis);
31   transform.TransformVector(&y_axis);
32   gfx::Vector3dF normal = CrossProduct(x_axis, y_axis);
33   normal.GetNormalized(&normal);
34   return normal;
35 }
36 
37 }  // namespace
38 
ContentElement(ContentInputDelegate * delegate,ContentElement::ScreenBoundsChangedCallback bounds_changed_callback)39 ContentElement::ContentElement(
40     ContentInputDelegate* delegate,
41     ContentElement::ScreenBoundsChangedCallback bounds_changed_callback)
42     : PlatformUiElement(),
43       bounds_changed_callback_(bounds_changed_callback),
44       content_delegate_(delegate) {
45   DCHECK(delegate);
46   SetDelegate(delegate);
47 }
48 
49 ContentElement::~ContentElement() = default;
50 
Render(UiElementRenderer * renderer,const CameraModel & model) const51 void ContentElement::Render(UiElementRenderer* renderer,
52                             const CameraModel& model) const {
53   if (uses_quad_layer_) {
54     renderer->DrawTexturedQuad(0, 0, texture_location(),
55                                model.view_proj_matrix * world_space_transform(),
56                                GetClipRect(), computed_opacity(), size(),
57                                corner_radius(), false);
58     return;
59   }
60 
61   unsigned int overlay_texture_id =
62       overlay_texture_non_empty_ ? overlay_texture_id_ : 0;
63   if (texture_id() || overlay_texture_id) {
64     renderer->DrawTexturedQuad(
65         texture_id(), overlay_texture_id, texture_location(),
66         model.view_proj_matrix * world_space_transform(), GetClipRect(),
67         computed_opacity(), size(), corner_radius(), true);
68   }
69 }
70 
OnFocusChanged(bool focused)71 void ContentElement::OnFocusChanged(bool focused) {
72   if (content_delegate_)
73     content_delegate_->OnFocusChanged(focused);
74 
75   focused_ = focused;
76   if (event_handlers_.focus_change)
77     event_handlers_.focus_change.Run(focused);
78 }
79 
OnInputEdited(const EditedText & info)80 void ContentElement::OnInputEdited(const EditedText& info) {
81   if (content_delegate_)
82     content_delegate_->OnWebInputEdited(info, false);
83 }
84 
OnInputCommitted(const EditedText & info)85 void ContentElement::OnInputCommitted(const EditedText& info) {
86   if (content_delegate_)
87     content_delegate_->OnWebInputEdited(info, true);
88 }
89 
SetOverlayTextureId(unsigned int texture_id)90 void ContentElement::SetOverlayTextureId(unsigned int texture_id) {
91   overlay_texture_id_ = texture_id;
92 }
93 
SetOverlayTextureLocation(GlTextureLocation location)94 void ContentElement::SetOverlayTextureLocation(GlTextureLocation location) {
95   overlay_texture_location_ = location;
96 }
97 
SetOverlayTextureEmpty(bool empty)98 void ContentElement::SetOverlayTextureEmpty(bool empty) {
99   overlay_texture_non_empty_ = !empty;
100 }
101 
GetOverlayTextureEmpty()102 bool ContentElement::GetOverlayTextureEmpty() {
103   return !overlay_texture_non_empty_;
104 }
105 
SetProjectionMatrix(const gfx::Transform & matrix)106 void ContentElement::SetProjectionMatrix(const gfx::Transform& matrix) {
107   projection_matrix_ = matrix;
108 }
109 
SetTextInputDelegate(TextInputDelegate * text_input_delegate)110 void ContentElement::SetTextInputDelegate(
111     TextInputDelegate* text_input_delegate) {
112   text_input_delegate_ = text_input_delegate;
113 }
114 
RequestFocus()115 void ContentElement::RequestFocus() {
116   if (!text_input_delegate_)
117     return;
118 
119   text_input_delegate_->RequestFocus(id());
120 }
121 
RequestUnfocus()122 void ContentElement::RequestUnfocus() {
123   if (!text_input_delegate_)
124     return;
125 
126   text_input_delegate_->RequestUnfocus(id());
127 }
128 
UpdateInput(const EditedText & info)129 void ContentElement::UpdateInput(const EditedText& info) {
130   if (text_input_delegate_ && focused_)
131     text_input_delegate_->UpdateInput(info.current);
132 }
133 
NotifyClientSizeAnimated(const gfx::SizeF & size,int target_property_id,cc::KeyframeModel * animation)134 void ContentElement::NotifyClientSizeAnimated(const gfx::SizeF& size,
135                                               int target_property_id,
136                                               cc::KeyframeModel* animation) {
137   if (target_property_id == BOUNDS && on_size_changed_callback_) {
138     on_size_changed_callback_.Run(size);
139   }
140   UiElement::NotifyClientSizeAnimated(size, target_property_id, animation);
141 }
142 
OnBeginFrame(const gfx::Transform & head_pose)143 bool ContentElement::OnBeginFrame(const gfx::Transform& head_pose) {
144   // TODO(mthiesse): This projection matrix is always going to be a frame
145   // behind when computing the content size. We'll need to address this somehow
146   // when we allow content resizing, or we could end up triggering an extra
147   // incorrect resize.
148   if (projection_matrix_.IsIdentity())
149     return false;
150 
151   // Determine if the projected size of the content quad changed more than a
152   // given threshold. If so, propagate this info so that the content's
153   // resolution and size can be adjusted. For the calculation, we cannot take
154   // the content quad's actual size (main_content_->size()) if this property
155   // is animated. If we took the actual size during an animation we would
156   // surpass the threshold with differing projected sizes and aspect ratios
157   // (depending on the animation's timing). The differing values may cause
158   // visual artefacts if, for instance, the fullscreen aspect ratio is not 16:9.
159   // As a workaround, take the final size of the content quad after the
160   // animation as the basis for the calculation.
161   gfx::SizeF target_size = GetTargetSize();
162   // We take the target transform in case the content quad's parent's translate
163   // is animated. This approach only works with the current scene hierarchy and
164   // set of animated properties.
165   gfx::Transform target_transform = ComputeTargetWorldSpaceTransform();
166 
167   gfx::Point3F target_center;
168   target_transform.TransformPoint(&target_center);
169   gfx::Vector3dF target_normal = GetNormalFromTransform(target_transform);
170   float distance = gfx::DotProduct(target_center - kOrigin, -target_normal);
171   gfx::SizeF screen_size =
172       CalculateScreenSize(projection_matrix_, distance, target_size);
173 
174   float aspect_ratio = target_size.width() / target_size.height();
175   gfx::SizeF screen_bounds;
176   if (screen_size.width() < screen_size.height() * aspect_ratio) {
177     screen_bounds.set_width(screen_size.height() * aspect_ratio);
178     screen_bounds.set_height(screen_size.height());
179   } else {
180     screen_bounds.set_width(screen_size.width());
181     screen_bounds.set_height(screen_size.width() / aspect_ratio);
182   }
183 
184   if (std::abs(screen_bounds.width() - last_content_screen_bounds_.width()) >
185           kContentBoundsPropagationThreshold ||
186       std::abs(screen_bounds.height() - last_content_screen_bounds_.height()) >
187           kContentBoundsPropagationThreshold ||
188       std::abs(aspect_ratio - last_content_aspect_ratio_) >
189           kContentAspectRatioPropagationThreshold) {
190     bounds_changed_callback_.Run(screen_bounds);
191     last_content_screen_bounds_.set_width(screen_bounds.width());
192     last_content_screen_bounds_.set_height(screen_bounds.height());
193     last_content_aspect_ratio_ = aspect_ratio;
194     return true;
195   }
196   return false;
197 }
198 
SetUsesQuadLayer(bool uses_quad_layer)199 void ContentElement::SetUsesQuadLayer(bool uses_quad_layer) {
200   uses_quad_layer_ = uses_quad_layer;
201 }
202 
203 }  // namespace vr
204