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