[Libreoffice-commits] core.git: 7 commits - android/experimental
Tomaž Vajngerl
tomaz.vajngerl at collabora.com
Thu Sep 18 14:03:34 PDT 2014
android/experimental/LOAndroid3/AndroidManifest.xml | 3
android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java | 34
android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java | 39
android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java | 53
android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java | 5
android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java | 5
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java | 97 +
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ImmutableViewportMetrics.java | 70 +
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/Layer.java | 116 -
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerController.java | 384 ++---
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java | 378 +++--
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerView.java | 60
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/MultiTileLayer.java | 66 -
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/NinePatchTileLayer.java | 99 +
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ScreenshotLayer.java | 253 +++
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ScrollbarLayer.java | 544 ++++----
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/SingleTileLayer.java | 119 +
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/TileLayer.java | 119 -
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ViewportMetrics.java | 173 --
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/VirtualLayer.java | 80 -
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomController.java | 653 ++++------
21 files changed, 1830 insertions(+), 1520 deletions(-)
New commits:
commit f02cef962b4f930eb5637dea1bb9d8c382839286
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.com>
Date: Thu Sep 18 22:22:08 2014 +0200
android: convert to ImmutableViewportMetrics
Change-Id: Idd5e604541577f6b812a971e585cee9b089d2b4b
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
index c5918e69..c12170f 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
@@ -7,6 +7,7 @@ import android.util.Log;
import org.mozilla.gecko.gfx.FloatSize;
import org.mozilla.gecko.gfx.GeckoLayerClient;
+import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
import org.mozilla.gecko.gfx.SubTile;
import org.mozilla.gecko.gfx.ViewportMetrics;
@@ -29,9 +30,9 @@ public class LOKitThread extends Thread {
mInputFile = inputFile;
}
- RectF normlizeRect(ViewportMetrics metrics) {
+ RectF normlizeRect(ImmutableViewportMetrics metrics) {
RectF rect = metrics.getViewport();
- float zoomFactor = metrics.getZoomFactor();
+ float zoomFactor = metrics.zoomFactor;
return new RectF(rect.left / zoomFactor, rect.top / zoomFactor, rect.right / zoomFactor, rect.bottom / zoomFactor);
}
@@ -68,7 +69,7 @@ public class LOKitThread extends Thread {
GeckoLayerClient layerClient = mApplication.getLayerClient();
layerClient.beginDrawing(mViewportMetrics);
- ViewportMetrics metrics = mApplication.getLayerController().getViewportMetrics();
+ ImmutableViewportMetrics metrics = mApplication.getLayerController().getViewportMetrics();
RectF viewport = normlizeRect(metrics);
Rect rect = inflate(roundToTileSize(viewport, TILE_SIZE), TILE_SIZE);
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
index 1ebb9a1..e7a9059 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
@@ -149,7 +149,7 @@ public class GeckoLayerClient {
mGeckoViewport = mNewGeckoViewport;
mGeckoViewport.setSize(viewportSize);
- PointF displayportOrigin = mGeckoViewport.getDisplayportOrigin();
+ PointF displayportOrigin = mGeckoViewport.getOrigin();
RectF position = mGeckoViewport.getViewport();
mTileLayer.setPosition(RectUtils.round(position));
mTileLayer.setResolution(mGeckoViewport.getZoomFactor());
@@ -288,7 +288,7 @@ public class GeckoLayerClient {
viewportMetrics.setViewport(viewportMetrics.getClampedViewport());
- mDisplayPort = calculateDisplayPort(new ImmutableViewportMetrics(mLayerController.getViewportMetrics()));
+ mDisplayPort = calculateDisplayPort(mLayerController.getViewportMetrics());
LOKitShell.sendEvent(LOEvent.viewport(viewportMetrics));
if (mViewportSizeChanged) {
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerController.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerController.java
index 9c497f7..e9d0cb6 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerController.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerController.java
@@ -70,7 +70,20 @@ public class LayerController {
private Layer mRootLayer; /* The root layer. */
private LayerView mView; /* The main rendering view. */
private Context mContext; /* The current context. */
- private ViewportMetrics mViewportMetrics; /* The current viewport metrics. */
+
+ /* This is volatile so that we can read and write to it from different threads.
+ * We avoid synchronization to make getting the viewport metrics from
+ * the compositor as cheap as possible. The viewport is immutable so
+ * we don't need to worry about anyone mutating it while we're reading from it.
+ * Specifically:
+ * 1) reading mViewportMetrics from any thread is fine without synchronization
+ * 2) writing to mViewportMetrics requires synchronizing on the layer controller object
+ * 3) whenver reading multiple fields from mViewportMetrics without synchronization (i.e. in
+ * case 1 above) you should always frist grab a local copy of the reference, and then use
+ * that because mViewportMetrics might get reassigned in between reading the different
+ * fields. */
+ private volatile ImmutableViewportMetrics mViewportMetrics; /* The current viewport metrics. */
+
private boolean mWaitForTouchListeners;
private PanZoomController mPanZoomController;
@@ -84,7 +97,7 @@ public class LayerController {
/* The new color for the checkerboard. */
private int mCheckerboardColor;
- private boolean mCheckerboardShouldShowChecks;
+ private boolean mCheckerboardShouldShowChecks = true;
private boolean mForceRedraw;
@@ -113,10 +126,9 @@ public class LayerController {
mContext = context;
mForceRedraw = true;
- mViewportMetrics = new ViewportMetrics();
+ mViewportMetrics = new ImmutableViewportMetrics(new ViewportMetrics());
mPanZoomController = new PanZoomController(this);
mView = new LayerView(context, this);
- mCheckerboardShouldShowChecks = true;
}
public void onDestroy() {
@@ -136,7 +148,7 @@ public class LayerController {
public Layer getRoot() { return mRootLayer; }
public LayerView getView() { return mView; }
public Context getContext() { return mContext; }
- public ViewportMetrics getViewportMetrics() { return mViewportMetrics; }
+ public ImmutableViewportMetrics getViewportMetrics() { return mViewportMetrics; }
public RectF getViewport() {
return mViewportMetrics.getViewport();
@@ -155,7 +167,7 @@ public class LayerController {
}
public float getZoomFactor() {
- return mViewportMetrics.getZoomFactor();
+ return mViewportMetrics.zoomFactor;
}
public Bitmap getBackgroundPattern() { return getDrawable("background"); }
@@ -185,13 +197,49 @@ public class LayerController {
* result in an infinite loop.
*/
public void setViewportSize(FloatSize size) {
+ // Resize the viewport, and modify its zoom factor so that the page retains proportionally
+ // zoomed relative to the screen.
ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics);
+ float oldHeight = viewportMetrics.getSize().height;
+ float oldWidth = viewportMetrics.getSize().width;
+ float oldZoomFactor = viewportMetrics.getZoomFactor();
viewportMetrics.setSize(size);
- mViewportMetrics = new ViewportMetrics(viewportMetrics);
+
+ // if the viewport got larger (presumably because the vkb went away), and the page
+ // is smaller than the new viewport size, increase the page size so that the panzoomcontroller
+ // doesn't zoom in to make it fit (bug 718270). this page size change is in anticipation of
+ // gecko increasing the page size to match the new viewport size, which will happen the next
+ // time we get a draw update.
+ if (size.width >= oldWidth && size.height >= oldHeight) {
+ FloatSize pageSize = viewportMetrics.getPageSize();
+ if (pageSize.width < size.width || pageSize.height < size.height) {
+ viewportMetrics.setPageSize(new FloatSize(Math.max(pageSize.width, size.width),
+ Math.max(pageSize.height, size.height)));
+ }
+ }
+
+ // For rotations, we want the focus point to be at the top left.
+ boolean rotation = (size.width > oldWidth && size.height < oldHeight) ||
+ (size.width < oldWidth && size.height > oldHeight);
+ PointF newFocus;
+ if (rotation) {
+ newFocus = new PointF(0, 0);
+ } else {
+ newFocus = new PointF(size.width / 2.0f, size.height / 2.0f);
+ }
+ float newZoomFactor = size.width * oldZoomFactor / oldWidth;
+ viewportMetrics.scaleTo(newZoomFactor, newFocus);
+ mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics);
+
+ setForceRedraw();
if (mLayerClient != null) {
mLayerClient.viewportSizeChanged();
+ notifyLayerClientOfGeometryChange();
}
+
+ mPanZoomController.abortAnimation();
+ mView.requestRender();
}
/** Scrolls the viewport by the given offset. You must hold the monitor while calling this. */
@@ -200,7 +248,7 @@ public class LayerController {
PointF origin = viewportMetrics.getOrigin();
origin.offset(point.x, point.y);
viewportMetrics.setOrigin(origin);
- mViewportMetrics = new ViewportMetrics(viewportMetrics);
+ mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics);
notifyLayerClientOfGeometryChange();
mView.requestRender();
@@ -213,7 +261,7 @@ public class LayerController {
ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics);
viewportMetrics.setPageSize(size);
- mViewportMetrics = new ViewportMetrics(viewportMetrics);
+ mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics);
// Page size is owned by the layer client, so no need to notify it of
// this change.
@@ -233,7 +281,7 @@ public class LayerController {
* while calling this.
*/
public void setViewportMetrics(ViewportMetrics viewport) {
- mViewportMetrics = new ViewportMetrics(viewport);
+ mViewportMetrics = new ImmutableViewportMetrics(viewport);
Log.d(LOGTAG, "setViewportMetrics: " + mViewportMetrics);
mView.requestRender();
}
@@ -245,7 +293,7 @@ public class LayerController {
public void scaleWithFocus(float zoomFactor, PointF focus) {
ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics);
viewportMetrics.scaleTo(zoomFactor, focus);
- mViewportMetrics = new ViewportMetrics(viewportMetrics);
+ mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics);
Log.d(LOGTAG, "scaleWithFocus: " + mViewportMetrics + "; zf=" + zoomFactor);
// We assume the zoom level will only be modified by the
@@ -317,31 +365,26 @@ public class LayerController {
* Converts a point from layer view coordinates to layer coordinates. In other words, given a
* point measured in pixels from the top left corner of the layer view, returns the point in
* pixels measured from the top left corner of the root layer, in the coordinate system of the
- * layer itself (CSS pixels). This method is used as part of the process of translating touch
- * events to Gecko's coordinate system.
+ * layer itself. This method is used by the viewport controller as part of the process of
+ * translating touch events to Gecko's coordinate system.
*/
public PointF convertViewPointToLayerPoint(PointF viewPoint) {
if (mRootLayer == null)
return null;
- ViewportMetrics viewportMetrics = mViewportMetrics;
+ ImmutableViewportMetrics viewportMetrics = mViewportMetrics;
+ // Undo the transforms.
PointF origin = viewportMetrics.getOrigin();
PointF newPoint = new PointF(origin.x, origin.y);
- float zoom = viewportMetrics.getZoomFactor();
+ float zoom = viewportMetrics.zoomFactor;
+ viewPoint.x /= zoom;
+ viewPoint.y /= zoom;
+ newPoint.offset(viewPoint.x, viewPoint.y);
Rect rootPosition = mRootLayer.getPosition();
- float rootScale = mRootLayer.getResolution();
-
- // viewPoint + origin gives the coordinate in device pixels from the top-left corner of the page.
- // Divided by zoom, this gives us the coordinate in CSS pixels from the top-left corner of the page.
- // rootPosition / rootScale is where Gecko thinks it is (scrollTo position) in CSS pixels from
- // the top-left corner of the page. Subtracting the two gives us the offset of the viewPoint from
- // the current Gecko coordinate in CSS pixels.
- PointF layerPoint = new PointF(
- ((viewPoint.x + origin.x) / zoom) - (rootPosition.left / rootScale),
- ((viewPoint.y + origin.y) / zoom) - (rootPosition.top / rootScale));
-
- return layerPoint;
+ newPoint.offset(-rootPosition.left, -rootPosition.top);
+
+ return newPoint;
}
/*
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java
index 44973bf..6ff9a8a 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java
@@ -286,7 +286,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
* Called whenever a new frame is about to be drawn.
*/
public void onDrawFrame(GL10 gl) {
- Frame frame = createFrame(new ImmutableViewportMetrics(mView.getController().getViewportMetrics()));
+ Frame frame = createFrame(mView.getController().getViewportMetrics());
synchronized (mView.getController()) {
frame.beginDrawing();
frame.drawBackground();
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ViewportMetrics.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ViewportMetrics.java
index d98b474..8c3004e 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ViewportMetrics.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ViewportMetrics.java
@@ -53,14 +53,10 @@ import org.mozilla.gecko.util.FloatUtils;
*/
public class ViewportMetrics {
private static final String LOGTAG = "GeckoViewportMetrics";
- private static final float MAX_BIAS = 0.8f;
+
private FloatSize mPageSize;
private RectF mViewportRect;
- private PointF mViewportOffset;
private float mZoomFactor;
- // A scale from -1,-1 to 1,1 that represents what edge of the displayport
- // we want the viewport to be biased towards.
- private PointF mViewportBias;
public ViewportMetrics() {
DisplayMetrics metrics = new DisplayMetrics();
@@ -68,119 +64,52 @@ public class ViewportMetrics {
mPageSize = new FloatSize(metrics.widthPixels, metrics.heightPixels);
mViewportRect = new RectF(0, 0, metrics.widthPixels, metrics.heightPixels);
- mViewportOffset = new PointF(0, 0);
mZoomFactor = 1.0f;
- mViewportBias = new PointF(0.0f, 0.0f);
}
public ViewportMetrics(ViewportMetrics viewport) {
mPageSize = new FloatSize(viewport.getPageSize());
mViewportRect = new RectF(viewport.getViewport());
- PointF offset = viewport.getViewportOffset();
- mViewportOffset = new PointF(offset.x, offset.y);
mZoomFactor = viewport.getZoomFactor();
- mViewportBias = viewport.mViewportBias;
}
+ public ViewportMetrics(ImmutableViewportMetrics viewport) {
+ mPageSize = new FloatSize(viewport.pageSizeWidth, viewport.pageSizeHeight);
+ mViewportRect = new RectF(viewport.viewportRectLeft,
+ viewport.viewportRectTop,
+ viewport.viewportRectRight,
+ viewport.viewportRectBottom);
+ mZoomFactor = viewport.zoomFactor;
+ }
+
+
public ViewportMetrics(JSONObject json) throws JSONException {
- float x = (float) json.getDouble("x");
- float y = (float) json.getDouble("y");
- float width = (float) json.getDouble("width");
- float height = (float) json.getDouble("height");
- float pageWidth = (float) json.getDouble("pageWidth");
- float pageHeight = (float) json.getDouble("pageHeight");
- float offsetX = (float) json.getDouble("offsetX");
- float offsetY = (float) json.getDouble("offsetY");
- float zoom = (float) json.getDouble("zoom");
+ float x = (float)json.getDouble("x");
+ float y = (float)json.getDouble("y");
+ float width = (float)json.getDouble("width");
+ float height = (float)json.getDouble("height");
+ float pageWidth = (float)json.getDouble("pageWidth");
+ float pageHeight = (float)json.getDouble("pageHeight");
+ float zoom = (float)json.getDouble("zoom");
mPageSize = new FloatSize(pageWidth, pageHeight);
mViewportRect = new RectF(x, y, x + width, y + height);
- mViewportOffset = new PointF(offsetX, offsetY);
mZoomFactor = zoom;
- mViewportBias = new PointF(0.0f, 0.0f);
- }
-
- public PointF getOptimumViewportOffset(IntSize displayportSize) {
- /* XXX Until bug #524925 is fixed, changing the viewport origin will
- * cause unnecessary relayouts. This may cause rendering time to
- * increase and should be considered.
- */
- RectF viewport = getClampedViewport();
-
- FloatSize bufferSpace = new FloatSize(displayportSize.width - viewport.width(),
- displayportSize.height - viewport.height());
- PointF optimumOffset =
- new PointF(bufferSpace.width * ((mViewportBias.x + 1.0f) / 2.0f),
- bufferSpace.height * ((mViewportBias.y + 1.0f) / 2.0f));
-
- // Make sure this offset won't cause wasted pixels in the displayport
- // (i.e. make sure the resultant displayport intersects with the page
- // as much as possible)
- if (viewport.left - optimumOffset.x < 0)
- optimumOffset.x = viewport.left;
- else if ((bufferSpace.width - optimumOffset.x) + viewport.right > mPageSize.width)
- optimumOffset.x = bufferSpace.width - (mPageSize.width - viewport.right);
-
- if (viewport.top - optimumOffset.y < 0)
- optimumOffset.y = viewport.top;
- else if ((bufferSpace.height - optimumOffset.y) + viewport.bottom > mPageSize.height)
- optimumOffset.y = bufferSpace.height - (mPageSize.height - viewport.bottom);
-
- return new PointF(Math.round(optimumOffset.x), Math.round(optimumOffset.y));
}
public PointF getOrigin() {
return new PointF(mViewportRect.left, mViewportRect.top);
}
- public void setOrigin(PointF origin) {
- // When the origin is set, we compare it with the last value set and
- // change the viewport bias accordingly, so that any viewport based
- // on these metrics will have a larger buffer in the direction of
- // movement.
-
- // XXX Note the comment about bug #524925 in getOptimumViewportOffset.
- // Ideally, the viewport bias would be a sliding scale, but we
- // don't want to change it too often at the moment.
- if (FloatUtils.fuzzyEquals(origin.x, mViewportRect.left))
- mViewportBias.x = 0;
- else
- mViewportBias.x = ((mViewportRect.left - origin.x) > 0) ? MAX_BIAS : -MAX_BIAS;
- if (FloatUtils.fuzzyEquals(origin.y, mViewportRect.top))
- mViewportBias.y = 0;
- else
- mViewportBias.y = ((mViewportRect.top - origin.y) > 0) ? MAX_BIAS : -MAX_BIAS;
-
- mViewportRect.set(origin.x, origin.y,
- origin.x + mViewportRect.width(),
- origin.y + mViewportRect.height());
- }
-
- public PointF getDisplayportOrigin() {
- return new PointF(mViewportRect.left - mViewportOffset.x,
- mViewportRect.top - mViewportOffset.y);
- }
-
public FloatSize getSize() {
return new FloatSize(mViewportRect.width(), mViewportRect.height());
}
- public void setSize(FloatSize size) {
- mViewportRect.right = mViewportRect.left + size.width;
- mViewportRect.bottom = mViewportRect.top + size.height;
- }
-
public RectF getViewport() {
return mViewportRect;
}
- public void setViewport(RectF viewport) {
- mViewportRect = viewport;
- }
-
- /**
- * Returns the viewport rectangle, clamped within the page-size.
- */
+ /** Returns the viewport rectangle, clamped within the page-size. */
public RectF getClampedViewport() {
RectF clampedViewport = new RectF(mViewportRect);
@@ -200,24 +129,31 @@ public class ViewportMetrics {
return clampedViewport;
}
- public PointF getViewportOffset() {
- return mViewportOffset;
- }
-
- public void setViewportOffset(PointF offset) {
- mViewportOffset = offset;
- }
-
public FloatSize getPageSize() {
return mPageSize;
}
+ public float getZoomFactor() {
+ return mZoomFactor;
+ }
+
public void setPageSize(FloatSize pageSize) {
mPageSize = pageSize;
}
- public float getZoomFactor() {
- return mZoomFactor;
+ public void setViewport(RectF viewport) {
+ mViewportRect = viewport;
+ }
+
+ public void setOrigin(PointF origin) {
+ mViewportRect.set(origin.x, origin.y,
+ origin.x + mViewportRect.width(),
+ origin.y + mViewportRect.height());
+ }
+
+ public void setSize(FloatSize size) {
+ mViewportRect.right = mViewportRect.left + size.width;
+ mViewportRect.bottom = mViewportRect.top + size.height;
}
public void setZoomFactor(float zoomFactor) {
@@ -240,15 +176,6 @@ public class ViewportMetrics {
setOrigin(origin);
mZoomFactor = newZoomFactor;
-
- // Similar to setOrigin, set the viewport bias based on the focal point
- // of the zoom so that a viewport based on these metrics will have a
- // larger buffer based on the direction of movement when scaling.
- //
- // This is biased towards scaling outwards, as zooming in doesn't
- // really require a viewport bias.
- mViewportBias.set(((focus.x / mViewportRect.width()) * (2.0f * MAX_BIAS)) - MAX_BIAS,
- ((focus.y / mViewportRect.height()) * (2.0f * MAX_BIAS)) - MAX_BIAS);
}
/*
@@ -261,15 +188,13 @@ public class ViewportMetrics {
result.mPageSize = mPageSize.interpolate(to.mPageSize, t);
result.mZoomFactor = FloatUtils.interpolate(mZoomFactor, to.mZoomFactor, t);
result.mViewportRect = RectUtils.interpolate(mViewportRect, to.mViewportRect, t);
- result.mViewportOffset = PointUtils.interpolate(mViewportOffset, to.mViewportOffset, t);
return result;
}
public boolean fuzzyEquals(ViewportMetrics other) {
return mPageSize.fuzzyEquals(other.mPageSize)
- && RectUtils.fuzzyEquals(mViewportRect, other.mViewportRect)
- && FloatUtils.fuzzyEquals(mViewportOffset, other.mViewportOffset)
- && FloatUtils.fuzzyEquals(mZoomFactor, other.mZoomFactor);
+ && RectUtils.fuzzyEquals(mViewportRect, other.mViewportRect)
+ && FloatUtils.fuzzyEquals(mZoomFactor, other.mZoomFactor);
}
public String toJSON() {
@@ -280,15 +205,13 @@ public class ViewportMetrics {
StringBuffer sb = new StringBuffer(256);
sb.append("{ \"x\" : ").append(mViewportRect.left)
- .append(", \"y\" : ").append(mViewportRect.top)
- .append(", \"width\" : ").append(width)
- .append(", \"height\" : ").append(height)
- .append(", \"pageWidth\" : ").append(mPageSize.width)
- .append(", \"pageHeight\" : ").append(mPageSize.height)
- .append(", \"offsetX\" : ").append(mViewportOffset.x)
- .append(", \"offsetY\" : ").append(mViewportOffset.y)
- .append(", \"zoom\" : ").append(mZoomFactor)
- .append(" }");
+ .append(", \"y\" : ").append(mViewportRect.top)
+ .append(", \"width\" : ").append(width)
+ .append(", \"height\" : ").append(height)
+ .append(", \"pageWidth\" : ").append(mPageSize.width)
+ .append(", \"pageHeight\" : ").append(mPageSize.height)
+ .append(", \"zoom\" : ").append(mZoomFactor)
+ .append(" }");
return sb.toString();
}
@@ -296,10 +219,8 @@ public class ViewportMetrics {
public String toString() {
StringBuffer buff = new StringBuffer(128);
buff.append("v=").append(mViewportRect.toString())
- .append(" p=").append(mPageSize.toString())
- .append(" z=").append(mZoomFactor)
- .append(" o=").append(mViewportOffset.x)
- .append(',').append(mViewportOffset.y);
+ .append(" p=").append(mPageSize.toString())
+ .append(" z=").append(mZoomFactor);
return buff.toString();
}
}
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomController.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomController.java
index 7d15bdb..1eb0331 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomController.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomController.java
@@ -61,45 +61,69 @@ import java.util.TimerTask;
* Many ideas are from Joe Hewitt's Scrollability:
* https://github.com/joehewitt/scrollability/
*/
-public class PanZoomController extends GestureDetector.SimpleOnGestureListener implements SimpleScaleGestureDetector.SimpleScaleGestureListener {
- // The distance the user has to pan before we recognize it as such (e.g. to avoid 1-pixel pans
- // between the touch-down and touch-up of a click). In units of density-independent pixels.
- public static final float PAN_THRESHOLD = 1 / 16f * LOKitShell.getDpi();
+public class PanZoomController
+ extends GestureDetector.SimpleOnGestureListener
+ implements SimpleScaleGestureDetector.SimpleScaleGestureListener
+{
private static final String LOGTAG = "GeckoPanZoomController";
+
+
// Animation stops if the velocity is below this value when overscrolled or panning.
private static final float STOPPED_THRESHOLD = 4.0f;
+
// Animation stops is the velocity is below this threshold when flinging.
private static final float FLING_STOPPED_THRESHOLD = 0.1f;
+
+ // The distance the user has to pan before we recognize it as such (e.g. to avoid 1-pixel pans
+ // between the touch-down and touch-up of a click). In units of density-independent pixels.
+ public static final float PAN_THRESHOLD = 1/16f * LOKitShell.getDpi();
+
// Angle from axis within which we stay axis-locked
private static final double AXIS_LOCK_ANGLE = Math.PI / 6.0; // 30 degrees
+
// The maximum amount we allow you to zoom into a page
private static final float MAX_ZOOM = 4.0f;
+
/* 16 precomputed frames of the _ease-out_ animation from the CSS Transitions specification. */
private static final float[] EASE_OUT_ANIMATION_FRAMES = {
- 0.00000f, /* 0 */
- 0.10211f, /* 1 */
- 0.19864f, /* 2 */
- 0.29043f, /* 3 */
- 0.37816f, /* 4 */
- 0.46155f, /* 5 */
- 0.54054f, /* 6 */
- 0.61496f, /* 7 */
- 0.68467f, /* 8 */
- 0.74910f, /* 9 */
- 0.80794f, /* 10 */
- 0.86069f, /* 11 */
- 0.90651f, /* 12 */
- 0.94471f, /* 13 */
- 0.97401f, /* 14 */
- 0.99309f, /* 15 */
+ 0.00000f, /* 0 */
+ 0.10211f, /* 1 */
+ 0.19864f, /* 2 */
+ 0.29043f, /* 3 */
+ 0.37816f, /* 4 */
+ 0.46155f, /* 5 */
+ 0.54054f, /* 6 */
+ 0.61496f, /* 7 */
+ 0.68467f, /* 8 */
+ 0.74910f, /* 9 */
+ 0.80794f, /* 10 */
+ 0.86069f, /* 11 */
+ 0.90651f, /* 12 */
+ 0.94471f, /* 13 */
+ 0.97401f, /* 14 */
+ 0.99309f, /* 15 */
};
- private static String MESSAGE_ZOOM_RECT = "Browser:ZoomToRect";
- private static String MESSAGE_ZOOM_PAGE = "Browser:ZoomToPageWidth";
+
+ private enum PanZoomState {
+ NOTHING, /* no touch-start events received */
+ FLING, /* all touches removed, but we're still scrolling page */
+ TOUCHING, /* one touch-start event received */
+ PANNING_LOCKED, /* touch-start followed by move (i.e. panning with axis lock) */
+ PANNING, /* panning without axis lock */
+ PANNING_HOLD, /* in panning, but not moving.
+ * similar to TOUCHING but after starting a pan */
+ PANNING_HOLD_LOCKED, /* like PANNING_HOLD, but axis lock still in effect */
+ PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */
+ ANIMATED_ZOOM /* animated zoom to a new rect */
+ }
+
private final LayerController mController;
private final SubdocumentScrollHelper mSubscroller;
private final Axis mX;
private final Axis mY;
+
private Thread mMainThread;
+
/* The timer that handles flings or bounces. */
private Timer mAnimationTimer;
/* The runnable being scheduled by the animation timer. */
@@ -118,59 +142,48 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
mY = new AxisY(mSubscroller);
mMainThread = LibreOfficeMainActivity.mAppContext.getMainLooper().getThread();
+ checkMainThread();
mState = PanZoomState.NOTHING;
}
+ // for debugging bug 713011; it can be taken out once that is resolved.
+ private void checkMainThread() {
+ if (mMainThread != Thread.currentThread()) {
+ // log with full stack trace
+ Log.e(LOGTAG, "Uh-oh, we're running on the wrong thread!", new Exception());
+ }
+ }
+
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_DOWN:
- return onTouchStart(event);
- case MotionEvent.ACTION_MOVE:
- return onTouchMove(event);
- case MotionEvent.ACTION_UP:
- return onTouchEnd(event);
- case MotionEvent.ACTION_CANCEL:
- return onTouchCancel(event);
- default:
- return false;
+ case MotionEvent.ACTION_DOWN: return onTouchStart(event);
+ case MotionEvent.ACTION_MOVE: return onTouchMove(event);
+ case MotionEvent.ACTION_UP: return onTouchEnd(event);
+ case MotionEvent.ACTION_CANCEL: return onTouchCancel(event);
+ default: return false;
}
}
- /**
- * This function must be called from the UI thread.
- */
+ /** This function must be called from the UI thread. */
public void abortAnimation() {
+ checkMainThread();
// this happens when gecko changes the viewport on us or if the device is rotated.
// if that's the case, abort any animation in progress and re-zoom so that the page
// snaps to edges. for other cases (where the user's finger(s) are down) don't do
// anything special.
- switch (mState) {
- case FLING:
- mX.stopFling();
- mY.stopFling();
- mState = PanZoomState.NOTHING;
- // fall through
- case ANIMATED_ZOOM:
- // the zoom that's in progress likely makes no sense any more (such as if
- // the screen orientation changed) so abort it
- // fall through
- case NOTHING:
- // Don't do animations here; they're distracting and can cause flashes on page
- // transitions.
- mController.setViewportMetrics(getValidViewportMetrics());
- mController.notifyLayerClientOfGeometryChange();
- break;
+ if (mState == PanZoomState.FLING) {
+ mX.stopFling();
+ mY.stopFling();
+ mState = PanZoomState.NOTHING;
}
}
- /**
- * This must be called on the UI thread.
- */
+ /** This must be called on the UI thread. */
public void pageSizeUpdated() {
if (mState == PanZoomState.NOTHING) {
ViewportMetrics validated = getValidViewportMetrics();
- if (!mController.getViewportMetrics().fuzzyEquals(validated)) {
+ if (! (new ViewportMetrics(mController.getViewportMetrics())).fuzzyEquals(validated)) {
// page size changed such that we are now in overscroll. snap to the
// the nearest valid viewport
mController.setViewportMetrics(validated);
@@ -179,116 +192,110 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
}
}
+ /*
+ * Panning/scrolling
+ */
+
private boolean onTouchStart(MotionEvent event) {
- Log.d(LOGTAG, "onTouchStart in state " + mState);
// user is taking control of movement, so stop
// any auto-movement we have going
stopAnimationTimer();
mSubscroller.cancel();
switch (mState) {
- case ANIMATED_ZOOM:
- return false;
- case FLING:
- case NOTHING:
- startTouch(event.getX(0), event.getY(0), event.getEventTime());
- return false;
- case TOUCHING:
- case PANNING:
- case PANNING_LOCKED:
- case PANNING_HOLD:
- case PANNING_HOLD_LOCKED:
- case PINCHING:
- Log.e(LOGTAG, "Received impossible touch down while in " + mState);
- return false;
+ case ANIMATED_ZOOM:
+ return false;
+ case FLING:
+ case NOTHING:
+ startTouch(event.getX(0), event.getY(0), event.getEventTime());
+ return false;
+ case TOUCHING:
+ case PANNING:
+ case PANNING_LOCKED:
+ case PANNING_HOLD:
+ case PANNING_HOLD_LOCKED:
+ case PINCHING:
+ Log.e(LOGTAG, "Received impossible touch down while in " + mState);
+ return false;
}
Log.e(LOGTAG, "Unhandled case " + mState + " in onTouchStart");
return false;
}
- /*
- * Panning/scrolling
- */
private boolean onTouchMove(MotionEvent event) {
- Log.d(LOGTAG, "onTouchMove in state " + mState);
switch (mState) {
- case NOTHING:
- case FLING:
- // should never happen
- Log.e(LOGTAG, "Received impossible touch move while in " + mState);
- return false;
+ case NOTHING:
+ case FLING:
+ // should never happen
+ Log.e(LOGTAG, "Received impossible touch move while in " + mState);
+ return false;
- case TOUCHING:
- if (panDistance(event) < PAN_THRESHOLD) {
- return false;
- }
- cancelTouch();
- startPanning(event.getX(0), event.getY(0), event.getEventTime());
- track(event);
- return true;
-
- case PANNING_HOLD_LOCKED:
- //GeckoApp.mAutoCompletePopup.hide();
- mState = PanZoomState.PANNING_LOCKED;
- // fall through
- case PANNING_LOCKED:
- track(event);
- return true;
-
- case PANNING_HOLD:
- //GeckoApp.mAutoCompletePopup.hide();
- mState = PanZoomState.PANNING;
- // fall through
- case PANNING:
- track(event);
- return true;
-
- case ANIMATED_ZOOM:
- case PINCHING:
- // scale gesture listener will handle this
+ case TOUCHING:
+ if (panDistance(event) < PAN_THRESHOLD) {
return false;
+ }
+ cancelTouch();
+ startPanning(event.getX(0), event.getY(0), event.getEventTime());
+ track(event);
+ return true;
+
+ case PANNING_HOLD_LOCKED:
+ mState = PanZoomState.PANNING_LOCKED;
+ // fall through
+ case PANNING_LOCKED:
+ track(event);
+ return true;
+
+ case PANNING_HOLD:
+ mState = PanZoomState.PANNING;
+ // fall through
+ case PANNING:
+ track(event);
+ return true;
+
+ case ANIMATED_ZOOM:
+ case PINCHING:
+ // scale gesture listener will handle this
+ return false;
}
Log.e(LOGTAG, "Unhandled case " + mState + " in onTouchMove");
return false;
}
private boolean onTouchEnd(MotionEvent event) {
- Log.d(LOGTAG, "onTouchEnd in " + mState);
switch (mState) {
- case NOTHING:
- case FLING:
- // should never happen
- Log.e(LOGTAG, "Received impossible touch end while in " + mState);
- return false;
- case TOUCHING:
- mState = PanZoomState.NOTHING;
- // the switch into TOUCHING might have happened while the page was
- // snapping back after overscroll. we need to finish the snap if that
- // was the case
- bounce();
- return false;
- case PANNING:
- case PANNING_LOCKED:
- case PANNING_HOLD:
- case PANNING_HOLD_LOCKED:
- mState = PanZoomState.FLING;
- fling();
- return true;
- case PINCHING:
- mState = PanZoomState.NOTHING;
- return true;
- case ANIMATED_ZOOM:
- return false;
+ case NOTHING:
+ case FLING:
+ // should never happen
+ Log.e(LOGTAG, "Received impossible touch end while in " + mState);
+ return false;
+ case TOUCHING:
+ mState = PanZoomState.NOTHING;
+ // the switch into TOUCHING might have happened while the page was
+ // snapping back after overscroll. we need to finish the snap if that
+ // was the case
+ bounce();
+ return false;
+ case PANNING:
+ case PANNING_LOCKED:
+ case PANNING_HOLD:
+ case PANNING_HOLD_LOCKED:
+ mState = PanZoomState.FLING;
+ fling();
+ return true;
+ case PINCHING:
+ mState = PanZoomState.NOTHING;
+ return true;
+ case ANIMATED_ZOOM:
+ return false;
}
Log.e(LOGTAG, "Unhandled case " + mState + " in onTouchEnd");
return false;
}
private boolean onTouchCancel(MotionEvent event) {
- Log.d(LOGTAG, "onTouchCancel in " + mState);
-
mState = PanZoomState.NOTHING;
// ensure we snap back if we're overscrolled
bounce();
@@ -332,7 +339,7 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
}
private void track(float x, float y, long time) {
- float timeDelta = (float) (time - mLastEventTime);
+ float timeDelta = (float)(time - mLastEventTime);
if (FloatUtils.fuzzyEquals(timeDelta, 0)) {
// probably a duplicate event, ignore it. using a zero timeDelta will mess
// up our velocity
@@ -350,8 +357,8 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
for (int i = 0; i < event.getHistorySize(); i++) {
track(event.getHistoricalX(0, i),
- event.getHistoricalY(0, i),
- event.getHistoricalEventTime(i));
+ event.getHistoricalY(0, i),
+ event.getHistoricalEventTime(i));
}
track(event.getX(0), event.getY(0), event.getEventTime());
@@ -395,7 +402,6 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
}
mState = PanZoomState.FLING;
- Log.d(LOGTAG, "end bounce at " + metrics);
startAnimationTimer(new BounceRunnable(bounceStartMetrics, metrics));
}
@@ -412,16 +418,12 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
stopAnimationTimer();
}
- //GeckoApp.mAppContext.hidePlugins(false /* don't hide layers */);
-
mAnimationTimer = new Timer("Animation Timer");
mAnimationRunnable = runnable;
mAnimationTimer.scheduleAtFixedRate(new TimerTask() {
@Override
- public void run() {
- mController.post(runnable);
- }
- }, 0, 1000L / 60L);
+ public void run() { mController.post(runnable); }
+ }, 0, 1000L/60L);
}
/* Stops the fling or bounce animation. */
@@ -437,9 +439,9 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
}
private float getVelocity() {
- float xVelocity = mX.getRealVelocity();
- float yVelocity = mY.getRealVelocity();
- return FloatMath.sqrt(xVelocity * xVelocity + yVelocity * yVelocity);
+ float xvel = mX.getRealVelocity();
+ float yvel = mY.getRealVelocity();
+ return FloatMath.sqrt(xvel * xvel + yvel * yvel);
}
private boolean stopped() {
@@ -454,14 +456,150 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
mX.displace();
mY.displace();
PointF displacement = getDisplacement();
- if (!mSubscroller.scrollBy(displacement)) {
+ if (! mSubscroller.scrollBy(displacement)) {
synchronized (mController) {
mController.scrollBy(displacement);
}
}
}
+ private abstract class AnimationRunnable implements Runnable {
+ private boolean mAnimationTerminated;
+
+ /* This should always run on the UI thread */
+ public final void run() {
+ /*
+ * Since the animation timer queues this runnable on the UI thread, it
+ * is possible that even when the animation timer is cancelled, there
+ * are multiple instances of this queued, so we need to have another
+ * mechanism to abort. This is done by using the mAnimationTerminated flag.
+ */
+ if (mAnimationTerminated) {
+ return;
+ }
+ animateFrame();
+ }
+
+ protected abstract void animateFrame();
+
+ /* This should always run on the UI thread */
+ protected final void terminate() {
+ mAnimationTerminated = true;
+ }
+ }
+
+ /* The callback that performs the bounce animation. */
+ private class BounceRunnable extends AnimationRunnable {
+ /* The current frame of the bounce-back animation */
+ private int mBounceFrame;
+ /*
+ * The viewport metrics that represent the start and end of the bounce-back animation,
+ * respectively.
+ */
+ private ViewportMetrics mBounceStartMetrics;
+ private ViewportMetrics mBounceEndMetrics;
+
+ BounceRunnable(ViewportMetrics startMetrics, ViewportMetrics endMetrics) {
+ mBounceStartMetrics = startMetrics;
+ mBounceEndMetrics = endMetrics;
+ }
+
+ protected void animateFrame() {
+ /*
+ * The pan/zoom controller might have signaled to us that it wants to abort the
+ * animation by setting the state to PanZoomState.NOTHING. Handle this case and bail
+ * out.
+ */
+ if (mState != PanZoomState.FLING) {
+ finishAnimation();
+ return;
+ }
+
+ /* Perform the next frame of the bounce-back animation. */
+ if (mBounceFrame < EASE_OUT_ANIMATION_FRAMES.length) {
+ advanceBounce();
+ return;
+ }
+
+ /* Finally, if there's nothing else to do, complete the animation and go to sleep. */
+ finishBounce();
+ finishAnimation();
+ mState = PanZoomState.NOTHING;
+ }
+
+ /* Performs one frame of a bounce animation. */
+ private void advanceBounce() {
+ synchronized (mController) {
+ float t = EASE_OUT_ANIMATION_FRAMES[mBounceFrame];
+ ViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t);
+ mController.setViewportMetrics(newMetrics);
+ mController.notifyLayerClientOfGeometryChange();
+ mBounceFrame++;
+ }
+ }
+
+ /* Concludes a bounce animation and snaps the viewport into place. */
+ private void finishBounce() {
+ synchronized (mController) {
+ mController.setViewportMetrics(mBounceEndMetrics);
+ mController.notifyLayerClientOfGeometryChange();
+ mBounceFrame = -1;
+ }
+ }
+ }
+
+ // The callback that performs the fling animation.
+ private class FlingRunnable extends AnimationRunnable {
+ protected void animateFrame() {
+ /*
+ * The pan/zoom controller might have signaled to us that it wants to abort the
+ * animation by setting the state to PanZoomState.NOTHING. Handle this case and bail
+ * out.
+ */
+ if (mState != PanZoomState.FLING) {
+ finishAnimation();
+ return;
+ }
+
+ /* Advance flings, if necessary. */
+ boolean flingingX = mX.advanceFling();
+ boolean flingingY = mY.advanceFling();
+
+ boolean overscrolled = (mX.overscrolled() || mY.overscrolled());
+
+ /* If we're still flinging in any direction, update the origin. */
+ if (flingingX || flingingY) {
+ updatePosition();
+
+ /*
+ * Check to see if we're still flinging with an appreciable velocity. The threshold is
+ * higher in the case of overscroll, so we bounce back eagerly when overscrolling but
+ * coast smoothly to a stop when not. In other words, require a greater velocity to
+ * maintain the fling once we enter overscroll.
+ */
+ float threshold = (overscrolled && !mSubscroller.scrolling() ? STOPPED_THRESHOLD : FLING_STOPPED_THRESHOLD);
+ if (getVelocity() >= threshold) {
+ // we're still flinging
+ return;
+ }
+
+ mX.stopFling();
+ mY.stopFling();
+ }
+
+ /* Perform a bounce-back animation if overscrolled. */
+ if (overscrolled) {
+ bounce();
+ } else {
+ finishAnimation();
+ mState = PanZoomState.NOTHING;
+ }
+ }
+ }
+
private void finishAnimation() {
+ checkMainThread();
+
Log.d(LOGTAG, "Finishing animation at " + mController.getViewportMetrics());
stopAnimationTimer();
@@ -517,6 +655,26 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
return viewportMetrics;
}
+ private class AxisX extends Axis {
+ AxisX(SubdocumentScrollHelper subscroller) { super(subscroller); }
+ @Override
+ public float getOrigin() { return mController.getOrigin().x; }
+ @Override
+ protected float getViewportLength() { return mController.getViewportSize().width; }
+ @Override
+ protected float getPageLength() { return mController.getPageSize().width; }
+ }
+
+ private class AxisY extends Axis {
+ AxisY(SubdocumentScrollHelper subscroller) { super(subscroller); }
+ @Override
+ public float getOrigin() { return mController.getOrigin().y; }
+ @Override
+ protected float getViewportLength() { return mController.getViewportSize().height; }
+ @Override
+ protected float getPageLength() { return mController.getPageSize().height; }
+ }
+
/*
* Zooming
*/
@@ -555,11 +713,10 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
* factor toward 1.0.
*/
float resistance = Math.min(mX.getEdgeResistance(), mY.getEdgeResistance());
- if (spanRatio > 1.0f) {
+ if (spanRatio > 1.0f)
spanRatio = 1.0f + (spanRatio - 1.0f) * resistance;
- } else {
+ else
spanRatio = 1.0f - (1.0f - spanRatio) * resistance;
- }
synchronized (mController) {
float newZoomFactor = mController.getZoomFactor() * spanRatio;
@@ -568,12 +725,12 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
// such that it asymptotically reaches MAX_ZOOM + 1.0
// but never exceeds that
float excessZoom = newZoomFactor - MAX_ZOOM;
- excessZoom = 1.0f - (float) Math.exp(-excessZoom);
+ excessZoom = 1.0f - (float)Math.exp(-excessZoom);
newZoomFactor = MAX_ZOOM + excessZoom;
}
mController.scrollBy(new PointF(mLastZoomFocus.x - detector.getFocusX(),
- mLastZoomFocus.y - detector.getFocusY()));
+ mLastZoomFocus.y - detector.getFocusY()));
PointF focus = new PointF(detector.getFocusX(), detector.getFocusY());
mController.scaleWithFocus(newZoomFactor, focus);
}
@@ -594,7 +751,6 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
startTouch(detector.getFocusX(), detector.getFocusY(), detector.getEventTime());
// Force a viewport synchronisation
- //GeckoApp.mAppContext.showPlugins();
mController.setForceRedraw();
mController.notifyLayerClientOfGeometryChange();
}
@@ -663,193 +819,4 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
bounce(finalMetrics);
return true;
}
-
- private enum PanZoomState {
- NOTHING, /* no touch-start events received */
- FLING, /* all touches removed, but we're still scrolling page */
- TOUCHING, /* one touch-start event received */
- PANNING_LOCKED, /* touch-start followed by move (i.e. panning with axis lock) */
- PANNING, /* panning without axis lock */
- PANNING_HOLD, /* in panning, but not moving.
- * similar to TOUCHING but after starting a pan */
- PANNING_HOLD_LOCKED, /* like PANNING_HOLD, but axis lock still in effect */
- PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */
- ANIMATED_ZOOM /* animated zoom to a new rect */
- }
-
- private abstract class AnimationRunnable implements Runnable {
- private boolean mAnimationTerminated;
-
- /* This should always run on the UI thread */
- public final void run() {
- /*
- * Since the animation timer queues this runnable on the UI thread, it
- * is possible that even when the animation timer is cancelled, there
- * are multiple instances of this queued, so we need to have another
- * mechanism to abort. This is done by using the mAnimationTerminated flag.
- */
- if (mAnimationTerminated) {
- return;
- }
- animateFrame();
- }
-
- protected abstract void animateFrame();
-
- /* This should always run on the UI thread */
- protected final void terminate() {
- mAnimationTerminated = true;
- }
- }
-
- /* The callback that performs the bounce animation. */
- private class BounceRunnable extends AnimationRunnable {
- /* The current frame of the bounce-back animation */
- private int mBounceFrame;
- /*
- * The viewport metrics that represent the start and end of the bounce-back animation,
- * respectively.
- */
- private ViewportMetrics mBounceStartMetrics;
- private ViewportMetrics mBounceEndMetrics;
-
- BounceRunnable(ViewportMetrics startMetrics, ViewportMetrics endMetrics) {
- mBounceStartMetrics = startMetrics;
- mBounceEndMetrics = endMetrics;
- }
-
- protected void animateFrame() {
- /*
- * The pan/zoom controller might have signaled to us that it wants to abort the
- * animation by setting the state to PanZoomState.NOTHING. Handle this case and bail
- * out.
- */
- if (mState != PanZoomState.FLING) {
- finishAnimation();
- return;
- }
-
- /* Perform the next frame of the bounce-back animation. */
- if (mBounceFrame < EASE_OUT_ANIMATION_FRAMES.length) {
- advanceBounce();
- return;
- }
-
- /* Finally, if there's nothing else to do, complete the animation and go to sleep. */
- finishBounce();
- finishAnimation();
- mState = PanZoomState.NOTHING;
- }
-
- /* Performs one frame of a bounce animation. */
- private void advanceBounce() {
- synchronized (mController) {
- float t = EASE_OUT_ANIMATION_FRAMES[mBounceFrame];
- ViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t);
- mController.setViewportMetrics(newMetrics);
- mController.notifyLayerClientOfGeometryChange();
- mBounceFrame++;
- }
- }
-
- /* Concludes a bounce animation and snaps the viewport into place. */
- private void finishBounce() {
- synchronized (mController) {
- mController.setViewportMetrics(mBounceEndMetrics);
- mController.notifyLayerClientOfGeometryChange();
- mBounceFrame = -1;
- }
- }
- }
-
- // The callback that performs the fling animation.
- private class FlingRunnable extends AnimationRunnable {
- protected void animateFrame() {
- /*
- * The pan/zoom controller might have signaled to us that it wants to abort the
- * animation by setting the state to PanZoomState.NOTHING. Handle this case and bail
- * out.
- */
- if (mState != PanZoomState.FLING) {
- finishAnimation();
- return;
- }
-
- /* Advance flings, if necessary. */
- boolean flingingX = mX.advanceFling();
- boolean flingingY = mY.advanceFling();
-
- boolean overscrolled = (mX.overscrolled() || mY.overscrolled());
-
- /* If we're still flinging in any direction, update the origin. */
- if (flingingX || flingingY) {
- updatePosition();
-
- /*
- * Check to see if we're still flinging with an appreciable velocity. The threshold is
- * higher in the case of overscroll, so we bounce back eagerly when overscrolling but
- * coast smoothly to a stop when not. In other words, require a greater velocity to
- * maintain the fling once we enter overscroll.
- */
- float threshold = (overscrolled && !mSubscroller.scrolling() ? STOPPED_THRESHOLD : FLING_STOPPED_THRESHOLD);
- if (getVelocity() >= threshold) {
- // we're still flinging
- return;
- }
-
- mX.stopFling();
- mY.stopFling();
- }
-
- /* Perform a bounce-back animation if overscrolled. */
- if (overscrolled) {
- bounce();
- } else {
- finishAnimation();
- mState = PanZoomState.NOTHING;
- }
- }
- }
-
- private class AxisX extends Axis {
- AxisX(SubdocumentScrollHelper subscroller) {
- super(subscroller);
- }
-
- @Override
- public float getOrigin() {
- return mController.getOrigin().x;
- }
-
- @Override
- protected float getViewportLength() {
- return mController.getViewportSize().width;
- }
-
- @Override
- protected float getPageLength() {
- return mController.getPageSize().width;
- }
- }
-
- private class AxisY extends Axis {
- AxisY(SubdocumentScrollHelper subscroller) {
- super(subscroller);
- }
-
- @Override
- public float getOrigin() {
- return mController.getOrigin().y;
- }
-
- @Override
- protected float getViewportLength() {
- return mController.getViewportSize().height;
- }
-
- @Override
- protected float getPageLength() {
- return mController.getPageSize().height;
- }
- }
}
commit 046e19ef2cb02d11560b5812364aa5211b20e4ac
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.com>
Date: Thu Sep 18 22:10:49 2014 +0200
android: thumbnail as background when tile is not available
Change-Id: Id38e40b3fdb46adeb19e6a1e106775391c5da455
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
index e75276d..c5918e69 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
@@ -1,5 +1,6 @@
package org.libreoffice;
+import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
@@ -22,6 +23,7 @@ public class LOKitThread extends Thread {
private ViewportMetrics mViewportMetrics;
private String mInputFile;
private Rect mOldRect;
+ private boolean mCheckboardImageSet = false;
LOKitThread(String inputFile) {
mInputFile = inputFile;
@@ -115,6 +117,7 @@ public class LOKitThread extends Thread {
layerClient.endDrawing();
Log.i(LOGTAG, "tilerender end draw");
+
return true;
}
@@ -128,7 +131,22 @@ public class LOKitThread extends Thread {
private boolean initialize() {
mApplication = LibreOfficeMainActivity.mAppContext;
mTileProvider = new LOKitTileProvider(mApplication.getLayerController(), mInputFile);
- return mTileProvider.isReady();
+ boolean isReady = mTileProvider.isReady();
+ if (isReady)
+ {
+ if (!mCheckboardImageSet) {
+ Log.i(LOGTAG, "Generate thumbnail!");
+ Bitmap bitmap = mTileProvider.thumbnail();
+ Log.i(LOGTAG, "Done generate thumbnail!");
+ if (bitmap != null) {
+ Log.i(LOGTAG, "Setting checkboard image!");
+ mApplication.getLayerController().getView().changeCheckerboardBitmap(bitmap);
+ Log.i(LOGTAG, "Done setting checkboard image!!");
+ mCheckboardImageSet = true;
+ }
+ }
+ }
+ return isReady;
}
public void run() {
commit 6ca57cb25ef8826aac5584dee5b41b6ad6114555
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.com>
Date: Thu Sep 18 22:09:48 2014 +0200
android: fix thumbnail() to produce a valid bitmap
Change-Id: I578ac9482f334765c71a66421a3fa2dfb85e22b3
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
index 5a906c9..f13dd8a 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
@@ -122,9 +122,6 @@ public class LOKitTileProvider implements TileProvider {
@Override
public Bitmap thumbnail() {
- ByteBuffer buffer = ByteBuffer.allocateDirect(TILE_SIZE * TILE_SIZE * 4);
- Bitmap bitmap = Bitmap.createBitmap(TILE_SIZE, TILE_SIZE, Bitmap.Config.ARGB_8888);
-
int widthPixel = getPageWidth();
int heightPixel = getPageHeight();
@@ -138,8 +135,14 @@ public class LOKitTileProvider implements TileProvider {
widthPixel = (int) (heightPixel * ratio);
}
+ ByteBuffer buffer = ByteBuffer.allocateDirect(widthPixel * heightPixel * 4);
mDocument.paintTile(buffer, widthPixel, heightPixel, 0, 0, (int) mWidthTwip, (int) mHeightTwip);
+ Bitmap bitmap = Bitmap.createBitmap(widthPixel, heightPixel, Bitmap.Config.ARGB_8888);
+ bitmap.copyPixelsFromBuffer(buffer);
+ if (bitmap == null) {
+ Log.w(LOGTAG, "Thumbnail not created!");
+ }
return bitmap;
}
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java
index 60abbdf..2a89e77 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java
@@ -63,7 +63,7 @@ public class MockTileProvider implements TileProvider {
@Override
public Bitmap thumbnail() {
- return null;
+ return layerController.getDrawable("dummy_page");
}
@Override
commit 58f5e531f792567beb99a6eee346ac61e6f94938
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.com>
Date: Thu Sep 18 22:08:01 2014 +0200
android: import changes from Fennec to get ScreenShotLayer
Change-Id: I1b72af85a906aef289d1be086274941094df4f96
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
index cf13afb..1ebb9a1 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
@@ -40,6 +40,7 @@ package org.mozilla.gecko.gfx;
import android.content.Context;
import android.graphics.PointF;
+import android.graphics.RectF;
import android.os.SystemClock;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -53,11 +54,13 @@ import java.util.List;
public class GeckoLayerClient {
private static final String LOGTAG = "GeckoLayerClient";
- private static final long MIN_VIEWPORT_CHANGE_DELAY = 25L;
+ private static final int DEFAULT_DISPLAY_PORT_MARGIN = 300;
+ private static final long MIN_VIEWPORT_CHANGE_DELAY = 25L;
private static final IntSize TILE_SIZE = new IntSize(256, 256);
protected IntSize mScreenSize;
+ private RectF mDisplayPort;
protected Layer mTileLayer;
/* The viewport that Gecko is currently displaying. */
protected ViewportMetrics mGeckoViewport;
@@ -79,6 +82,7 @@ public class GeckoLayerClient {
public GeckoLayerClient(Context context) {
mContext = context;
mScreenSize = new IntSize(0, 0);
+ mDisplayPort = new RectF();
}
protected void setupLayer() {
@@ -110,7 +114,7 @@ public class GeckoLayerClient {
layerController.setViewportMetrics(mGeckoViewport);
}
- sendResizeEventIfNecessary(false);
+ sendResizeEventIfNecessary(true);
}
public void beginDrawing(ViewportMetrics viewportMetrics) {
@@ -132,6 +136,10 @@ public class GeckoLayerClient {
Log.i(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - endDrawing");
}
+ RectF getDisplayPort() {
+ return mDisplayPort;
+ }
+
protected void updateViewport(boolean onlyUpdatePageSize) {
// save and restore the viewport size stored in java; never let the
// JS-side viewport dimensions override the java-side ones because
@@ -142,7 +150,8 @@ public class GeckoLayerClient {
mGeckoViewport.setSize(viewportSize);
PointF displayportOrigin = mGeckoViewport.getDisplayportOrigin();
- mTileLayer.setOrigin(PointUtils.round(displayportOrigin));
+ RectF position = mGeckoViewport.getViewport();
+ mTileLayer.setPosition(RectUtils.round(position));
mTileLayer.setResolution(mGeckoViewport.getZoomFactor());
Log.e(LOGTAG, "### updateViewport onlyUpdatePageSize=" + onlyUpdatePageSize + " getTileViewport " + mGeckoViewport);
@@ -160,7 +169,7 @@ public class GeckoLayerClient {
}
/* Informs Gecko that the screen size has changed. */
- protected void sendResizeEventIfNecessary(boolean force) {
+ private void sendResizeEventIfNecessary(boolean force) {
Log.e(LOGTAG, "### sendResizeEventIfNecessary " + force);
DisplayMetrics metrics = new DisplayMetrics();
@@ -172,9 +181,8 @@ public class GeckoLayerClient {
// size is zero (which indicates that the rendering surface hasn't been
// allocated yet).
boolean screenSizeChanged = !mScreenSize.equals(newScreenSize);
- boolean viewportSizeValid = (mLayerController != null && mLayerController.getViewportSize().isPositive());
- if (!(force || (screenSizeChanged && viewportSizeValid))) {
+ if (!force && !screenSizeChanged) {
return;
}
@@ -213,11 +221,75 @@ public class GeckoLayerClient {
mViewportSizeChanged = true;
}
+ private static RectF calculateDisplayPort(ImmutableViewportMetrics metrics) {
+ float desiredXMargins = 2 * DEFAULT_DISPLAY_PORT_MARGIN;
+ float desiredYMargins = 2 * DEFAULT_DISPLAY_PORT_MARGIN;
+
+ // we need to avoid having a display port that is larger than the page, or we will end up
+ // painting things outside the page bounds (bug 729169). we simultaneously need to make
+ // the display port as large as possible so that we redraw less.
+
+ // figure out how much of the desired buffer amount we can actually use on the horizontal axis
+ float xBufferAmount = Math.min(desiredXMargins, metrics.pageSizeWidth - metrics.getWidth());
+ // if we reduced the buffer amount on the horizontal axis, we should take that saved memory and
+ // use it on the vertical axis
+ float savedPixels = (desiredXMargins - xBufferAmount) * (metrics.getHeight() + desiredYMargins);
+ float extraYAmount = (float)Math.floor(savedPixels / (metrics.getWidth() + xBufferAmount));
+ float yBufferAmount = Math.min(desiredYMargins + extraYAmount, metrics.pageSizeHeight - metrics.getHeight());
+ // and the reverse - if we shrunk the buffer on the vertical axis we can add it to the horizontal
+ if (xBufferAmount == desiredXMargins && yBufferAmount < desiredYMargins) {
+ savedPixels = (desiredYMargins - yBufferAmount) * (metrics.getWidth() + xBufferAmount);
+ float extraXAmount = (float)Math.floor(savedPixels / (metrics.getHeight() + yBufferAmount));
+ xBufferAmount = Math.min(xBufferAmount + extraXAmount, metrics.pageSizeWidth - metrics.getWidth());
+ }
+
+ // and now calculate the display port margins based on how much buffer we've decided to use and
+ // the page bounds, ensuring we use all of the available buffer amounts on one side or the other
+ // on any given axis. (i.e. if we're scrolled to the top of the page, the vertical buffer is
+ // entirely below the visible viewport, but if we're halfway down the page, the vertical buffer
+ // is split).
+ float leftMargin = Math.min(DEFAULT_DISPLAY_PORT_MARGIN, metrics.viewportRectLeft);
+ float rightMargin = Math.min(DEFAULT_DISPLAY_PORT_MARGIN, metrics.pageSizeWidth - (metrics.viewportRectLeft + metrics.getWidth()));
+ if (leftMargin < DEFAULT_DISPLAY_PORT_MARGIN) {
+ rightMargin = xBufferAmount - leftMargin;
+ } else if (rightMargin < DEFAULT_DISPLAY_PORT_MARGIN) {
+ leftMargin = xBufferAmount - rightMargin;
+ } else if (!FloatUtils.fuzzyEquals(leftMargin + rightMargin, xBufferAmount)) {
+ float delta = xBufferAmount - leftMargin - rightMargin;
+ leftMargin += delta / 2;
+ rightMargin += delta / 2;
+ }
+
+ float topMargin = Math.min(DEFAULT_DISPLAY_PORT_MARGIN, metrics.viewportRectTop);
+ float bottomMargin = Math.min(DEFAULT_DISPLAY_PORT_MARGIN, metrics.pageSizeHeight - (metrics.viewportRectTop + metrics.getHeight()));
+ if (topMargin < DEFAULT_DISPLAY_PORT_MARGIN) {
+ bottomMargin = yBufferAmount - topMargin;
+ } else if (bottomMargin < DEFAULT_DISPLAY_PORT_MARGIN) {
+ topMargin = yBufferAmount - bottomMargin;
+ } else if (!FloatUtils.fuzzyEquals(topMargin + bottomMargin, yBufferAmount)) {
+ float delta = yBufferAmount - topMargin - bottomMargin;
+ topMargin += delta / 2;
+ bottomMargin += delta / 2;
+ }
+
+ // note that unless the viewport size changes, or the page dimensions change (either because of
+ // content changes or zooming), the size of the display port should remain constant. this
+ // is intentional to avoid re-creating textures and all sorts of other reallocations in the
+ // draw and composition code.
+ return new RectF(metrics.viewportRectLeft - leftMargin,
+ metrics.viewportRectTop - topMargin,
+ metrics.viewportRectRight + rightMargin,
+ metrics.viewportRectBottom + bottomMargin);
+ }
+
private void adjustViewport() {
- ViewportMetrics viewportMetrics = new ViewportMetrics(mLayerController.getViewportMetrics());
+ ViewportMetrics viewportMetrics =
+ new ViewportMetrics(mLayerController.getViewportMetrics());
viewportMetrics.setViewport(viewportMetrics.getClampedViewport());
+ mDisplayPort = calculateDisplayPort(new ImmutableViewportMetrics(mLayerController.getViewportMetrics()));
+
LOKitShell.sendEvent(LOEvent.viewport(viewportMetrics));
if (mViewportSizeChanged) {
mViewportSizeChanged = false;
@@ -228,19 +300,18 @@ public class GeckoLayerClient {
}
public void geometryChanged() {
- sendResizeEventIfNecessary();
- render();
+ sendResizeEventIfNecessary(false);
+ if (mLayerController.getRedrawHint())
+ adjustViewport();
}
public ViewportMetrics getGeckoViewportMetrics() {
+ // Return a copy, as we modify this inside the Gecko thread
if (mGeckoViewport != null)
return new ViewportMetrics(mGeckoViewport);
return null;
}
- private void sendResizeEventIfNecessary() {
- sendResizeEventIfNecessary(false);
- }
public void addTile(SubTile tile) {
if (mTileLayer instanceof MultiTileLayer) {
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ImmutableViewportMetrics.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ImmutableViewportMetrics.java
new file mode 100644
index 0000000..b9eb34d
--- /dev/null
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ImmutableViewportMetrics.java
@@ -0,0 +1,70 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.gfx;
+
+import android.graphics.PointF;
+import android.graphics.RectF;
+
+/**
+ * ImmutableViewportMetrics are used to store the viewport metrics
+ * in way that we can access a version of them from multiple threads
+ * without having to take a lock
+ */
+public class ImmutableViewportMetrics {
+
+ // We need to flatten the RectF and FloatSize structures
+ // because Java doesn't have the concept of const classes
+ public final float pageSizeWidth;
+ public final float pageSizeHeight;
+ public final float viewportRectBottom;
+ public final float viewportRectLeft;
+ public final float viewportRectRight;
+ public final float viewportRectTop;
+ public final float zoomFactor;
+
+ public ImmutableViewportMetrics(ViewportMetrics m) {
+ RectF viewportRect = m.getViewport();
+ viewportRectBottom = viewportRect.bottom;
+ viewportRectLeft = viewportRect.left;
+ viewportRectRight = viewportRect.right;
+ viewportRectTop = viewportRect.top;
+
+ FloatSize pageSize = m.getPageSize();
+ pageSizeWidth = pageSize.width;
+ pageSizeHeight = pageSize.height;
+
+ zoomFactor = m.getZoomFactor();
+ }
+
+ public float getWidth() {
+ return viewportRectRight - viewportRectLeft;
+ }
+
+ public float getHeight() {
+ return viewportRectBottom - viewportRectTop;
+ }
+
+ // some helpers to make ImmutableViewportMetrics act more like ViewportMetrics
+
+ public PointF getOrigin() {
+ return new PointF(viewportRectLeft, viewportRectTop);
+ }
+
+ public FloatSize getSize() {
+ return new FloatSize(viewportRectRight - viewportRectLeft, viewportRectBottom - viewportRectTop);
+ }
+
+ public RectF getViewport() {
+ return new RectF(viewportRectLeft,
+ viewportRectTop,
+ viewportRectRight,
+ viewportRectBottom);
+ }
+
+ public FloatSize getPageSize() {
+ return new FloatSize(pageSizeWidth, pageSizeHeight);
+ }
+}
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/Layer.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/Layer.java
index cc689ed..7e575b5 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/Layer.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/Layer.java
@@ -39,7 +39,7 @@
package org.mozilla.gecko.gfx;
-import android.graphics.Point;
+import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
@@ -50,16 +50,24 @@ import java.util.concurrent.locks.ReentrantLock;
public abstract class Layer {
private final ReentrantLock mTransactionLock;
- protected Point mOrigin;
- protected float mResolution;
private boolean mInTransaction;
- private Point mNewOrigin;
+ private Rect mNewPosition;
private float mNewResolution;
- private LayerView mView;
+
+ protected Rect mPosition;
+ protected float mResolution;
public Layer() {
+ this(null);
+ }
+
+ public Layer(IntSize size) {
mTransactionLock = new ReentrantLock();
- mOrigin = new Point(0, 0);
+ if (size == null) {
+ mPosition = new Rect();
+ } else {
+ mPosition = new Rect(0, 0, size.width, size.height);
+ }
mResolution = 1.0f;
}
@@ -69,12 +77,14 @@ public abstract class Layer {
*/
public final boolean update(RenderContext context) {
if (mTransactionLock.isHeldByCurrentThread()) {
- throw new RuntimeException("draw() called while transaction lock held by this thread?!");
+ throw new RuntimeException("draw() called while transaction lock held by this " +
+ "thread?!");
}
if (mTransactionLock.tryLock()) {
try {
- return performUpdates(context);
+ performUpdates(context);
+ return true;
} finally {
mTransactionLock.unlock();
}
@@ -83,24 +93,12 @@ public abstract class Layer {
return false;
}
- /**
- * Subclasses override this function to draw the layer.
- */
+ /** Subclasses override this function to draw the layer. */
public abstract void draw(RenderContext context);
- /**
- * Subclasses override this function to provide access to the size of the layer.
- */
- public abstract IntSize getSize();
-
- /**
- * Given the intrinsic size of the layer, returns the pixel boundaries of the layer rect.
- */
- protected RectF getBounds(RenderContext context, FloatSize size) {
- float scaleFactor = context.zoomFactor / mResolution;
- float x = mOrigin.x * scaleFactor, y = mOrigin.y * scaleFactor;
- float width = size.width * scaleFactor, height = size.height * scaleFactor;
- return new RectF(x, y, x + width, y + height);
+ /** Given the intrinsic size of the layer, returns the pixel boundaries of the layer rect. */
+ protected RectF getBounds(RenderContext context) {
+ return RectUtils.scale(new RectF(mPosition), context.zoomFactor / mResolution);
}
/**
@@ -109,68 +107,50 @@ public abstract class Layer {
* may be overridden.
*/
public Region getValidRegion(RenderContext context) {
- return new Region(RectUtils.round(getBounds(context, new FloatSize(getSize()))));
+ return new Region(RectUtils.round(getBounds(context)));
}
/**
* Call this before modifying the layer. Note that, for TileLayers, "modifying the layer"
* includes altering the underlying CairoImage in any way. Thus you must call this function
* before modifying the byte buffer associated with this layer.
- * <p/>
+ *
* This function may block, so you should never call this on the main UI thread.
*/
- public void beginTransaction(LayerView aView) {
- //if (mTransactionLock.isHeldByCurrentThread())
- // throw new RuntimeException("Nested transactions are not supported");
+ public void beginTransaction() {
+ if (mTransactionLock.isHeldByCurrentThread())
+ throw new RuntimeException("Nested transactions are not supported");
mTransactionLock.lock();
- mView = aView;
mInTransaction = true;
mNewResolution = mResolution;
}
- public void beginTransaction() {
- beginTransaction(null);
- }
-
- /**
- * Call this when you're done modifying the layer.
- */
+ /** Call this when you're done modifying the layer. */
public void endTransaction() {
if (!mInTransaction)
throw new RuntimeException("endTransaction() called outside a transaction");
mInTransaction = false;
mTransactionLock.unlock();
-
- if (mView != null)
- mView.requestRender();
}
- /**
- * Returns true if the layer is currently in a transaction and false otherwise.
- */
+ /** Returns true if the layer is currently in a transaction and false otherwise. */
protected boolean inTransaction() {
return mInTransaction;
}
- /**
- * Returns the current layer origin.
- */
- public Point getOrigin() {
- return mOrigin;
+ /** Returns the current layer position. */
+ public Rect getPosition() {
+ return mPosition;
}
- /**
- * Sets the origin. Only valid inside a transaction.
- */
- public void setOrigin(Point newOrigin) {
+ /** Sets the position. Only valid inside a transaction. */
+ public void setPosition(Rect newPosition) {
if (!mInTransaction)
- throw new RuntimeException("setOrigin() is only valid inside a transaction");
- mNewOrigin = newOrigin;
+ throw new RuntimeException("setPosition() is only valid inside a transaction");
+ mNewPosition = newPosition;
}
- /**
- * Returns the current layer's resolution.
- */
+ /** Returns the current layer's resolution. */
public float getResolution() {
return mResolution;
}
@@ -179,8 +159,7 @@ public abstract class Layer {
* Sets the layer resolution. This value is used to determine how many pixels per
* device pixel this layer was rendered at. This will be reflected by scaling by
* the reciprocal of the resolution in the layer's transform() function.
- * Only valid inside a transaction.
- */
+ * Only valid inside a transaction. */
public void setResolution(float newResolution) {
if (!mInTransaction)
throw new RuntimeException("setResolution() is only valid inside a transaction");
@@ -193,22 +172,15 @@ public abstract class Layer {
* superclass implementation. Returns false if there is still work to be done after this
* update is complete.
*/
- protected boolean performUpdates(RenderContext context) {
-
- if (mNewOrigin != null) {
- mOrigin = mNewOrigin;
- mNewOrigin = null;
+ protected void performUpdates(RenderContext context) {
+ if (mNewPosition != null) {
+ mPosition = mNewPosition;
+ mNewPosition = null;
}
if (mNewResolution != 0.0f) {
mResolution = mNewResolution;
mNewResolution = 0.0f;
}
-
- return true;
- }
-
- protected boolean dimensionChangesPending() {
- return (mNewOrigin != null) || (mNewResolution != 0.0f);
}
public static class RenderContext {
@@ -234,8 +206,8 @@ public abstract class Layer {
return false;
}
return RectUtils.fuzzyEquals(viewport, other.viewport)
- && pageSize.fuzzyEquals(other.pageSize)
- && FloatUtils.fuzzyEquals(zoomFactor, other.zoomFactor);
+ && pageSize.fuzzyEquals(other.pageSize)
+ && FloatUtils.fuzzyEquals(zoomFactor, other.zoomFactor);
}
}
}
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerController.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerController.java
index 54def4a..9c497f7 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerController.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerController.java
@@ -42,8 +42,8 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.graphics.Point;
import android.graphics.PointF;
+import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
import android.view.GestureDetector;
@@ -55,172 +55,167 @@ import org.mozilla.gecko.ui.SimpleScaleGestureDetector;
import java.util.Timer;
import java.util.TimerTask;
+import java.util.regex.Pattern;
/**
* The layer controller manages a tile that represents the visible page. It does panning and
* zooming natively by delegating to a panning/zooming controller. Touch events can be dispatched
* to a higher-level view.
- * <p/>
+ *
* Many methods require that the monitor be held, with a synchronized (controller) { ... } block.
*/
public class LayerController {
- /* The extra area on the sides of the page that we want to buffer to help with
- * smooth, asynchronous scrolling. Depending on a device's support for NPOT
- * textures, this may be rounded up to the nearest power of two.
- */
private static final String LOGTAG = "GeckoLayerController";
- /* If the visible rect is within the danger zone (measured in pixels from each edge of a tile),
- * we start aggressively redrawing to minimize checkerboarding. */
- private static final int DANGER_ZONE_X = 75;
- private static final int DANGER_ZONE_Y = 150;
- /* The time limit for pages to respond with preventDefault on touchevents
- * before we begin panning the page */
- private static final int PREVENT_DEFAULT_TIMEOUT = 200;
+
private Layer mRootLayer; /* The root layer. */
private LayerView mView; /* The main rendering view. */
-
private Context mContext; /* The current context. */
private ViewportMetrics mViewportMetrics; /* The current viewport metrics. */
private boolean mWaitForTouchListeners;
+
private PanZoomController mPanZoomController;
+ /*
+ * The panning and zooming controller, which interprets pan and zoom gestures for us and
+ * updates our visible rect appropriately.
+ */
+
private OnTouchListener mOnTouchListener; /* The touch listener. */
- private GeckoLayerClient mLayerClient; /* The layer client. */
+ private GeckoLayerClient mLayerClient; /* The layer client. */
+
/* The new color for the checkerboard. */
private int mCheckerboardColor;
private boolean mCheckerboardShouldShowChecks;
+
private boolean mForceRedraw;
+
+ /* The extra area on the sides of the page that we want to buffer to help with
+ * smooth, asynchronous scrolling. Depending on a device's support for NPOT
+ * textures, this may be rounded up to the nearest power of two.
+ */
+ public static final IntSize MIN_BUFFER = new IntSize(512, 1024);
+
+ /* If the visible rect is within the danger zone (measured in pixels from each edge of a tile),
+ * we start aggressively redrawing to minimize checkerboarding. */
+ private static final int DANGER_ZONE_X = 75;
+ private static final int DANGER_ZONE_Y = 150;
+
+ /* The time limit for pages to respond with preventDefault on touchevents
+ * before we begin panning the page */
+ private int mTimeout = 200;
+
private boolean allowDefaultActions = true;
- private Timer allowDefaultTimer = null;
- private boolean inTouchSession = false;
+ private Timer allowDefaultTimer = null;
private PointF initialTouchLocation = null;
+ private static Pattern sColorPattern;
+
public LayerController(Context context) {
mContext = context;
+
mForceRedraw = true;
mViewportMetrics = new ViewportMetrics();
mPanZoomController = new PanZoomController(this);
mView = new LayerView(context, this);
+ mCheckerboardShouldShowChecks = true;
}
- public void setForceRedraw() {
- mForceRedraw = true;
+ public void onDestroy() {
}
- public GeckoLayerClient getLayerClient() {
- return mLayerClient;
- }
+ public void setRoot(Layer layer) { mRootLayer = layer; }
public void setLayerClient(GeckoLayerClient layerClient) {
mLayerClient = layerClient;
layerClient.setLayerController(this);
}
- public Layer getRoot() {
- return mRootLayer;
+ public void setForceRedraw() {
+ mForceRedraw = true;
}
- public void setRoot(Layer layer) {
- mRootLayer = layer;
+ public Layer getRoot() { return mRootLayer; }
+ public LayerView getView() { return mView; }
+ public Context getContext() { return mContext; }
+ public ViewportMetrics getViewportMetrics() { return mViewportMetrics; }
+
+ public RectF getViewport() {
+ return mViewportMetrics.getViewport();
}
- public LayerView getView() {
- return mView;
+ public FloatSize getViewportSize() {
+ return mViewportMetrics.getSize();
}
- public Context getContext() {
- return mContext;
+ public FloatSize getPageSize() {
+ return mViewportMetrics.getPageSize();
}
- public ViewportMetrics getViewportMetrics() {
- return mViewportMetrics;
+ public PointF getOrigin() {
+ return mViewportMetrics.getOrigin();
}
- /**
- * Sets the entire viewport metrics at once. This function does not notify the layer client or
- * the pan/zoom controller, so you will need to call notifyLayerClientOfGeometryChange() or
- * notifyPanZoomControllerOfGeometryChange() after calling this. You must hold the monitor
- * while calling this.
- */
- public void setViewportMetrics(ViewportMetrics viewport) {
- mViewportMetrics = new ViewportMetrics(viewport);
- Log.d(LOGTAG, "setViewportMetrics: " + mViewportMetrics);
- // this function may or may not be called on the UI thread,
- // but repositionPluginViews must only be called on the UI thread.
- //GeckoApp.mAppContext.runOnUiThread(new Runnable() {
- // public void run() {
- // GeckoApp.mAppContext.repositionPluginViews(false);
- // }
- //});
- mView.requestRender();
+ public float getZoomFactor() {
+ return mViewportMetrics.getZoomFactor();
}
- public RectF getViewport() {
- return mViewportMetrics.getViewport();
+ public Bitmap getBackgroundPattern() { return getDrawable("background"); }
+ public Bitmap getShadowPattern() { return getDrawable("shadow"); }
+
+ public PanZoomController getPanZoomController() { return mPanZoomController; }
+ public GestureDetector.OnGestureListener getGestureListener() { return mPanZoomController; }
+ public SimpleScaleGestureDetector.SimpleScaleGestureListener getScaleGestureListener() {
+ return mPanZoomController;
}
+ public GestureDetector.OnDoubleTapListener getDoubleTapListener() { return mPanZoomController; }
- public FloatSize getViewportSize() {
- return mViewportMetrics.getSize();
+ public Bitmap getDrawable(String name) {
+ Resources resources = mContext.getResources();
+ int resourceID = resources.getIdentifier(name, "drawable", mContext.getPackageName());
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inScaled = false;
+ return BitmapFactory.decodeResource(mContext.getResources(), resourceID, options);
}
/**
* The view calls this function to indicate that the viewport changed size. It must hold the
* monitor while calling it.
- * <p/>
+ *
* TODO: Refactor this to use an interface. Expose that interface only to the view and not
* to the layer client. That way, the layer client won't be tempted to call this, which might
* result in an infinite loop.
*/
public void setViewportSize(FloatSize size) {
- // Resize the viewport, and modify its zoom factor so that the page retains proportionally
- // zoomed relative to the screen.
- float oldHeight = mViewportMetrics.getSize().height;
- float oldWidth = mViewportMetrics.getSize().width;
- float oldZoomFactor = mViewportMetrics.getZoomFactor();
- mViewportMetrics.setSize(size);
-
- // if the viewport got larger (presumably because the vkb went away), and the page
- // is smaller than the new viewport size, increase the page size so that the panzoomcontroller
- // doesn't zoom in to make it fit (bug 718270). this page size change is in anticipation of
- // gecko increasing the page size to match the new viewport size, which will happen the next
- // time we get a draw update.
- if (size.width >= oldWidth && size.height >= oldHeight) {
- FloatSize pageSize = mViewportMetrics.getPageSize();
- if (pageSize.width < size.width || pageSize.height < size.height) {
- mViewportMetrics.setPageSize(new FloatSize(Math.max(pageSize.width, size.width),
- Math.max(pageSize.height, size.height)));
- }
- }
-
- PointF newFocus = new PointF(size.width / 2.0f, size.height / 2.0f);
- float newZoomFactor = size.width * oldZoomFactor / oldWidth;
- mViewportMetrics.scaleTo(newZoomFactor, newFocus);
+ ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics);
+ viewportMetrics.setSize(size);
+ mViewportMetrics = new ViewportMetrics(viewportMetrics);
- Log.d(LOGTAG, "setViewportSize: " + mViewportMetrics);
- setForceRedraw();
-
- if (mLayerClient != null)
+ if (mLayerClient != null) {
mLayerClient.viewportSizeChanged();
+ }
+ }
+
+ /** Scrolls the viewport by the given offset. You must hold the monitor while calling this. */
+ public void scrollBy(PointF point) {
+ ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics);
+ PointF origin = viewportMetrics.getOrigin();
+ origin.offset(point.x, point.y);
+ viewportMetrics.setOrigin(origin);
+ mViewportMetrics = new ViewportMetrics(viewportMetrics);
notifyLayerClientOfGeometryChange();
- mPanZoomController.abortAnimation();
mView.requestRender();
}
- public FloatSize getPageSize() {
- return mViewportMetrics.getPageSize();
- }
-
- /**
- * Sets the current page size. You must hold the monitor while calling this.
- */
+ /** Sets the current page size. You must hold the monitor while calling this. */
public void setPageSize(FloatSize size) {
if (mViewportMetrics.getPageSize().fuzzyEquals(size))
return;
- mViewportMetrics.setPageSize(size);
- Log.d(LOGTAG, "setPageSize: " + mViewportMetrics);
+ ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics);
+ viewportMetrics.setPageSize(size);
+ mViewportMetrics = new ViewportMetrics(viewportMetrics);
- // Page size is owned by the LayerClient, so no need to notify it of
+ // Page size is owned by the layer client, so no need to notify it of
// this change.
mView.post(new Runnable() {
@@ -231,59 +226,15 @@ public class LayerController {
});
}
- public PointF getOrigin() {
- return mViewportMetrics.getOrigin();
- }
-
- public float getZoomFactor() {
- return mViewportMetrics.getZoomFactor();
- }
-
- public Bitmap getBackgroundPattern() {
- return getDrawable("background");
- }
-
- public Bitmap getShadowPattern() {
- return getDrawable("shadow");
- }
-
- public PanZoomController getPanZoomController() {
- return mPanZoomController;
- }
-
- public GestureDetector.OnGestureListener getGestureListener() {
- return mPanZoomController;
- }
-
- public SimpleScaleGestureDetector.SimpleScaleGestureListener getScaleGestureListener() {
- return mPanZoomController;
- }
-
- public GestureDetector.OnDoubleTapListener getDoubleTapListener() {
- return mPanZoomController;
- }
-
- public Bitmap getDrawable(String name) {
- Resources resources = mContext.getResources();
- int resourceID = resources.getIdentifier(name, "drawable", mContext.getPackageName());
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inScaled = false;
- return BitmapFactory.decodeResource(mContext.getResources(), resourceID, options);
- }
-
/**
- * Scrolls the viewport by the given offset. You must hold the monitor while calling this.
+ * Sets the entire viewport metrics at once. This function does not notify the layer client or
+ * the pan/zoom controller, so you will need to call notifyLayerClientOfGeometryChange() or
+ * notifyPanZoomControllerOfGeometryChange() after calling this. You must hold the monitor
+ * while calling this.
*/
- public void scrollBy(PointF point) {
- if (point.equals(0,0)) {
- return;
- }
-
- PointF origin = mViewportMetrics.getOrigin();
- origin.offset(point.x, point.y);
- mViewportMetrics.setOrigin(origin);
- Log.d(LOGTAG, "scrollBy: " + mViewportMetrics);
- notifyLayerClientOfGeometryChange();
+ public void setViewportMetrics(ViewportMetrics viewport) {
+ mViewportMetrics = new ViewportMetrics(viewport);
+ Log.d(LOGTAG, "setViewportMetrics: " + mViewportMetrics);
mView.requestRender();
}
@@ -292,7 +243,9 @@ public class LayerController {
* scale operation. You must hold the monitor while calling this.
*/
public void scaleWithFocus(float zoomFactor, PointF focus) {
- mViewportMetrics.scaleTo(zoomFactor, focus);
+ ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics);
+ viewportMetrics.scaleTo(zoomFactor, focus);
+ mViewportMetrics = new ViewportMetrics(viewportMetrics);
Log.d(LOGTAG, "scaleWithFocus: " + mViewportMetrics + "; zf=" + zoomFactor);
// We assume the zoom level will only be modified by the
@@ -301,9 +254,7 @@ public class LayerController {
mView.requestRender();
}
- public boolean post(Runnable action) {
- return mView.post(action);
- }
+ public boolean post(Runnable action) { return mView.post(action); }
public void setOnTouchListener(OnTouchListener onTouchListener) {
mOnTouchListener = onTouchListener;
@@ -314,14 +265,11 @@ public class LayerController {
* the geometry changed.
*/
public void notifyLayerClientOfGeometryChange() {
- if (mLayerClient != null) {
+ if (mLayerClient != null)
mLayerClient.geometryChanged();
- }
}
- /**
- * Aborts any pan/zoom animation that is currently in progress.
- */
+ /** Aborts any pan/zoom animation that is currently in progress. */
public void abortPanZoomAnimation() {
if (mPanZoomController != null) {
mView.post(new Runnable() {
@@ -337,24 +285,16 @@ public class LayerController {
* would prefer that the action didn't take place.
*/
public boolean getRedrawHint() {
- // FIXME: Allow redraw while a finger is down, but only if we're about to checkerboard.
- // This requires fixing aboutToCheckerboard() to know about the new buffer size.
-
if (mForceRedraw) {
mForceRedraw = false;
return true;
}
- return mPanZoomController.getRedrawHint();
- }
-
- private RectF getTileRect() {
- if (mRootLayer == null)
- return new RectF();
+ if (!mPanZoomController.getRedrawHint()) {
+ return false;
+ }
- float x = mRootLayer.getOrigin().x, y = mRootLayer.getOrigin().y;
- IntSize layerSize = mRootLayer.getSize();
- return new RectF(x, y, x + layerSize.width, y + layerSize.height);
+ return aboutToCheckerboard();
}
// Returns true if a checkerboard is about to be visible.
... etc. - the rest is truncated
More information about the Libreoffice-commits
mailing list