Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
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/. */
6 package org.mozilla.gecko.gfx;
8 import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
9 import org.mozilla.gecko.util.FloatUtils;
11 import android.graphics.PointF;
12 import android.graphics.RectF;
13 import android.util.DisplayMetrics;
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 {
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;
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 }
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 }
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 }
98 public float getWidth() {
99 return viewportRectRight - viewportRectLeft;
100 }
102 public float getHeight() {
103 return viewportRectBottom - viewportRectTop;
104 }
106 public float getWidthWithoutMargins() {
107 return viewportRectRight - viewportRectLeft - marginLeft - marginRight;
108 }
110 public float getHeightWithoutMargins() {
111 return viewportRectBottom - viewportRectTop - marginTop - marginBottom;
112 }
114 public PointF getOrigin() {
115 return new PointF(viewportRectLeft, viewportRectTop);
116 }
118 public PointF getMarginOffset() {
119 if (isRTL) {
120 return new PointF(marginLeft - marginRight, marginTop);
121 }
122 return new PointF(marginLeft, marginTop);
123 }
125 public FloatSize getSize() {
126 return new FloatSize(viewportRectRight - viewportRectLeft, viewportRectBottom - viewportRectTop);
127 }
129 public RectF getViewport() {
130 return new RectF(viewportRectLeft,
131 viewportRectTop,
132 viewportRectRight,
133 viewportRectBottom);
134 }
136 public RectF getCssViewport() {
137 return RectUtils.scale(getViewport(), 1/zoomFactor);
138 }
140 public RectF getPageRect() {
141 return new RectF(pageRectLeft, pageRectTop, pageRectRight, pageRectBottom);
142 }
144 public float getPageWidth() {
145 return pageRectRight - pageRectLeft;
146 }
148 public float getPageWidthWithMargins() {
149 return (pageRectRight - pageRectLeft) + marginLeft + marginRight;
150 }
152 public float getPageHeight() {
153 return pageRectBottom - pageRectTop;
154 }
156 public float getPageHeightWithMargins() {
157 return (pageRectBottom - pageRectTop) + marginTop + marginBottom;
158 }
160 public RectF getCssPageRect() {
161 return new RectF(cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom);
162 }
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 }
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 }
198 public ImmutableViewportMetrics setViewportSize(float width, float height) {
199 if (FloatUtils.fuzzyEquals(width, getWidth()) && FloatUtils.fuzzyEquals(height, getHeight())) {
200 return this;
201 }
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 }
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 }
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 }
229 public ImmutableViewportMetrics offsetViewportBy(float dx, float dy) {
230 return setViewportOrigin(viewportRectLeft + dx, viewportRectTop + dy);
231 }
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 }
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 }
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 }
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 }
268 public ImmutableViewportMetrics setMarginsFrom(ImmutableViewportMetrics fromMetrics) {
269 return setMargins(fromMetrics.marginLeft,
270 fromMetrics.marginTop,
271 fromMetrics.marginRight,
272 fromMetrics.marginBottom);
273 }
275 public ImmutableViewportMetrics setIsRTL(boolean aIsRTL) {
276 if (isRTL == aIsRTL) {
277 return this;
278 }
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 }
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);
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);
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 }
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();
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);
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);
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 }
337 public ImmutableViewportMetrics clamp() {
338 return clamp(0, 0, 0, 0);
339 }
341 public ImmutableViewportMetrics clampWithMargins() {
342 return clamp(marginLeft, marginTop,
343 marginRight, marginBottom);
344 }
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 }
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 }