|
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 package org.mozilla.gecko.gfx; |
|
7 |
|
8 import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI; |
|
9 import org.mozilla.gecko.util.FloatUtils; |
|
10 |
|
11 import android.graphics.PointF; |
|
12 import android.graphics.RectF; |
|
13 import android.util.DisplayMetrics; |
|
14 |
|
15 /** |
|
16 * ImmutableViewportMetrics are used to store the viewport metrics |
|
17 * in way that we can access a version of them from multiple threads |
|
18 * without having to take a lock |
|
19 */ |
|
20 public class ImmutableViewportMetrics { |
|
21 |
|
22 // We need to flatten the RectF and FloatSize structures |
|
23 // because Java doesn't have the concept of const classes |
|
24 public final float pageRectLeft; |
|
25 public final float pageRectTop; |
|
26 public final float pageRectRight; |
|
27 public final float pageRectBottom; |
|
28 public final float cssPageRectLeft; |
|
29 public final float cssPageRectTop; |
|
30 public final float cssPageRectRight; |
|
31 public final float cssPageRectBottom; |
|
32 public final float viewportRectLeft; |
|
33 public final float viewportRectTop; |
|
34 public final float viewportRectRight; |
|
35 public final float viewportRectBottom; |
|
36 public final float marginLeft; |
|
37 public final float marginTop; |
|
38 public final float marginRight; |
|
39 public final float marginBottom; |
|
40 public final float zoomFactor; |
|
41 public final boolean isRTL; |
|
42 |
|
43 public ImmutableViewportMetrics(DisplayMetrics metrics) { |
|
44 viewportRectLeft = pageRectLeft = cssPageRectLeft = 0; |
|
45 viewportRectTop = pageRectTop = cssPageRectTop = 0; |
|
46 viewportRectRight = pageRectRight = cssPageRectRight = metrics.widthPixels; |
|
47 viewportRectBottom = pageRectBottom = cssPageRectBottom = metrics.heightPixels; |
|
48 marginLeft = marginTop = marginRight = marginBottom = 0; |
|
49 zoomFactor = 1.0f; |
|
50 isRTL = false; |
|
51 } |
|
52 |
|
53 /** This constructor is used by native code in AndroidJavaWrappers.cpp, be |
|
54 * careful when modifying the signature. |
|
55 */ |
|
56 @WrapElementForJNI(allowMultithread = true) |
|
57 public ImmutableViewportMetrics(float aPageRectLeft, float aPageRectTop, |
|
58 float aPageRectRight, float aPageRectBottom, float aCssPageRectLeft, |
|
59 float aCssPageRectTop, float aCssPageRectRight, float aCssPageRectBottom, |
|
60 float aViewportRectLeft, float aViewportRectTop, float aViewportRectRight, |
|
61 float aViewportRectBottom, float aZoomFactor) |
|
62 { |
|
63 this(aPageRectLeft, aPageRectTop, |
|
64 aPageRectRight, aPageRectBottom, aCssPageRectLeft, |
|
65 aCssPageRectTop, aCssPageRectRight, aCssPageRectBottom, |
|
66 aViewportRectLeft, aViewportRectTop, aViewportRectRight, |
|
67 aViewportRectBottom, 0.0f, 0.0f, 0.0f, 0.0f, aZoomFactor, false); |
|
68 } |
|
69 |
|
70 private ImmutableViewportMetrics(float aPageRectLeft, float aPageRectTop, |
|
71 float aPageRectRight, float aPageRectBottom, float aCssPageRectLeft, |
|
72 float aCssPageRectTop, float aCssPageRectRight, float aCssPageRectBottom, |
|
73 float aViewportRectLeft, float aViewportRectTop, float aViewportRectRight, |
|
74 float aViewportRectBottom, float aMarginLeft, |
|
75 float aMarginTop, float aMarginRight, |
|
76 float aMarginBottom, float aZoomFactor, boolean aIsRTL) |
|
77 { |
|
78 pageRectLeft = aPageRectLeft; |
|
79 pageRectTop = aPageRectTop; |
|
80 pageRectRight = aPageRectRight; |
|
81 pageRectBottom = aPageRectBottom; |
|
82 cssPageRectLeft = aCssPageRectLeft; |
|
83 cssPageRectTop = aCssPageRectTop; |
|
84 cssPageRectRight = aCssPageRectRight; |
|
85 cssPageRectBottom = aCssPageRectBottom; |
|
86 viewportRectLeft = aViewportRectLeft; |
|
87 viewportRectTop = aViewportRectTop; |
|
88 viewportRectRight = aViewportRectRight; |
|
89 viewportRectBottom = aViewportRectBottom; |
|
90 marginLeft = aMarginLeft; |
|
91 marginTop = aMarginTop; |
|
92 marginRight = aMarginRight; |
|
93 marginBottom = aMarginBottom; |
|
94 zoomFactor = aZoomFactor; |
|
95 isRTL = aIsRTL; |
|
96 } |
|
97 |
|
98 public float getWidth() { |
|
99 return viewportRectRight - viewportRectLeft; |
|
100 } |
|
101 |
|
102 public float getHeight() { |
|
103 return viewportRectBottom - viewportRectTop; |
|
104 } |
|
105 |
|
106 public float getWidthWithoutMargins() { |
|
107 return viewportRectRight - viewportRectLeft - marginLeft - marginRight; |
|
108 } |
|
109 |
|
110 public float getHeightWithoutMargins() { |
|
111 return viewportRectBottom - viewportRectTop - marginTop - marginBottom; |
|
112 } |
|
113 |
|
114 public PointF getOrigin() { |
|
115 return new PointF(viewportRectLeft, viewportRectTop); |
|
116 } |
|
117 |
|
118 public PointF getMarginOffset() { |
|
119 if (isRTL) { |
|
120 return new PointF(marginLeft - marginRight, marginTop); |
|
121 } |
|
122 return new PointF(marginLeft, marginTop); |
|
123 } |
|
124 |
|
125 public FloatSize getSize() { |
|
126 return new FloatSize(viewportRectRight - viewportRectLeft, viewportRectBottom - viewportRectTop); |
|
127 } |
|
128 |
|
129 public RectF getViewport() { |
|
130 return new RectF(viewportRectLeft, |
|
131 viewportRectTop, |
|
132 viewportRectRight, |
|
133 viewportRectBottom); |
|
134 } |
|
135 |
|
136 public RectF getCssViewport() { |
|
137 return RectUtils.scale(getViewport(), 1/zoomFactor); |
|
138 } |
|
139 |
|
140 public RectF getPageRect() { |
|
141 return new RectF(pageRectLeft, pageRectTop, pageRectRight, pageRectBottom); |
|
142 } |
|
143 |
|
144 public float getPageWidth() { |
|
145 return pageRectRight - pageRectLeft; |
|
146 } |
|
147 |
|
148 public float getPageWidthWithMargins() { |
|
149 return (pageRectRight - pageRectLeft) + marginLeft + marginRight; |
|
150 } |
|
151 |
|
152 public float getPageHeight() { |
|
153 return pageRectBottom - pageRectTop; |
|
154 } |
|
155 |
|
156 public float getPageHeightWithMargins() { |
|
157 return (pageRectBottom - pageRectTop) + marginTop + marginBottom; |
|
158 } |
|
159 |
|
160 public RectF getCssPageRect() { |
|
161 return new RectF(cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom); |
|
162 } |
|
163 |
|
164 public RectF getOverscroll() { |
|
165 return new RectF(Math.max(0, pageRectLeft - viewportRectLeft), |
|
166 Math.max(0, pageRectTop - viewportRectTop), |
|
167 Math.max(0, viewportRectRight - pageRectRight), |
|
168 Math.max(0, viewportRectBottom - pageRectBottom)); |
|
169 } |
|
170 |
|
171 /* |
|
172 * Returns the viewport metrics that represent a linear transition between "this" and "to" at |
|
173 * time "t", which is on the scale [0, 1). This function interpolates all values stored in |
|
174 * the viewport metrics. |
|
175 */ |
|
176 public ImmutableViewportMetrics interpolate(ImmutableViewportMetrics to, float t) { |
|
177 return new ImmutableViewportMetrics( |
|
178 FloatUtils.interpolate(pageRectLeft, to.pageRectLeft, t), |
|
179 FloatUtils.interpolate(pageRectTop, to.pageRectTop, t), |
|
180 FloatUtils.interpolate(pageRectRight, to.pageRectRight, t), |
|
181 FloatUtils.interpolate(pageRectBottom, to.pageRectBottom, t), |
|
182 FloatUtils.interpolate(cssPageRectLeft, to.cssPageRectLeft, t), |
|
183 FloatUtils.interpolate(cssPageRectTop, to.cssPageRectTop, t), |
|
184 FloatUtils.interpolate(cssPageRectRight, to.cssPageRectRight, t), |
|
185 FloatUtils.interpolate(cssPageRectBottom, to.cssPageRectBottom, t), |
|
186 FloatUtils.interpolate(viewportRectLeft, to.viewportRectLeft, t), |
|
187 FloatUtils.interpolate(viewportRectTop, to.viewportRectTop, t), |
|
188 FloatUtils.interpolate(viewportRectRight, to.viewportRectRight, t), |
|
189 FloatUtils.interpolate(viewportRectBottom, to.viewportRectBottom, t), |
|
190 FloatUtils.interpolate(marginLeft, to.marginLeft, t), |
|
191 FloatUtils.interpolate(marginTop, to.marginTop, t), |
|
192 FloatUtils.interpolate(marginRight, to.marginRight, t), |
|
193 FloatUtils.interpolate(marginBottom, to.marginBottom, t), |
|
194 FloatUtils.interpolate(zoomFactor, to.zoomFactor, t), |
|
195 t >= 0.5 ? to.isRTL : isRTL); |
|
196 } |
|
197 |
|
198 public ImmutableViewportMetrics setViewportSize(float width, float height) { |
|
199 if (FloatUtils.fuzzyEquals(width, getWidth()) && FloatUtils.fuzzyEquals(height, getHeight())) { |
|
200 return this; |
|
201 } |
|
202 |
|
203 return new ImmutableViewportMetrics( |
|
204 pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, |
|
205 cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, |
|
206 viewportRectLeft, viewportRectTop, viewportRectLeft + width, viewportRectTop + height, |
|
207 marginLeft, marginTop, marginRight, marginBottom, |
|
208 zoomFactor, isRTL); |
|
209 } |
|
210 |
|
211 public ImmutableViewportMetrics setViewportOrigin(float newOriginX, float newOriginY) { |
|
212 return new ImmutableViewportMetrics( |
|
213 pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, |
|
214 cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, |
|
215 newOriginX, newOriginY, newOriginX + getWidth(), newOriginY + getHeight(), |
|
216 marginLeft, marginTop, marginRight, marginBottom, |
|
217 zoomFactor, isRTL); |
|
218 } |
|
219 |
|
220 public ImmutableViewportMetrics setZoomFactor(float newZoomFactor) { |
|
221 return new ImmutableViewportMetrics( |
|
222 pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, |
|
223 cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, |
|
224 viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom, |
|
225 marginLeft, marginTop, marginRight, marginBottom, |
|
226 newZoomFactor, isRTL); |
|
227 } |
|
228 |
|
229 public ImmutableViewportMetrics offsetViewportBy(float dx, float dy) { |
|
230 return setViewportOrigin(viewportRectLeft + dx, viewportRectTop + dy); |
|
231 } |
|
232 |
|
233 public ImmutableViewportMetrics offsetViewportByAndClamp(float dx, float dy) { |
|
234 if (isRTL) { |
|
235 return setViewportOrigin( |
|
236 Math.min(pageRectRight - getWidthWithoutMargins(), Math.max(viewportRectLeft + dx, pageRectLeft)), |
|
237 Math.max(pageRectTop, Math.min(viewportRectTop + dy, pageRectBottom - getHeightWithoutMargins()))); |
|
238 } |
|
239 return setViewportOrigin( |
|
240 Math.max(pageRectLeft, Math.min(viewportRectLeft + dx, pageRectRight - getWidthWithoutMargins())), |
|
241 Math.max(pageRectTop, Math.min(viewportRectTop + dy, pageRectBottom - getHeightWithoutMargins()))); |
|
242 } |
|
243 |
|
244 public ImmutableViewportMetrics setPageRect(RectF pageRect, RectF cssPageRect) { |
|
245 return new ImmutableViewportMetrics( |
|
246 pageRect.left, pageRect.top, pageRect.right, pageRect.bottom, |
|
247 cssPageRect.left, cssPageRect.top, cssPageRect.right, cssPageRect.bottom, |
|
248 viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom, |
|
249 marginLeft, marginTop, marginRight, marginBottom, |
|
250 zoomFactor, isRTL); |
|
251 } |
|
252 |
|
253 public ImmutableViewportMetrics setMargins(float left, float top, float right, float bottom) { |
|
254 if (FloatUtils.fuzzyEquals(left, marginLeft) |
|
255 && FloatUtils.fuzzyEquals(top, marginTop) |
|
256 && FloatUtils.fuzzyEquals(right, marginRight) |
|
257 && FloatUtils.fuzzyEquals(bottom, marginBottom)) { |
|
258 return this; |
|
259 } |
|
260 |
|
261 return new ImmutableViewportMetrics( |
|
262 pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, |
|
263 cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, |
|
264 viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom, |
|
265 left, top, right, bottom, zoomFactor, isRTL); |
|
266 } |
|
267 |
|
268 public ImmutableViewportMetrics setMarginsFrom(ImmutableViewportMetrics fromMetrics) { |
|
269 return setMargins(fromMetrics.marginLeft, |
|
270 fromMetrics.marginTop, |
|
271 fromMetrics.marginRight, |
|
272 fromMetrics.marginBottom); |
|
273 } |
|
274 |
|
275 public ImmutableViewportMetrics setIsRTL(boolean aIsRTL) { |
|
276 if (isRTL == aIsRTL) { |
|
277 return this; |
|
278 } |
|
279 |
|
280 return new ImmutableViewportMetrics( |
|
281 pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, |
|
282 cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, |
|
283 viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom, |
|
284 marginLeft, marginTop, marginRight, marginBottom, zoomFactor, aIsRTL); |
|
285 } |
|
286 |
|
287 /* This will set the zoom factor and re-scale page-size and viewport offset |
|
288 * accordingly. The given focus will remain at the same point on the screen |
|
289 * after scaling. |
|
290 */ |
|
291 public ImmutableViewportMetrics scaleTo(float newZoomFactor, PointF focus) { |
|
292 // cssPageRect* is invariant, since we're setting the scale factor |
|
293 // here. The page rect is based on the CSS page rect. |
|
294 float newPageRectLeft = cssPageRectLeft * newZoomFactor; |
|
295 float newPageRectTop = cssPageRectTop * newZoomFactor; |
|
296 float newPageRectRight = cssPageRectLeft + ((cssPageRectRight - cssPageRectLeft) * newZoomFactor); |
|
297 float newPageRectBottom = cssPageRectTop + ((cssPageRectBottom - cssPageRectTop) * newZoomFactor); |
|
298 |
|
299 PointF origin = getOrigin(); |
|
300 origin.offset(focus.x, focus.y); |
|
301 origin = PointUtils.scale(origin, newZoomFactor / zoomFactor); |
|
302 origin.offset(-focus.x, -focus.y); |
|
303 |
|
304 return new ImmutableViewportMetrics( |
|
305 newPageRectLeft, newPageRectTop, newPageRectRight, newPageRectBottom, |
|
306 cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, |
|
307 origin.x, origin.y, origin.x + getWidth(), origin.y + getHeight(), |
|
308 marginLeft, marginTop, marginRight, marginBottom, |
|
309 newZoomFactor, isRTL); |
|
310 } |
|
311 |
|
312 /** Clamps the viewport to remain within the page rect. */ |
|
313 private ImmutableViewportMetrics clamp(float marginLeft, float marginTop, |
|
314 float marginRight, float marginBottom) { |
|
315 RectF newViewport = getViewport(); |
|
316 PointF offset = getMarginOffset(); |
|
317 |
|
318 // The viewport bounds ought to never exceed the page bounds. |
|
319 if (newViewport.right > pageRectRight + marginLeft + marginRight) |
|
320 newViewport.offset((pageRectRight + marginLeft + marginRight) - newViewport.right, 0); |
|
321 if (newViewport.left < pageRectLeft) |
|
322 newViewport.offset(pageRectLeft - newViewport.left, 0); |
|
323 |
|
324 if (newViewport.bottom > pageRectBottom + marginTop + marginBottom) |
|
325 newViewport.offset(0, (pageRectBottom + marginTop + marginBottom) - newViewport.bottom); |
|
326 if (newViewport.top < pageRectTop) |
|
327 newViewport.offset(0, pageRectTop - newViewport.top); |
|
328 |
|
329 return new ImmutableViewportMetrics( |
|
330 pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, |
|
331 cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, |
|
332 newViewport.left, newViewport.top, newViewport.right, newViewport.bottom, |
|
333 marginLeft, marginTop, marginRight, marginBottom, |
|
334 zoomFactor, isRTL); |
|
335 } |
|
336 |
|
337 public ImmutableViewportMetrics clamp() { |
|
338 return clamp(0, 0, 0, 0); |
|
339 } |
|
340 |
|
341 public ImmutableViewportMetrics clampWithMargins() { |
|
342 return clamp(marginLeft, marginTop, |
|
343 marginRight, marginBottom); |
|
344 } |
|
345 |
|
346 public boolean fuzzyEquals(ImmutableViewportMetrics other) { |
|
347 // Don't bother checking the pageRectXXX values because they are a product |
|
348 // of the cssPageRectXXX values and the zoomFactor, except with more rounding |
|
349 // error. Checking those is both inefficient and can lead to false negatives. |
|
350 // |
|
351 // This doesn't return false if the margins differ as none of the users |
|
352 // of this function are interested in the margins in that way. |
|
353 return FloatUtils.fuzzyEquals(cssPageRectLeft, other.cssPageRectLeft) |
|
354 && FloatUtils.fuzzyEquals(cssPageRectTop, other.cssPageRectTop) |
|
355 && FloatUtils.fuzzyEquals(cssPageRectRight, other.cssPageRectRight) |
|
356 && FloatUtils.fuzzyEquals(cssPageRectBottom, other.cssPageRectBottom) |
|
357 && FloatUtils.fuzzyEquals(viewportRectLeft, other.viewportRectLeft) |
|
358 && FloatUtils.fuzzyEquals(viewportRectTop, other.viewportRectTop) |
|
359 && FloatUtils.fuzzyEquals(viewportRectRight, other.viewportRectRight) |
|
360 && FloatUtils.fuzzyEquals(viewportRectBottom, other.viewportRectBottom) |
|
361 && FloatUtils.fuzzyEquals(zoomFactor, other.zoomFactor); |
|
362 } |
|
363 |
|
364 @Override |
|
365 public String toString() { |
|
366 return "ImmutableViewportMetrics v=(" + viewportRectLeft + "," + viewportRectTop + "," |
|
367 + viewportRectRight + "," + viewportRectBottom + ") p=(" + pageRectLeft + "," |
|
368 + pageRectTop + "," + pageRectRight + "," + pageRectBottom + ") c=(" |
|
369 + cssPageRectLeft + "," + cssPageRectTop + "," + cssPageRectRight + "," |
|
370 + cssPageRectBottom + ") m=(" + marginLeft + "," |
|
371 + marginTop + "," + marginRight + "," |
|
372 + marginBottom + ") z=" + zoomFactor + ", rtl=" + isRTL; |
|
373 } |
|
374 } |