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 android.graphics.PointF;
9 import android.graphics.RectF;
10 import android.util.DisplayMetrics;
11 
12 import org.mozilla.gecko.util.FloatUtils;
13 
14 /**
15  * ImmutableViewportMetrics are used to store the viewport metrics
16  * in way that we can access a version of them from multiple threads
17  * without having to take a lock
18  */
19 public class ImmutableViewportMetrics {
20 
21     // We need to flatten the RectF and FloatSize structures
22     // because Java doesn't have the concept of const classes
23     public final float pageRectLeft;
24     public final float pageRectTop;
25     public final float pageRectRight;
26     public final float pageRectBottom;
27     public final float cssPageRectLeft;
28     public final float cssPageRectTop;
29     public final float cssPageRectRight;
30     public final float cssPageRectBottom;
31     public final float viewportRectLeft;
32     public final float viewportRectTop;
33     public final float viewportRectRight;
34     public final float viewportRectBottom;
35     public final float zoomFactor;
36 
ImmutableViewportMetrics(DisplayMetrics metrics)37     public ImmutableViewportMetrics(DisplayMetrics metrics) {
38         viewportRectLeft   = pageRectLeft   = cssPageRectLeft   = 0;
39         viewportRectTop    = pageRectTop    = cssPageRectTop    = 0;
40         viewportRectRight  = pageRectRight  = cssPageRectRight  = metrics.widthPixels;
41         viewportRectBottom = pageRectBottom = cssPageRectBottom = metrics.heightPixels;
42         zoomFactor = 1.0f;
43     }
44 
ImmutableViewportMetrics(float aPageRectLeft, float aPageRectTop, float aPageRectRight, float aPageRectBottom, float aCssPageRectLeft, float aCssPageRectTop, float aCssPageRectRight, float aCssPageRectBottom, float aViewportRectLeft, float aViewportRectTop, float aViewportRectRight, float aViewportRectBottom, float aZoomFactor)45     private ImmutableViewportMetrics(float aPageRectLeft, float aPageRectTop,
46         float aPageRectRight, float aPageRectBottom, float aCssPageRectLeft,
47         float aCssPageRectTop, float aCssPageRectRight, float aCssPageRectBottom,
48         float aViewportRectLeft, float aViewportRectTop, float aViewportRectRight,
49         float aViewportRectBottom, float aZoomFactor)
50     {
51         pageRectLeft = aPageRectLeft;
52         pageRectTop = aPageRectTop;
53         pageRectRight = aPageRectRight;
54         pageRectBottom = aPageRectBottom;
55         cssPageRectLeft = aCssPageRectLeft;
56         cssPageRectTop = aCssPageRectTop;
57         cssPageRectRight = aCssPageRectRight;
58         cssPageRectBottom = aCssPageRectBottom;
59         viewportRectLeft = aViewportRectLeft;
60         viewportRectTop = aViewportRectTop;
61         viewportRectRight = aViewportRectRight;
62         viewportRectBottom = aViewportRectBottom;
63         zoomFactor = aZoomFactor;
64     }
65 
getWidth()66     public float getWidth() {
67         return viewportRectRight - viewportRectLeft;
68     }
69 
getHeight()70     public float getHeight() {
71         return viewportRectBottom - viewportRectTop;
72     }
73 
getOrigin()74     public PointF getOrigin() {
75         return new PointF(viewportRectLeft, viewportRectTop);
76     }
77 
getSize()78     public FloatSize getSize() {
79         return new FloatSize(viewportRectRight - viewportRectLeft, viewportRectBottom - viewportRectTop);
80     }
81 
getViewport()82     public RectF getViewport() {
83         return new RectF(viewportRectLeft,
84                          viewportRectTop,
85                          viewportRectRight,
86                          viewportRectBottom);
87     }
88 
getCssViewport()89     public RectF getCssViewport() {
90         return RectUtils.scale(getViewport(), 1/zoomFactor);
91     }
92 
getPageRect()93     public RectF getPageRect() {
94         return new RectF(pageRectLeft, pageRectTop, pageRectRight, pageRectBottom);
95     }
96 
getPageWidth()97     public float getPageWidth() {
98         return pageRectRight - pageRectLeft;
99     }
100 
getPageHeight()101     public float getPageHeight() {
102         return pageRectBottom - pageRectTop;
103     }
104 
getCssPageRect()105     public RectF getCssPageRect() {
106         return new RectF(cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom);
107     }
108 
getZoomFactor()109     public float getZoomFactor() {
110         return zoomFactor;
111     }
112 
113     /*
114      * Returns the viewport metrics that represent a linear transition between "this" and "to" at
115      * time "t", which is on the scale [0, 1). This function interpolates all values stored in
116      * the viewport metrics.
117      */
interpolate(ImmutableViewportMetrics to, float t)118     public ImmutableViewportMetrics interpolate(ImmutableViewportMetrics to, float t) {
119         return new ImmutableViewportMetrics(
120             FloatUtils.interpolate(pageRectLeft, to.pageRectLeft, t),
121             FloatUtils.interpolate(pageRectTop, to.pageRectTop, t),
122             FloatUtils.interpolate(pageRectRight, to.pageRectRight, t),
123             FloatUtils.interpolate(pageRectBottom, to.pageRectBottom, t),
124             FloatUtils.interpolate(cssPageRectLeft, to.cssPageRectLeft, t),
125             FloatUtils.interpolate(cssPageRectTop, to.cssPageRectTop, t),
126             FloatUtils.interpolate(cssPageRectRight, to.cssPageRectRight, t),
127             FloatUtils.interpolate(cssPageRectBottom, to.cssPageRectBottom, t),
128             FloatUtils.interpolate(viewportRectLeft, to.viewportRectLeft, t),
129             FloatUtils.interpolate(viewportRectTop, to.viewportRectTop, t),
130             FloatUtils.interpolate(viewportRectRight, to.viewportRectRight, t),
131             FloatUtils.interpolate(viewportRectBottom, to.viewportRectBottom, t),
132             FloatUtils.interpolate(zoomFactor, to.zoomFactor, t));
133     }
134 
setViewportSize(float width, float height)135     public ImmutableViewportMetrics setViewportSize(float width, float height) {
136         return new ImmutableViewportMetrics(
137             pageRectLeft, pageRectTop, pageRectRight, pageRectBottom,
138             cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom,
139             viewportRectLeft, viewportRectTop, viewportRectLeft + width, viewportRectTop + height,
140             zoomFactor);
141     }
142 
setViewportOrigin(float newOriginX, float newOriginY)143     public ImmutableViewportMetrics setViewportOrigin(float newOriginX, float newOriginY) {
144         return new ImmutableViewportMetrics(
145             pageRectLeft, pageRectTop, pageRectRight, pageRectBottom,
146             cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom,
147             newOriginX, newOriginY, newOriginX + getWidth(), newOriginY + getHeight(),
148             zoomFactor);
149     }
150 
setZoomFactor(float newZoomFactor)151     public ImmutableViewportMetrics setZoomFactor(float newZoomFactor) {
152         return new ImmutableViewportMetrics(
153             pageRectLeft, pageRectTop, pageRectRight, pageRectBottom,
154             cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom,
155             viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom,
156             newZoomFactor);
157     }
158 
offsetViewportBy(float dx, float dy)159     public ImmutableViewportMetrics offsetViewportBy(float dx, float dy) {
160         return setViewportOrigin(viewportRectLeft + dx, viewportRectTop + dy);
161     }
162 
setPageRect(RectF pageRect, RectF cssPageRect)163     public ImmutableViewportMetrics setPageRect(RectF pageRect, RectF cssPageRect) {
164         return new ImmutableViewportMetrics(
165             pageRect.left, pageRect.top, pageRect.right, pageRect.bottom,
166             cssPageRect.left, cssPageRect.top, cssPageRect.right, cssPageRect.bottom,
167             viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom,
168             zoomFactor);
169     }
170 
171     /* This will set the zoom factor and re-scale page-size and viewport offset
172      * accordingly. The given focus will remain at the same point on the screen
173      * after scaling.
174      */
scaleTo(float newZoomFactor, PointF focus)175     public ImmutableViewportMetrics scaleTo(float newZoomFactor, PointF focus) {
176         // cssPageRect* is invariant, since we're setting the scale factor
177         // here. The page rect is based on the CSS page rect.
178         float newPageRectLeft = cssPageRectLeft * newZoomFactor;
179         float newPageRectTop = cssPageRectTop * newZoomFactor;
180         float newPageRectRight = cssPageRectLeft + ((cssPageRectRight - cssPageRectLeft) * newZoomFactor);
181         float newPageRectBottom = cssPageRectTop + ((cssPageRectBottom - cssPageRectTop) * newZoomFactor);
182 
183         PointF origin = getOrigin();
184         origin.offset(focus.x, focus.y);
185         origin = PointUtils.scale(origin, newZoomFactor / zoomFactor);
186         origin.offset(-focus.x, -focus.y);
187 
188         return new ImmutableViewportMetrics(
189             newPageRectLeft, newPageRectTop, newPageRectRight, newPageRectBottom,
190             cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom,
191             origin.x, origin.y, origin.x + getWidth(), origin.y + getHeight(),
192             newZoomFactor);
193     }
194 
195     /** Clamps the viewport to remain within the page rect. */
clamp()196     public ImmutableViewportMetrics clamp() {
197         RectF newViewport = getViewport();
198 
199         // The viewport bounds ought to never exceed the page bounds.
200         if (newViewport.right > pageRectRight)
201             newViewport.offset(pageRectRight - newViewport.right, 0);
202         if (newViewport.left < pageRectLeft)
203             newViewport.offset(pageRectLeft - newViewport.left, 0);
204 
205         if (newViewport.bottom > pageRectBottom)
206             newViewport.offset(0, pageRectBottom - newViewport.bottom);
207         if (newViewport.top < pageRectTop)
208             newViewport.offset(0, pageRectTop - newViewport.top);
209 
210         return new ImmutableViewportMetrics(
211             pageRectLeft, pageRectTop, pageRectRight, pageRectBottom,
212             cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom,
213             newViewport.left, newViewport.top, newViewport.right, newViewport.bottom,
214             zoomFactor);
215     }
216 
fuzzyEquals(ImmutableViewportMetrics other)217     public boolean fuzzyEquals(ImmutableViewportMetrics other) {
218         return FloatUtils.fuzzyEquals(pageRectLeft, other.pageRectLeft)
219             && FloatUtils.fuzzyEquals(pageRectTop, other.pageRectTop)
220             && FloatUtils.fuzzyEquals(pageRectRight, other.pageRectRight)
221             && FloatUtils.fuzzyEquals(pageRectBottom, other.pageRectBottom)
222             && FloatUtils.fuzzyEquals(cssPageRectLeft, other.cssPageRectLeft)
223             && FloatUtils.fuzzyEquals(cssPageRectTop, other.cssPageRectTop)
224             && FloatUtils.fuzzyEquals(cssPageRectRight, other.cssPageRectRight)
225             && FloatUtils.fuzzyEquals(cssPageRectBottom, other.cssPageRectBottom)
226             && FloatUtils.fuzzyEquals(viewportRectLeft, other.viewportRectLeft)
227             && FloatUtils.fuzzyEquals(viewportRectTop, other.viewportRectTop)
228             && FloatUtils.fuzzyEquals(viewportRectRight, other.viewportRectRight)
229             && FloatUtils.fuzzyEquals(viewportRectBottom, other.viewportRectBottom)
230             && FloatUtils.fuzzyEquals(zoomFactor, other.zoomFactor);
231     }
232 
233     @Override
toString()234     public String toString() {
235         return "ImmutableViewportMetrics v=(" + viewportRectLeft + "," + viewportRectTop + ","
236                 + viewportRectRight + "," + viewportRectBottom + ") p=(" + pageRectLeft + ","
237                 + pageRectTop + "," + pageRectRight + "," + pageRectBottom + ") c=("
238                 + cssPageRectLeft + "," + cssPageRectTop + "," + cssPageRectRight + ","
239                 + cssPageRectBottom + ") z=" + zoomFactor;
240     }
241 }
242