compositing/
pinch_zoom.rs1use embedder_traits::Scroll;
6use euclid::{Point2D, Rect, Scale, Transform2D, Vector2D};
7use style_traits::CSSPixel;
8use webrender_api::units::{DevicePixel, DevicePoint, DeviceRect, DeviceSize, DeviceVector2D};
9
10#[derive(Clone, Copy, Debug, PartialEq)]
14pub(crate) struct PinchZoom {
15 zoom_factor: f32,
16 transform: Transform2D<f32, DevicePixel, DevicePixel>,
17 unscaled_viewport_size: DeviceSize,
18}
19
20impl PinchZoom {
21 pub(crate) fn new(webview_rect: DeviceRect) -> Self {
22 Self {
23 zoom_factor: 1.0,
24 unscaled_viewport_size: webview_rect.size(),
25 transform: Transform2D::identity(),
26 }
27 }
28
29 pub(crate) fn transform(&self) -> Transform2D<f32, DevicePixel, DevicePixel> {
30 self.transform
31 }
32
33 pub(crate) fn zoom_factor(&self) -> Scale<f32, DevicePixel, DevicePixel> {
34 Scale::new(self.zoom_factor)
35 }
36
37 pub(crate) fn resize_unscaled_viewport(&mut self, webview_rect: DeviceRect) {
38 self.unscaled_viewport_size = webview_rect.size();
39 }
40
41 fn set_transform(&mut self, transform: Transform2D<f32, DevicePixel, DevicePixel>) {
42 let rect = Rect::new(
43 Point2D::origin(),
44 self.unscaled_viewport_size.to_vector().to_size(),
45 )
46 .cast_unit();
47 let mut rect = transform
48 .inverse()
49 .expect("Should always be able to invert provided transform")
50 .outer_transformed_rect(&rect);
51 rect.origin = rect.origin.clamp(
52 Point2D::origin(),
53 (self.unscaled_viewport_size - rect.size)
54 .to_vector()
55 .to_point(),
56 );
57 let scale = self.unscaled_viewport_size.width / rect.width();
58 self.transform = Transform2D::identity()
59 .then_translate(Vector2D::new(-rect.origin.x, -rect.origin.y))
60 .then_scale(scale, scale);
61 }
62
63 pub(crate) fn zoom(&mut self, magnification: f32, new_center: DevicePoint) {
64 const MINIMUM_PINCH_ZOOM: f32 = 1.0;
65 const MAXIMUM_PINCH_ZOOM: f32 = 10.0;
66 let new_factor =
67 (self.zoom_factor * magnification).clamp(MINIMUM_PINCH_ZOOM, MAXIMUM_PINCH_ZOOM);
68 let old_factor = std::mem::replace(&mut self.zoom_factor, new_factor);
69
70 if self.zoom_factor <= 1.0 {
71 self.transform = Transform2D::identity();
72 return;
73 }
74
75 let magnification = self.zoom_factor / old_factor;
76 let transform = self
77 .transform
78 .then_translate(Vector2D::new(-new_center.x, -new_center.y))
79 .then_scale(magnification, magnification)
80 .then_translate(Vector2D::new(new_center.x, new_center.y));
81 self.set_transform(transform);
82 }
83
84 pub(crate) fn pan(&mut self, scroll: &mut Scroll, scale: Scale<f32, CSSPixel, DevicePixel>) {
87 let remaining = self.pan_with_device_scroll(*scroll, scale);
88
89 if let Scroll::Delta(delta) = scroll {
90 *delta = remaining.into();
91 }
92 }
93
94 pub(crate) fn pan_with_device_scroll(
97 &mut self,
98 scroll: Scroll,
99 scale: Scale<f32, CSSPixel, DevicePixel>,
100 ) -> DeviceVector2D {
101 let current_viewport = Rect::new(
102 Point2D::origin(),
103 self.unscaled_viewport_size.to_vector().to_size(),
104 );
105 let layout_viewport_in_device_pixels =
106 self.transform.outer_transformed_rect(¤t_viewport);
107 let max_viewport_offset = -(layout_viewport_in_device_pixels.size -
108 self.unscaled_viewport_size.to_vector().to_size());
109 let max_delta = layout_viewport_in_device_pixels.origin - max_viewport_offset;
110
111 let delta = match scroll {
112 Scroll::Delta(delta) => delta.as_device_vector(scale),
113 Scroll::Start => DeviceVector2D::new(0.0, max_delta.y),
114 Scroll::End => DeviceVector2D::new(0.0, -layout_viewport_in_device_pixels.origin.y),
115 };
116
117 let mut remaining = Vector2D::zero();
118 if delta.x < 0.0 {
119 remaining.x = (delta.x - layout_viewport_in_device_pixels.origin.x).min(0.0);
120 }
121 if delta.y < 0.0 {
122 remaining.y = (delta.y - layout_viewport_in_device_pixels.origin.y).min(0.0);
123 }
124 if delta.x > 0.0 {
125 remaining.x = (delta.x - max_delta.x).max(0.0);
126 }
127 if delta.y > 0.0 {
128 remaining.y = (delta.y - max_delta.y).max(0.0);
129 }
130
131 self.set_transform(
132 self.transform
133 .then_translate(Vector2D::new(-delta.x, -delta.y)),
134 );
135
136 remaining
137 }
138}