mobile/android/base/gfx/ImmutableViewportMetrics.java

branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
equal deleted inserted replaced
-1:000000000000 0:b7e88e74fe27
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 }

mercurial